    001    /* Copyright 2000, 2001, Compaq Computer Corporation */
    002
    003    package javafe.tc;
    004
    005
    006    import javafe.ast.*;
    007    import javafe.util.*;
    008
    009
    010    public class ConstantExpr {
    011
    012      // ----------------------------------------------------------------------
    013
    014      //@ requires t != null;
    015      public static boolean constantValueFitsIn( Object val, PrimitiveType t ) {
    016        if( val instanceof Integer || val instanceof Long ) {
    017          long l = getLongConstant( val );
    018          switch( t.getTag() ) {
    019          case TagConstants.INTTYPE:
    020            return Integer.MIN_VALUE <= l && l <= Integer.MAX_VALUE;
    021          case TagConstants.SHORTTYPE:
    022            return -0x8000 <= l && l <= 0x7fff;
    023          case TagConstants.BYTETYPE:
    024            return -0x80 <= l && l <= 0x7f;
    025          case TagConstants.CHARTYPE:
    026            return 0x0 <= l && l <= 0xffff;
    027          default:
    028            return false;
    029          }
    030        }
    031        else
    032          return false;
    033      }
    034
    035
    036      /** Evaluates a compile-time constant expression.  Returns Integer,
    037       * Long, Float, Double, Boolean, String or null (if the expression
    038       * is not a constant.)
    039
    040       * The relation between the FlowInsensitiveChecks.getType(e),
    041       * and the type of eval(e) is as follows:
    042
    043       * <PRE>
    044       * getType(e)          eval(e)
    045       *
    046       * boolean             Boolean or null
    047       * byte                Integer or null    (*)
    048       * short               Integer or null    (*)
    049       * char                Integer or null    (*)
    050       * int                 Integer or null
    051       * long                Long or null
    052       * float               Float or null
    053       * double              Double or null
    054       * String              String or null
    055       * </PRE>
    056       *
    057       * These will have been widened to int appropriately...
    058       */
    059
    060      //@ requires e != null;
    061      public static Object eval(Expr e) {
    062
    063        Type t = FlowInsensitiveChecks.getType( e );
    064
    065        //System.out.println("eval at "+Location.toString( e.getStartLoc() )+" type "+Types.printName(t) );
    066
    067        try {
    068
    069          switch( e.getTag() ) {
    070
    071          case TagConstants.PARENEXPR:
    072            return eval( ((ParenExpr)e).expr );
    073
    074          case TagConstants.CONDEXPR:
    075            {
    076              CondExpr ce = (CondExpr)e;
    077              Object val = eval(ce.test);
    078              if( val!= null && val instanceof Boolean ) {
    079                if( ((Boolean)val).booleanValue() )
    080                  return eval(ce.thn);
    081                else
    082                  return eval(ce.els);
    083              } else
    084                return null;
    085            }
    086
    087          case TagConstants.CASTEXPR:
    088            {
    089              CastExpr ce = (CastExpr)e;
    090              Object val = eval(ce.expr);
    091              if( val == null ) return null;
    092
    093              Type tsub = FlowInsensitiveChecks.getType(ce.expr);
    094              if( Types.isSameType( t, tsub ) ) {
    095                // Identity conversion
    096                return val;
    097              }
    098              else if( Types.isSameType( t, Types.javaLangString() ) ) {
    099                // convert to a string
    100                return val.toString();
    101              }
    102              else if( Types.isIntegralType( t ) ) {
    103                if( Types.isSameType( t, Types.longType ) ) {
    104                  if( Types.isFloatingPointType( tsub ) )
    105                    return new Long( (long)getDoubleConstant( val ) );
    106                  else if( Types.isIntegralType( tsub ) )
    107                    return new Long( getLongConstant( val ) );
    108                  else return null;
    109                } else {
    110                    // Type uses Integer representation:
    111
    112                    // First, get ce.expr's val as a widened int:
    113                    int ival;
    114                    if (Types.isFloatingPointType(tsub))
    115                        ival = (int)getDoubleConstant(val);
    116                    else if (Types.isIntegralType(tsub))
    117                        ival = (int)getLongConstant(val);
    118                    else return null;
    119
    120                    // Do narrowing conversion if needed:
    121                    if (Types.isByteType(t))
    122                        ival = (byte)ival;
    123                    else if (Types.isShortType(t))
    124                        ival = (short)ival;
    125                    else if (Types.isCharType(t))
    126                        ival = (char)ival;
    127
    128                    return new Integer(ival);
    129                }
    130              } else if( Types.isFloatingPointType( t ) ) {
    131                if( Types.isSameType( t, Types.doubleType ) ) {
    132                  if( Types.isFloatingPointType( tsub ) )
    133                    return new Double( getDoubleConstant( val ) );
    134                  else if( Types.isIntegralType( tsub ) )
    135                    return new Double( getLongConstant( val ) );
    136                  else return null;
    137                } else {
    138                  // Type is float
    139
    140                  if( Types.isFloatingPointType( tsub ) )
    141                      return new Float( (float)getDoubleConstant( val ) );
    142                    else if( Types.isIntegralType( tsub ) )
    143                      return new Float( getLongConstant( val ) );
    144                    else return null;
    145                  }
    146                } else {
    147                  Assert.fail("Bad cast");
    148                  return null; // dummy return
    149                }
    150              }
    151
    152          case TagConstants.FIELDACCESS:
    153            {
    154              FieldAccess fa = (FieldAccess)e;
    155              // Assume fa.od is prepped, and decl is defined
    156
    157              VarInit init = fa.decl.init;          //@ nowarn Null;
    158
    159              if(Modifiers.isFinal( fa.decl.modifiers )
    160                 && init != null
    161                 && init instanceof Expr ) {
    162
    163                TypeCheck.inst.makeFlowInsensitiveChecks()
    164                  .checkFieldDecl( fa.decl );
    165                return eval( (Expr)fa.decl.init );
    166              }
    167              else
    168                return null;
    169            }
    170
    171          case TagConstants.VARIABLEACCESS:
    172            {
    173              VariableAccess lva = (VariableAccess)e;
    174              if( lva.decl instanceof LocalVarDecl ) {
    175                LocalVarDecl d = (LocalVarDecl)lva.decl;
    176
    177                if(Modifiers.isFinal( d.modifiers )
    178                   && d.init != null
    179                   && d.init instanceof Expr )
    180                return eval( (Expr)d.init );
    181                else
    182                  return null;
    183              }
    184              else
    185                // refers to a formal parameter
    186                return null;
    187            }
    188
    189          default:
    190            if( e instanceof LiteralExpr ) {
    191              // System.out.println("eval: LiteralExpr: value="+((LiteralExpr)e).value);
    192              return ((LiteralExpr)e).value;
    193            }
    194            else if( e instanceof UnaryExpr ) {
    195
    196              UnaryExpr ue = (UnaryExpr)e;
    197              Object val = eval( ue.expr );
    198              // System.out.println("eval: unary: op="+e.getTag()+" sub value="+val);
    199              if( val==null ) return null;
    200
    201              if( Types.isSameType( t, Types.intType ) ) {
    202                int i = getIntConstant( val );
    203                switch( e.getTag() ) {
    204                case TagConstants.UNARYADD:
    205                  return val;
    206                case TagConstants.UNARYSUB:
    207                  return new Integer(-i);
    208                case TagConstants.BITNOT:
    209                  return new Integer(~i);
    210                default:
    211                  return null;
    212                }
    213              } else if( Types.isSameType( t, Types.longType ) ) {
    214                long l = getLongConstant( val );
    215                switch( e.getTag() ) {
    216                case TagConstants.UNARYADD:
    217                  return val;
    218                case TagConstants.UNARYSUB:
    219                  return new Long(-l);
    220                case TagConstants.BITNOT:
    221                  return new Long(~l);
    222                default:
    223                  return null;
    224                }
    225              } else if( Types.isSameType( t, Types.floatType ) ) {
    226                float f = getFloatConstant( val );
    227                switch( e.getTag() ) {
    228                case TagConstants.UNARYADD:
    229                  return val;
    230                case TagConstants.UNARYSUB:
    231                  return new Float(-f);
    232                default:
    233                  return null;
    234                }
    235              } else if( Types.isSameType( t, Types.doubleType ) ) {
    236                double d = getDoubleConstant( val );
    237                switch( e.getTag() ) {
    238                case TagConstants.UNARYADD:
    239                  return val;
    240                case TagConstants.UNARYSUB:
    241                  return new Double(-d);
    242                default:
    243                  return null;
    244                }
    245              } else if( Types.isBooleanType( t ) ) {
    246                switch( e.getTag() ) {
    247                case TagConstants.NOT:
    248                  return Boolean.valueOf( ! getBooleanConstant(val) );
    249                default:
    250                  return null;
    251                }
    252              } else
    253                return null;
    254            }
    255            else if( e instanceof BinaryExpr ) {
    256
    257              BinaryExpr be = (BinaryExpr) e;
    258              Object lval = eval( be.left );
    259              Object rval = eval( be.right );
    260
    261              if( lval==null || rval==null ) return null;
    262
    263              if( Types.isSameType( t, Types.intType ) )
    264                return evalIntBinaryOp( e.getTag(), lval, rval );
    265              else if( Types.isSameType( t, Types.longType ) )
    266                return evalLongBinaryOp( e.getTag(), lval, rval );
    267              else if( Types.isSameType( t, Types.floatType ) )
    268                return evalFloatBinaryOp( e.getTag(), lval, rval );
    269              else if( Types.isSameType( t, Types.doubleType ) )
    270                return evalDoubleBinaryOp( e.getTag(), lval, rval );
    271              else if( Types.isBooleanType( t ) )
    272                return evalBooleanBinaryOp( e.getTag(), lval, rval );
    273              else
    274                return null;
    275            }
    276
    277            return null;
    278          }
    279        } catch( ArithmeticException ex ) {
    280          ErrorSet.error("Arithmetic exception ("+ex.getMessage()
    281                         +") in evaluating constant expression");
    282          return null;
    283        } catch( AssertionFailureException ex ) {
    284          System.out.println("At "+Location.toString( e.getStartLoc() ));
    285          throw ex;
    286        }
    287      }
    288
    289      // ----------------------------------------------------------------------
    290
    291      private static Object evalIntBinaryOp(int op,
    292                                            Object leftVal,
    293                                            Object rightVal )
    294            throws ArithmeticException {
    295
    296        int x = getIntConstant(leftVal);
    297        int y = getIntConstant(rightVal);
    298
    299        switch( op ) {
    300        default:
    301          return null;
    302        case TagConstants.ADD:     return new Integer(x+y);
    303        case TagConstants.SUB:     return new Integer(x-y);
    304        case TagConstants.STAR:    return new Integer(x*y);
    305        case TagConstants.DIV:   return new Integer(x/y); //@nowarn ZeroDiv;//caught
    306        case TagConstants.MOD:   return new Integer(x%y); //@nowarn ZeroDiv;//caught
    307        case TagConstants.LSHIFT:  return new Integer(x<<y);
    308        case TagConstants.RSHIFT:  return new Integer(x>>y);
    309        case TagConstants.URSHIFT: return new Integer(x>>>y);
    310        case TagConstants.LT:      return Boolean.valueOf(x<y);
    311        case TagConstants.LE:      return Boolean.valueOf(x<=y);
    312        case TagConstants.GT:      return Boolean.valueOf(x>y);
    313        case TagConstants.GE:      return Boolean.valueOf(x>=y);
    314        case TagConstants.EQ:      return Boolean.valueOf(x==y);
    315        case TagConstants.NE:      return Boolean.valueOf(x != y);
    316        case TagConstants.BITAND:  return new Integer(x&y);
    317        case TagConstants.BITOR:   return new Integer(x|y);
    318        case TagConstants.BITXOR:  return new Integer(x^y);
    319        }
    320      }
    321
    322      private static Object evalLongBinaryOp(int op,
    323                                             Object leftVal,
    324                                             Object rightVal )
    325            throws ArithmeticException {
    326
    327        long x = getLongConstant(leftVal);
    328        long y = getLongConstant(rightVal);
    329
    330        switch( op ) {
    331        default:
    332          return null;
    333        case TagConstants.ADD:     return new Long(x+y);
    334        case TagConstants.SUB:     return new Long(x-y);
    335        case TagConstants.STAR:    return new Long(x*y);
    336        case TagConstants.DIV:     return new Long(x/y); //@ nowarn ZeroDiv;//caught
    337        case TagConstants.MOD:     return new Long(x%y); //@ nowarn ZeroDiv;//caught
    338        case TagConstants.LSHIFT:  return new Long(x<<y);
    339        case TagConstants.RSHIFT:  return new Long(x>>y);
    340        case TagConstants.URSHIFT: return new Long(x>>>y);
    341        case TagConstants.LT:      return Boolean.valueOf(x<y);
    342        case TagConstants.LE:      return Boolean.valueOf(x<=y);
    343        case TagConstants.GT:      return Boolean.valueOf(x>y);
    344        case TagConstants.GE:      return Boolean.valueOf(x>=y);
    345        case TagConstants.EQ:      return Boolean.valueOf(x==y);
    346        case TagConstants.NE:      return Boolean.valueOf(x != y);
    347        case TagConstants.BITAND:  return new Long(x&y);
    348        case TagConstants.BITOR:   return new Long(x|y);
    349        case TagConstants.BITXOR:  return new Long(x^y);
    350        }
    351      }
    352
    353      private static Object evalBooleanBinaryOp(int op,
    354                                                 Object leftVal,
    355                                                 Object rightVal )
    356            throws ArithmeticException {
    357
    358        if (leftVal instanceof Float || leftVal instanceof Double ||
    359            rightVal instanceof Float || rightVal instanceof Double) {
    360          return evalDoubleBinaryOp(op, leftVal, rightVal);
    361        } else if (! (leftVal instanceof Boolean)) {
    362          return evalLongBinaryOp(op, leftVal, rightVal);
    363        }
    364
    365        boolean x = getBooleanConstant(leftVal);
    366        boolean y = getBooleanConstant(rightVal);
    367
    368        switch( op ) {
    369        default:
    370          return null;
    371        case TagConstants.EQ:      return Boolean.valueOf(x==y);
    372        case TagConstants.NE:      return Boolean.valueOf(x != y);
    373        case TagConstants.BITAND:  return Boolean.valueOf(x&y);
    374        case TagConstants.BITOR:   return Boolean.valueOf(x|y);
    375        case TagConstants.BITXOR:  return Boolean.valueOf(x^y);
    376        case TagConstants.AND:     return Boolean.valueOf(x&&y);
    377        case TagConstants.OR:      return Boolean.valueOf(x||y);
    378        }
    379      }
    380
    381      private static  Object evalFloatBinaryOp(int op,
    382                                               Object leftVal,
    383                                               Object rightVal ) {
    384        float x = getFloatConstant(leftVal);
    385        float y = getFloatConstant(rightVal);
    386
    387        switch( op ) {
    388        default:
    389          return null;
    390        case TagConstants.ADD: return new Float(x+y);
    391        case TagConstants.SUB: return new Float(x-y);
    392        case TagConstants.STAR:return new Float(x*y);
    393        case TagConstants.DIV: return new Float(x/y);
    394        case TagConstants.MOD: return new Float(x%y);
    395        case TagConstants.EQ:  return Boolean.valueOf(x==y);
    396        case TagConstants.NE:  return Boolean.valueOf(x != y);
    397        case TagConstants.LT:  return Boolean.valueOf(x<y);
    398        case TagConstants.LE:  return Boolean.valueOf(x<=y);
    399        case TagConstants.GT:  return Boolean.valueOf(x>y);
    400        case TagConstants.GE:  return Boolean.valueOf(x>=y);
    401        }
    402      }
    403
    404      private static  Object evalDoubleBinaryOp(int op,
    405                                                Object leftVal,
    406                                                Object rightVal ) {
    407        double x = getDoubleConstant(leftVal);
    408        double y = getDoubleConstant(rightVal);
    409
    410        switch( op ) {
    411        default:
    412          return null;
    413        case TagConstants.ADD: return new Double(x+y);
    414        case TagConstants.SUB: return new Double(x-y);
    415        case TagConstants.STAR:return new Double(x*y);
    416        case TagConstants.DIV: return new Double(x/y);
    417        case TagConstants.MOD: return new Double(x%y);
    418        case TagConstants.EQ:  return Boolean.valueOf(x==y);
    419        case TagConstants.NE:  return Boolean.valueOf(x != y);
    420        case TagConstants.LT:  return Boolean.valueOf(x<y);
    421        case TagConstants.LE:  return Boolean.valueOf(x<=y);
    422        case TagConstants.GT:  return Boolean.valueOf(x>y);
    423        case TagConstants.GE:  return Boolean.valueOf(x>=y);
    424        }
    425      }
    426
    427      // ----------------------------------------------------------------------
    428
    429      public static int getIntConstant(Object c) {
    430        if( c instanceof Integer )
    431          return ((Integer)c).intValue();
    432        else {
    433          Assert.fail("Bad getIntConstant");
    434          return 0; // dummy return
    435        }
    436      }
    437
    438      public static long getLongConstant(Object c) {
    439        if( c instanceof Long )
    440          return ((Long)c).longValue();
    441        else if( c instanceof Integer )
    442          return ((Integer)c).intValue();
    443        else {
    444          Assert.fail("Bad getLongConstant: "+c);
    445          return 0; // dummy return
    446        }
    447      }
    448
    449      private static boolean getBooleanConstant(Object c) {
    450        if( c instanceof Boolean )
    451          return ((Boolean)c).booleanValue();
    452        else {
    453          Assert.fail("Bad getBooleanConstant");
    454          return false; // dummy return
    455        }
    456      }
    457
    458      private static float getFloatConstant(Object c) {
    459        if( c instanceof Integer )
    460          return ((Integer)c).intValue();
    461        else if( c instanceof Long )
    462          return ((Long)c).longValue();
    463        else if( c instanceof Float )
    464          return ((Float)c).floatValue();
    465        else {
    466          Assert.fail("Bad getFloatConstant");
    467          return 0; // dummy return
    468        }
    469      }
    470
    471      private static double getDoubleConstant(Object c) {
    472        if( c instanceof Integer )
    473          return ((Integer)c).intValue();
    474        else if( c instanceof Long )
    475          return ((Long)c).longValue();
    476        else if( c instanceof Float )
    477          return ((Float)c).floatValue();
    478        else if( c instanceof Double )
    479          return ((Double)c).doubleValue();
    480        else {
    481          Assert.fail("Bad getDoubleConstant");
    482          return 0; // dummy return
    483        }
    484      }
    485    }