/* * 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. * *

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 values; /** The class environment. */ private final CompoundEnv env; private final CompoundScope scope; public ConstEvaluator( ClassSymbol origin, ClassSymbol owner, SourceTypeBoundClass base, CompoundScope scope, Env values, CompoundEnv 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. * *

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 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 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 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 evaluateAnnotations(ImmutableList annotations) { ImmutableList.Builder result = ImmutableList.builder(); for (AnnoInfo annotation : annotations) { result.add(evaluateAnnotation(annotation)); } return result.build(); } /** * Evaluates annotation arguments given the symbol of the annotation declaration and a list of * expression trees. */ AnnoInfo evaluateAnnotation(AnnoInfo info) { Map template = new LinkedHashMap<>(); for (MethodInfo method : env.get(info.sym()).methods()) { template.put(method.name(), method.returnType()); } ImmutableMap.Builder values = ImmutableMap.builder(); for (Expression arg : info.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 info.withValues(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(new AnnoInfo(base.source(), sym, t, null)); return new AnnotationValue(annoInfo.sym(), annoInfo.values()); } private Const.ArrayInitValue evalArrayInit(ArrayInit t) { ImmutableList.Builder 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); if (value == null) { throw error(tree.position(), ErrorKind.EXPRESSION_ERROR); } 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 elements = value.kind() == Const.Kind.ARRAY ? ((Const.ArrayInitValue) value).elements() : ImmutableList.of(value); ImmutableList.Builder 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); } }