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    }