| /* |
| * Copyright 2016 Google Inc. All Rights Reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.google.turbine.binder; |
| |
| import static com.google.common.base.Preconditions.checkNotNull; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.Iterables; |
| import com.google.turbine.binder.bound.AnnotationValue; |
| import com.google.turbine.binder.bound.ClassValue; |
| import com.google.turbine.binder.bound.EnumConstantValue; |
| import com.google.turbine.binder.bound.SourceTypeBoundClass; |
| import com.google.turbine.binder.bound.TypeBoundClass; |
| import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo; |
| import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo; |
| import com.google.turbine.binder.env.CompoundEnv; |
| import com.google.turbine.binder.env.Env; |
| import com.google.turbine.binder.lookup.CompoundScope; |
| import com.google.turbine.binder.lookup.LookupKey; |
| import com.google.turbine.binder.lookup.LookupResult; |
| import com.google.turbine.binder.sym.ClassSymbol; |
| import com.google.turbine.binder.sym.FieldSymbol; |
| import com.google.turbine.diag.TurbineError; |
| import com.google.turbine.diag.TurbineError.ErrorKind; |
| import com.google.turbine.model.Const; |
| import com.google.turbine.model.Const.Value; |
| import com.google.turbine.model.TurbineConstantTypeKind; |
| import com.google.turbine.model.TurbineFlag; |
| import com.google.turbine.tree.Tree; |
| import com.google.turbine.tree.Tree.ArrayInit; |
| import com.google.turbine.tree.Tree.Binary; |
| import com.google.turbine.tree.Tree.ClassLiteral; |
| import com.google.turbine.tree.Tree.ClassTy; |
| import com.google.turbine.tree.Tree.Conditional; |
| import com.google.turbine.tree.Tree.ConstVarName; |
| import com.google.turbine.tree.Tree.Expression; |
| import com.google.turbine.tree.Tree.PrimTy; |
| import com.google.turbine.tree.Tree.TypeCast; |
| import com.google.turbine.tree.Tree.Unary; |
| import com.google.turbine.type.AnnoInfo; |
| import com.google.turbine.type.Type; |
| import java.util.ArrayDeque; |
| import java.util.Iterator; |
| import java.util.LinkedHashMap; |
| import java.util.Map; |
| |
| /** |
| * Constant expression evaluation. |
| * |
| * <p>JLS ยง15.4 requires this class to be strictfp. |
| */ |
| public strictfp class ConstEvaluator { |
| |
| /** The symbol of the originating class, for visibility checks. */ |
| private final ClassSymbol origin; |
| |
| /** The symbol of the enclosing class, for lexical field lookups. */ |
| private final ClassSymbol owner; |
| |
| /** The bound node of the enclosing class. */ |
| private final SourceTypeBoundClass base; |
| |
| /** The constant variable environment. */ |
| private final Env<FieldSymbol, Const.Value> values; |
| |
| /** The class environment. */ |
| private final CompoundEnv<ClassSymbol, TypeBoundClass> env; |
| |
| private final CompoundScope scope; |
| |
| public ConstEvaluator( |
| ClassSymbol origin, |
| ClassSymbol owner, |
| SourceTypeBoundClass base, |
| CompoundScope scope, |
| Env<FieldSymbol, Const.Value> values, |
| CompoundEnv<ClassSymbol, TypeBoundClass> env) { |
| |
| this.origin = origin; |
| this.owner = owner; |
| this.base = base; |
| this.values = values; |
| this.env = env; |
| this.scope = scope; |
| } |
| |
| /** Evaluates the given expression's value. */ |
| public Const eval(Tree t) { |
| switch (t.kind()) { |
| case LITERAL: |
| { |
| Const.Value a = (Const.Value) ((Tree.Literal) t).value(); |
| if (a == null) { |
| return null; |
| } |
| switch (a.constantTypeKind()) { |
| case CHAR: |
| return new Const.CharValue(((com.google.turbine.model.Const.CharValue) a).value()); |
| case INT: |
| return new Const.IntValue(((com.google.turbine.model.Const.IntValue) a).value()); |
| case LONG: |
| return new Const.LongValue(((com.google.turbine.model.Const.LongValue) a).value()); |
| case FLOAT: |
| return new Const.FloatValue(((com.google.turbine.model.Const.FloatValue) a).value()); |
| case DOUBLE: |
| return new Const.DoubleValue( |
| ((com.google.turbine.model.Const.DoubleValue) a).value()); |
| case BOOLEAN: |
| return new Const.BooleanValue( |
| ((com.google.turbine.model.Const.BooleanValue) a).value()); |
| case STRING: |
| return new Const.StringValue( |
| ((com.google.turbine.model.Const.StringValue) a).value()); |
| case SHORT: |
| case BYTE: |
| case NULL: |
| default: |
| throw new AssertionError(a.constantTypeKind()); |
| } |
| } |
| case VOID_TY: |
| throw new AssertionError(t.kind()); |
| case CONST_VAR_NAME: |
| return evalConstVar((ConstVarName) t); |
| case CLASS_LITERAL: |
| return evalClassLiteral((ClassLiteral) t); |
| case BINARY: |
| return evalBinary((Binary) t); |
| case TYPE_CAST: |
| return evalCast((TypeCast) t); |
| case UNARY: |
| return evalUnary((Unary) t); |
| case CONDITIONAL: |
| return evalConditional((Conditional) t); |
| case ARRAY_INIT: |
| return evalArrayInit((ArrayInit) t); |
| case ANNO_EXPR: |
| return evalAnno(((Tree.AnnoExpr) t).value()); |
| default: |
| throw new AssertionError(t.kind()); |
| } |
| } |
| |
| /** Evaluates a class literal. */ |
| Const evalClassLiteral(ClassLiteral t) { |
| return new ClassValue(evalClassLiteralType(t.type())); |
| } |
| |
| private Type evalClassLiteralType(Tree.Type type) { |
| switch (type.kind()) { |
| case PRIM_TY: |
| return new Type.PrimTy(((PrimTy) type).tykind(), ImmutableList.of()); |
| case VOID_TY: |
| return Type.VOID; |
| case CLASS_TY: |
| return Type.ClassTy.asNonParametricClassTy(resolveClass((ClassTy) type)); |
| case ARR_TY: |
| return new Type.ArrayTy( |
| evalClassLiteralType(((Tree.ArrTy) type).elem()), ImmutableList.of()); |
| default: |
| throw new AssertionError(type.kind()); |
| } |
| } |
| |
| /** |
| * Resolves the {@link ClassSymbol} for the given {@link Tree.ClassTy}, with handling for |
| * non-canonical qualified type names. |
| * |
| * <p>Similar to {@link HierarchyBinder#resolveClass}, except we can't unconditionally consider |
| * members of the current class (e.g. when binding constants inside annotations on that class), |
| * and when we do want to consider members we can rely on them being in the current scope (it |
| * isn't completed during the hierarchy phase). |
| */ |
| private ClassSymbol resolveClass(ClassTy classTy) { |
| ArrayDeque<String> flat = new ArrayDeque<>(); |
| for (ClassTy curr = classTy; curr != null; curr = curr.base().orNull()) { |
| flat.addFirst(curr.name()); |
| } |
| LookupResult result = scope.lookup(new LookupKey(flat)); |
| if (result == null) { |
| throw error(classTy.position(), ErrorKind.SYMBOL_NOT_FOUND, flat.peekFirst()); |
| } |
| ClassSymbol classSym = (ClassSymbol) result.sym(); |
| for (String bit : result.remaining()) { |
| classSym = Resolve.resolve(env, origin, classSym, bit); |
| if (classSym == null) { |
| throw error(classTy.position(), ErrorKind.SYMBOL_NOT_FOUND, bit); |
| } |
| } |
| return classSym; |
| } |
| |
| /** Evaluates a reference to another constant variable. */ |
| Const evalConstVar(ConstVarName t) { |
| FieldInfo field = resolveField(t); |
| if (field == null) { |
| return null; |
| } |
| if ((field.access() & TurbineFlag.ACC_ENUM) == TurbineFlag.ACC_ENUM) { |
| return new EnumConstantValue(field.sym()); |
| } |
| if (field.value() != null) { |
| return field.value(); |
| } |
| return values.get(field.sym()); |
| } |
| |
| FieldInfo resolveField(ConstVarName t) { |
| String simpleName = t.name().get(0); |
| FieldInfo field = lexicalField(env, owner, simpleName); |
| if (field != null) { |
| return field; |
| } |
| field = resolveQualifiedField(t); |
| if (field != null) { |
| return field; |
| } |
| ClassSymbol classSymbol = base.memberImports().singleMemberImport(simpleName); |
| if (classSymbol != null) { |
| field = Resolve.resolveField(env, origin, classSymbol, simpleName); |
| if (field != null) { |
| return field; |
| } |
| } |
| Iterator<ClassSymbol> it = base.memberImports().onDemandImports(); |
| while (it.hasNext()) { |
| field = Resolve.resolveField(env, origin, it.next(), simpleName); |
| if (field == null) { |
| continue; |
| } |
| // resolve handles visibility of inherited members; on-demand imports of private members are |
| // a special case |
| if ((field.access() & TurbineFlag.ACC_PRIVATE) == TurbineFlag.ACC_PRIVATE) { |
| continue; |
| } |
| return field; |
| } |
| return null; |
| } |
| |
| private FieldInfo resolveQualifiedField(ConstVarName t) { |
| if (t.name().size() <= 1) { |
| return null; |
| } |
| LookupResult result = scope.lookup(new LookupKey(t.name())); |
| if (result == null) { |
| return null; |
| } |
| ClassSymbol sym = (ClassSymbol) result.sym(); |
| for (int i = 0; i < result.remaining().size() - 1; i++) { |
| sym = Resolve.resolve(env, sym, sym, result.remaining().get(i)); |
| if (sym == null) { |
| return null; |
| } |
| } |
| return Resolve.resolveField(env, origin, sym, Iterables.getLast(result.remaining())); |
| } |
| |
| /** Search for constant variables in lexically enclosing scopes. */ |
| private FieldInfo lexicalField( |
| Env<ClassSymbol, TypeBoundClass> env, ClassSymbol sym, String name) { |
| while (sym != null) { |
| TypeBoundClass info = env.get(sym); |
| FieldInfo field = Resolve.resolveField(env, origin, sym, name); |
| if (field != null) { |
| return field; |
| } |
| sym = info.owner(); |
| } |
| return null; |
| } |
| |
| /** Casts the value to the given type. */ |
| static Const cast(Type ty, Const value) { |
| checkNotNull(value); |
| switch (ty.tyKind()) { |
| case CLASS_TY: |
| case TY_VAR: |
| return value; |
| case PRIM_TY: |
| return coerce((Const.Value) value, ((Type.PrimTy) ty).primkind()); |
| default: |
| throw new AssertionError(ty.tyKind()); |
| } |
| } |
| |
| private static Const.Value coerce(Const.Value value, TurbineConstantTypeKind kind) { |
| switch (kind) { |
| case BOOLEAN: |
| return value.asBoolean(); |
| case STRING: |
| return value.asString(); |
| case LONG: |
| return value.asLong(); |
| case INT: |
| return value.asInteger(); |
| case BYTE: |
| return value.asByte(); |
| case CHAR: |
| return value.asChar(); |
| case SHORT: |
| return value.asShort(); |
| case DOUBLE: |
| return value.asDouble(); |
| case FLOAT: |
| return value.asFloat(); |
| default: |
| throw new AssertionError(kind); |
| } |
| } |
| |
| private Const.Value evalValue(Expression tree) { |
| Const result = eval(tree); |
| // TODO(cushon): consider distinguishing between constant field and annotation values, |
| // and only allowing class literals / enum constants in the latter |
| return (result instanceof Const.Value) ? (Const.Value) result : null; |
| } |
| |
| private Const.Value evalConditional(Conditional t) { |
| Const.Value condition = evalValue(t.cond()); |
| if (condition == null) { |
| return null; |
| } |
| return condition.asBoolean().value() ? evalValue(t.iftrue()) : evalValue(t.iffalse()); |
| } |
| |
| private Const.Value evalUnary(Unary t) { |
| Const.Value expr = evalValue(t.expr()); |
| if (expr == null) { |
| return null; |
| } |
| switch (t.op()) { |
| case NOT: |
| return unaryNegate(expr); |
| case BITWISE_COMP: |
| return bitwiseComp(expr); |
| case UNARY_PLUS: |
| return unaryPlus(expr); |
| case NEG: |
| return unaryMinus(expr); |
| default: |
| throw new AssertionError(t.op()); |
| } |
| } |
| |
| private Value unaryNegate(Value expr) { |
| switch (expr.constantTypeKind()) { |
| case BOOLEAN: |
| return new Const.BooleanValue(!expr.asBoolean().value()); |
| default: |
| throw new AssertionError(expr.constantTypeKind()); |
| } |
| } |
| |
| private Value bitwiseComp(Value expr) { |
| expr = promoteUnary(expr); |
| switch (expr.constantTypeKind()) { |
| case INT: |
| return new Const.IntValue(~expr.asInteger().value()); |
| case LONG: |
| return new Const.LongValue(~expr.asLong().value()); |
| default: |
| throw new AssertionError(expr.constantTypeKind()); |
| } |
| } |
| |
| private Value unaryPlus(Value expr) { |
| expr = promoteUnary(expr); |
| switch (expr.constantTypeKind()) { |
| case INT: |
| return new Const.IntValue(+expr.asInteger().value()); |
| case LONG: |
| return new Const.LongValue(+expr.asLong().value()); |
| case FLOAT: |
| return new Const.FloatValue(+expr.asFloat().value()); |
| case DOUBLE: |
| return new Const.DoubleValue(+expr.asDouble().value()); |
| default: |
| throw new AssertionError(expr.constantTypeKind()); |
| } |
| } |
| |
| private Value unaryMinus(Value expr) { |
| expr = promoteUnary(expr); |
| switch (expr.constantTypeKind()) { |
| case INT: |
| return new Const.IntValue(-expr.asInteger().value()); |
| case LONG: |
| return new Const.LongValue(-expr.asLong().value()); |
| case FLOAT: |
| return new Const.FloatValue(-expr.asFloat().value()); |
| case DOUBLE: |
| return new Const.DoubleValue(-expr.asDouble().value()); |
| default: |
| throw new AssertionError(expr.constantTypeKind()); |
| } |
| } |
| |
| private Const.Value evalCast(TypeCast t) { |
| Const.Value expr = evalValue(t.expr()); |
| if (expr == null) { |
| return null; |
| } |
| switch (t.ty().kind()) { |
| case PRIM_TY: |
| return coerce(expr, ((Tree.PrimTy) t.ty()).tykind()); |
| case CLASS_TY: |
| { |
| ClassTy classTy = (ClassTy) t.ty(); |
| // TODO(cushon): check package? |
| if (!classTy.name().equals("String")) { |
| throw new AssertionError(classTy); |
| } |
| return expr.asString(); |
| } |
| default: |
| throw new AssertionError(t.ty().kind()); |
| } |
| } |
| |
| static Const.Value add(Const.Value a, Const.Value b) { |
| if (a.constantTypeKind() == TurbineConstantTypeKind.STRING |
| || b.constantTypeKind() == TurbineConstantTypeKind.STRING) { |
| return new Const.StringValue(a.asString().value() + b.asString().value()); |
| } |
| TurbineConstantTypeKind type = promoteBinary(a, b); |
| a = coerce(a, type); |
| b = coerce(b, type); |
| switch (type) { |
| case INT: |
| return new Const.IntValue(a.asInteger().value() + b.asInteger().value()); |
| case LONG: |
| return new Const.LongValue(a.asLong().value() + b.asLong().value()); |
| case FLOAT: |
| return new Const.FloatValue(a.asFloat().value() + b.asFloat().value()); |
| case DOUBLE: |
| return new Const.DoubleValue(a.asDouble().value() + b.asDouble().value()); |
| default: |
| throw new AssertionError(type); |
| } |
| } |
| |
| static Const.Value subtract(Const.Value a, Const.Value b) { |
| TurbineConstantTypeKind type = promoteBinary(a, b); |
| a = coerce(a, type); |
| b = coerce(b, type); |
| switch (type) { |
| case INT: |
| return new Const.IntValue(a.asInteger().value() - b.asInteger().value()); |
| case LONG: |
| return new Const.LongValue(a.asLong().value() - b.asLong().value()); |
| case FLOAT: |
| return new Const.FloatValue(a.asFloat().value() - b.asFloat().value()); |
| case DOUBLE: |
| return new Const.DoubleValue(a.asDouble().value() - b.asDouble().value()); |
| default: |
| throw new AssertionError(type); |
| } |
| } |
| |
| static Const.Value mult(Const.Value a, Const.Value b) { |
| TurbineConstantTypeKind type = promoteBinary(a, b); |
| a = coerce(a, type); |
| b = coerce(b, type); |
| switch (type) { |
| case INT: |
| return new Const.IntValue(a.asInteger().value() * b.asInteger().value()); |
| case LONG: |
| return new Const.LongValue(a.asLong().value() * b.asLong().value()); |
| case FLOAT: |
| return new Const.FloatValue(a.asFloat().value() * b.asFloat().value()); |
| case DOUBLE: |
| return new Const.DoubleValue(a.asDouble().value() * b.asDouble().value()); |
| default: |
| throw new AssertionError(type); |
| } |
| } |
| |
| static Const.Value divide(Const.Value a, Const.Value b) { |
| TurbineConstantTypeKind type = promoteBinary(a, b); |
| a = coerce(a, type); |
| b = coerce(b, type); |
| switch (type) { |
| case INT: |
| return new Const.IntValue(a.asInteger().value() / b.asInteger().value()); |
| case LONG: |
| return new Const.LongValue(a.asLong().value() / b.asLong().value()); |
| case FLOAT: |
| return new Const.FloatValue(a.asFloat().value() / b.asFloat().value()); |
| case DOUBLE: |
| return new Const.DoubleValue(a.asDouble().value() / b.asDouble().value()); |
| default: |
| throw new AssertionError(type); |
| } |
| } |
| |
| static Const.Value mod(Const.Value a, Const.Value b) { |
| TurbineConstantTypeKind type = promoteBinary(a, b); |
| a = coerce(a, type); |
| b = coerce(b, type); |
| switch (type) { |
| case INT: |
| return new Const.IntValue(a.asInteger().value() % b.asInteger().value()); |
| case LONG: |
| return new Const.LongValue(a.asLong().value() % b.asLong().value()); |
| case FLOAT: |
| return new Const.FloatValue(a.asFloat().value() % b.asFloat().value()); |
| case DOUBLE: |
| return new Const.DoubleValue(a.asDouble().value() % b.asDouble().value()); |
| default: |
| throw new AssertionError(type); |
| } |
| } |
| |
| static final int INT_SHIFT_MASK = 0b11111; |
| |
| static final int LONG_SHIFT_MASK = 0b111111; |
| |
| static Const.Value shiftLeft(Const.Value a, Const.Value b) { |
| a = promoteUnary(a); |
| b = promoteUnary(b); |
| switch (a.constantTypeKind()) { |
| case INT: |
| return new Const.IntValue( |
| a.asInteger().value() << (b.asInteger().value() & INT_SHIFT_MASK)); |
| case LONG: |
| return new Const.LongValue(a.asLong().value() << (b.asInteger().value() & LONG_SHIFT_MASK)); |
| default: |
| throw new AssertionError(a.constantTypeKind()); |
| } |
| } |
| |
| static Const.Value shiftRight(Const.Value a, Const.Value b) { |
| a = promoteUnary(a); |
| b = promoteUnary(b); |
| switch (a.constantTypeKind()) { |
| case INT: |
| return new Const.IntValue( |
| a.asInteger().value() >> (b.asInteger().value() & INT_SHIFT_MASK)); |
| case LONG: |
| return new Const.LongValue(a.asLong().value() >> (b.asInteger().value() & LONG_SHIFT_MASK)); |
| default: |
| throw new AssertionError(a.constantTypeKind()); |
| } |
| } |
| |
| static Const.Value unsignedShiftRight(Const.Value a, Const.Value b) { |
| a = promoteUnary(a); |
| b = promoteUnary(b); |
| switch (a.constantTypeKind()) { |
| case INT: |
| return new Const.IntValue( |
| a.asInteger().value() >>> (b.asInteger().value() & INT_SHIFT_MASK)); |
| case LONG: |
| return new Const.LongValue( |
| a.asLong().value() >>> (b.asInteger().value() & LONG_SHIFT_MASK)); |
| default: |
| throw new AssertionError(a.constantTypeKind()); |
| } |
| } |
| |
| static Const.Value lessThan(Const.Value a, Const.Value b) { |
| TurbineConstantTypeKind type = promoteBinary(a, b); |
| a = coerce(a, type); |
| b = coerce(b, type); |
| switch (type) { |
| case INT: |
| return new Const.BooleanValue(a.asInteger().value() < b.asInteger().value()); |
| case LONG: |
| return new Const.BooleanValue(a.asLong().value() < b.asLong().value()); |
| case FLOAT: |
| return new Const.BooleanValue(a.asFloat().value() < b.asFloat().value()); |
| case DOUBLE: |
| return new Const.BooleanValue(a.asDouble().value() < b.asDouble().value()); |
| default: |
| throw new AssertionError(type); |
| } |
| } |
| |
| static Const.Value lessThanEqual(Const.Value a, Const.Value b) { |
| TurbineConstantTypeKind type = promoteBinary(a, b); |
| a = coerce(a, type); |
| b = coerce(b, type); |
| switch (type) { |
| case INT: |
| return new Const.BooleanValue(a.asInteger().value() <= b.asInteger().value()); |
| case LONG: |
| return new Const.BooleanValue(a.asLong().value() <= b.asLong().value()); |
| case FLOAT: |
| return new Const.BooleanValue(a.asFloat().value() <= b.asFloat().value()); |
| case DOUBLE: |
| return new Const.BooleanValue(a.asDouble().value() <= b.asDouble().value()); |
| default: |
| throw new AssertionError(type); |
| } |
| } |
| |
| static Const.Value greaterThan(Const.Value a, Const.Value b) { |
| TurbineConstantTypeKind type = promoteBinary(a, b); |
| a = coerce(a, type); |
| b = coerce(b, type); |
| switch (type) { |
| case INT: |
| return new Const.BooleanValue(a.asInteger().value() > b.asInteger().value()); |
| case LONG: |
| return new Const.BooleanValue(a.asLong().value() > b.asLong().value()); |
| case FLOAT: |
| return new Const.BooleanValue(a.asFloat().value() > b.asFloat().value()); |
| case DOUBLE: |
| return new Const.BooleanValue(a.asDouble().value() > b.asDouble().value()); |
| default: |
| throw new AssertionError(type); |
| } |
| } |
| |
| static Const.Value greaterThanEqual(Const.Value a, Const.Value b) { |
| TurbineConstantTypeKind type = promoteBinary(a, b); |
| a = coerce(a, type); |
| b = coerce(b, type); |
| switch (type) { |
| case INT: |
| return new Const.BooleanValue(a.asInteger().value() >= b.asInteger().value()); |
| case LONG: |
| return new Const.BooleanValue(a.asLong().value() >= b.asLong().value()); |
| case FLOAT: |
| return new Const.BooleanValue(a.asFloat().value() >= b.asFloat().value()); |
| case DOUBLE: |
| return new Const.BooleanValue(a.asDouble().value() >= b.asDouble().value()); |
| default: |
| throw new AssertionError(type); |
| } |
| } |
| |
| static Const.Value equal(Const.Value a, Const.Value b) { |
| switch (a.constantTypeKind()) { |
| case STRING: |
| return new Const.BooleanValue(a.asString().value().equals(b.asString().value())); |
| case BOOLEAN: |
| return new Const.BooleanValue(a.asBoolean().value() == b.asBoolean().value()); |
| default: |
| break; |
| } |
| TurbineConstantTypeKind type = promoteBinary(a, b); |
| a = coerce(a, type); |
| b = coerce(b, type); |
| switch (type) { |
| case INT: |
| return new Const.BooleanValue(a.asInteger().value() == b.asInteger().value()); |
| case LONG: |
| return new Const.BooleanValue(a.asLong().value() == b.asLong().value()); |
| case FLOAT: |
| return new Const.BooleanValue(a.asFloat().value() == b.asFloat().value()); |
| case DOUBLE: |
| return new Const.BooleanValue(a.asDouble().value() == b.asDouble().value()); |
| default: |
| throw new AssertionError(type); |
| } |
| } |
| |
| static Const.Value notEqual(Const.Value a, Const.Value b) { |
| switch (a.constantTypeKind()) { |
| case STRING: |
| return new Const.BooleanValue(!a.asString().value().equals(b.asString().value())); |
| case BOOLEAN: |
| return new Const.BooleanValue(a.asBoolean().value() != b.asBoolean().value()); |
| default: |
| break; |
| } |
| TurbineConstantTypeKind type = promoteBinary(a, b); |
| a = coerce(a, type); |
| b = coerce(b, type); |
| switch (type) { |
| case INT: |
| return new Const.BooleanValue(a.asInteger().value() != b.asInteger().value()); |
| case LONG: |
| return new Const.BooleanValue(a.asLong().value() != b.asLong().value()); |
| case FLOAT: |
| return new Const.BooleanValue(a.asFloat().value() != b.asFloat().value()); |
| case DOUBLE: |
| return new Const.BooleanValue(a.asDouble().value() != b.asDouble().value()); |
| default: |
| throw new AssertionError(type); |
| } |
| } |
| |
| static Const.Value bitwiseAnd(Const.Value a, Const.Value b) { |
| switch (a.constantTypeKind()) { |
| case BOOLEAN: |
| return new Const.BooleanValue(a.asBoolean().value() & b.asBoolean().value()); |
| default: |
| break; |
| } |
| TurbineConstantTypeKind type = promoteBinary(a, b); |
| a = coerce(a, type); |
| b = coerce(b, type); |
| switch (type) { |
| case INT: |
| return new Const.IntValue(a.asInteger().value() & b.asInteger().value()); |
| case LONG: |
| return new Const.LongValue(a.asLong().value() & b.asLong().value()); |
| default: |
| throw new AssertionError(type); |
| } |
| } |
| |
| static Const.Value bitwiseOr(Const.Value a, Const.Value b) { |
| switch (a.constantTypeKind()) { |
| case BOOLEAN: |
| return new Const.BooleanValue(a.asBoolean().value() | b.asBoolean().value()); |
| default: |
| break; |
| } |
| TurbineConstantTypeKind type = promoteBinary(a, b); |
| a = coerce(a, type); |
| b = coerce(b, type); |
| switch (type) { |
| case INT: |
| return new Const.IntValue(a.asInteger().value() | b.asInteger().value()); |
| case LONG: |
| return new Const.LongValue(a.asLong().value() | b.asLong().value()); |
| default: |
| throw new AssertionError(type); |
| } |
| } |
| |
| static Const.Value bitwiseXor(Const.Value a, Const.Value b) { |
| switch (a.constantTypeKind()) { |
| case BOOLEAN: |
| return new Const.BooleanValue(a.asBoolean().value() ^ b.asBoolean().value()); |
| default: |
| break; |
| } |
| TurbineConstantTypeKind type = promoteBinary(a, b); |
| a = coerce(a, type); |
| b = coerce(b, type); |
| switch (type) { |
| case INT: |
| return new Const.IntValue(a.asInteger().value() ^ b.asInteger().value()); |
| case LONG: |
| return new Const.LongValue(a.asLong().value() ^ b.asLong().value()); |
| default: |
| throw new AssertionError(type); |
| } |
| } |
| |
| private Const.Value evalBinary(Binary t) { |
| Const.Value lhs = evalValue(t.lhs()); |
| Const.Value rhs = evalValue(t.rhs()); |
| if (lhs == null || rhs == null) { |
| return null; |
| } |
| switch (t.op()) { |
| case PLUS: |
| return add(lhs, rhs); |
| case MINUS: |
| return subtract(lhs, rhs); |
| case MULT: |
| return mult(lhs, rhs); |
| case DIVIDE: |
| return divide(lhs, rhs); |
| case MODULO: |
| return mod(lhs, rhs); |
| case SHIFT_LEFT: |
| return shiftLeft(lhs, rhs); |
| case SHIFT_RIGHT: |
| return shiftRight(lhs, rhs); |
| case UNSIGNED_SHIFT_RIGHT: |
| return unsignedShiftRight(lhs, rhs); |
| case LESS_THAN: |
| return lessThan(lhs, rhs); |
| case GREATER_THAN: |
| return greaterThan(lhs, rhs); |
| case LESS_THAN_EQ: |
| return lessThanEqual(lhs, rhs); |
| case GREATER_THAN_EQ: |
| return greaterThanEqual(lhs, rhs); |
| case EQUAL: |
| return equal(lhs, rhs); |
| case NOT_EQUAL: |
| return notEqual(lhs, rhs); |
| case AND: |
| return new Const.BooleanValue(lhs.asBoolean().value() && rhs.asBoolean().value()); |
| case OR: |
| return new Const.BooleanValue(lhs.asBoolean().value() || rhs.asBoolean().value()); |
| case BITWISE_AND: |
| return bitwiseAnd(lhs, rhs); |
| case BITWISE_XOR: |
| return bitwiseXor(lhs, rhs); |
| case BITWISE_OR: |
| return bitwiseOr(lhs, rhs); |
| default: |
| throw new AssertionError(t.op()); |
| } |
| } |
| |
| private static Const.Value promoteUnary(Const.Value v) { |
| switch (v.constantTypeKind()) { |
| case CHAR: |
| case SHORT: |
| case BYTE: |
| return v.asInteger(); |
| case INT: |
| case LONG: |
| case FLOAT: |
| case DOUBLE: |
| return v; |
| default: |
| throw new AssertionError(v.constantTypeKind()); |
| } |
| } |
| |
| private static TurbineConstantTypeKind promoteBinary(Const.Value a, Const.Value b) { |
| a = promoteUnary(a); |
| b = promoteUnary(b); |
| switch (a.constantTypeKind()) { |
| case INT: |
| switch (b.constantTypeKind()) { |
| case INT: |
| case LONG: |
| case DOUBLE: |
| case FLOAT: |
| return b.constantTypeKind(); |
| default: |
| throw new AssertionError(b.constantTypeKind()); |
| } |
| case LONG: |
| switch (b.constantTypeKind()) { |
| case INT: |
| return TurbineConstantTypeKind.LONG; |
| case LONG: |
| case DOUBLE: |
| case FLOAT: |
| return b.constantTypeKind(); |
| default: |
| throw new AssertionError(b.constantTypeKind()); |
| } |
| case FLOAT: |
| switch (b.constantTypeKind()) { |
| case INT: |
| case LONG: |
| case FLOAT: |
| return TurbineConstantTypeKind.FLOAT; |
| case DOUBLE: |
| return TurbineConstantTypeKind.DOUBLE; |
| default: |
| throw new AssertionError(b.constantTypeKind()); |
| } |
| case DOUBLE: |
| switch (b.constantTypeKind()) { |
| case INT: |
| case LONG: |
| case FLOAT: |
| case DOUBLE: |
| return TurbineConstantTypeKind.DOUBLE; |
| default: |
| throw new AssertionError(b.constantTypeKind()); |
| } |
| default: |
| throw new AssertionError(a.constantTypeKind()); |
| } |
| } |
| |
| ImmutableList<AnnoInfo> evaluateAnnotations(ImmutableList<AnnoInfo> annotations) { |
| ImmutableList.Builder<AnnoInfo> result = ImmutableList.builder(); |
| for (AnnoInfo annotation : annotations) { |
| result.add(evaluateAnnotation(annotation.sym(), annotation.args())); |
| } |
| return result.build(); |
| } |
| |
| /** |
| * Evaluates annotation arguments given the symbol of the annotation declaration and a list of |
| * expression trees. |
| */ |
| AnnoInfo evaluateAnnotation(ClassSymbol sym, ImmutableList<Expression> args) { |
| Map<String, Type> template = new LinkedHashMap<>(); |
| for (MethodInfo method : env.get(sym).methods()) { |
| template.put(method.name(), method.returnType()); |
| } |
| |
| ImmutableMap.Builder<String, Const> values = ImmutableMap.builder(); |
| for (Expression arg : args) { |
| Expression expr; |
| String key; |
| if (arg.kind() == Tree.Kind.ASSIGN) { |
| Tree.Assign assign = (Tree.Assign) arg; |
| key = assign.name(); |
| expr = assign.expr(); |
| } else { |
| // expand the implicit 'value' name; `@Foo(42)` is sugar for `@Foo(value=42)` |
| key = "value"; |
| expr = arg; |
| } |
| Type ty = template.get(key); |
| if (ty == null) { |
| throw error(arg.position(), ErrorKind.CANNOT_RESOLVE, key); |
| } |
| Const value = evalAnnotationValue(expr, ty); |
| if (value == null) { |
| throw error(expr.position(), ErrorKind.EXPRESSION_ERROR); |
| } |
| values.put(key, value); |
| } |
| return new AnnoInfo(sym, args, values.build()); |
| } |
| |
| private AnnotationValue evalAnno(Tree.Anno t) { |
| LookupResult result = scope.lookup(new LookupKey(t.name())); |
| ClassSymbol sym = (ClassSymbol) result.sym(); |
| for (String name : result.remaining()) { |
| sym = Resolve.resolve(env, sym, sym, name); |
| } |
| AnnoInfo annoInfo = evaluateAnnotation(sym, t.args()); |
| return new AnnotationValue(annoInfo.sym(), annoInfo.values()); |
| } |
| |
| private Const.ArrayInitValue evalArrayInit(ArrayInit t) { |
| ImmutableList.Builder<Const> elements = ImmutableList.builder(); |
| for (Expression e : t.exprs()) { |
| Const arg = eval(e); |
| if (arg == null) { |
| return null; |
| } |
| elements.add(arg); |
| } |
| return new Const.ArrayInitValue(elements.build()); |
| } |
| |
| Const evalAnnotationValue(Tree tree, Type ty) { |
| if (ty == null) { |
| throw error(tree.position(), ErrorKind.EXPRESSION_ERROR); |
| } |
| Const value = eval(tree); |
| switch (ty.tyKind()) { |
| case PRIM_TY: |
| return coerce((Const.Value) value, ((Type.PrimTy) ty).primkind()); |
| case CLASS_TY: |
| case TY_VAR: |
| return value; |
| case ARRAY_TY: |
| { |
| Type elementType = ((Type.ArrayTy) ty).elementType(); |
| ImmutableList<Const> elements = |
| value.kind() == Const.Kind.ARRAY |
| ? ((Const.ArrayInitValue) value).elements() |
| : ImmutableList.of(value); |
| ImmutableList.Builder<Const> coerced = ImmutableList.builder(); |
| for (Const element : elements) { |
| coerced.add(cast(elementType, element)); |
| } |
| return new Const.ArrayInitValue(coerced.build()); |
| } |
| default: |
| throw new AssertionError(ty.tyKind()); |
| } |
| } |
| |
| private TurbineError error(int position, ErrorKind kind, Object... args) { |
| return TurbineError.format(base.source(), position, kind, args); |
| } |
| |
| public Const.Value evalFieldInitializer(Expression expression, Type type) { |
| Const value = eval(expression); |
| if (value == null || value.kind() != Const.Kind.PRIMITIVE) { |
| return null; |
| } |
| return (Const.Value) cast(type, value); |
| } |
| } |