1 /* 2 * Copyright 2016 Google Inc. All Rights Reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.turbine.binder.bytecode; 18 19 import com.google.common.collect.ImmutableList; 20 import com.google.common.collect.ImmutableMap; 21 import com.google.turbine.binder.bound.AnnotationValue; 22 import com.google.turbine.binder.bound.EnumConstantValue; 23 import com.google.turbine.binder.bound.ModuleInfo; 24 import com.google.turbine.binder.bound.TurbineClassValue; 25 import com.google.turbine.binder.sym.ClassSymbol; 26 import com.google.turbine.binder.sym.FieldSymbol; 27 import com.google.turbine.binder.sym.TyVarSymbol; 28 import com.google.turbine.bytecode.ClassFile; 29 import com.google.turbine.bytecode.ClassFile.AnnotationInfo; 30 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue; 31 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ArrayValue; 32 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstTurbineClassValue; 33 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstValue; 34 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.EnumConstValue; 35 import com.google.turbine.bytecode.ClassReader; 36 import com.google.turbine.bytecode.sig.Sig; 37 import com.google.turbine.bytecode.sig.Sig.LowerBoundTySig; 38 import com.google.turbine.bytecode.sig.Sig.UpperBoundTySig; 39 import com.google.turbine.bytecode.sig.Sig.WildTySig; 40 import com.google.turbine.bytecode.sig.SigParser; 41 import com.google.turbine.model.Const; 42 import com.google.turbine.model.Const.ArrayInitValue; 43 import com.google.turbine.type.AnnoInfo; 44 import com.google.turbine.type.Type; 45 import java.util.ArrayList; 46 import java.util.List; 47 import java.util.Map; 48 import java.util.function.Function; 49 import java.util.function.Supplier; 50 51 /** Bind {@link Type}s from bytecode. */ 52 public class BytecodeBinder { 53 bindClassTy(Sig.ClassTySig sig, Function<String, TyVarSymbol> scope)54 static Type.ClassTy bindClassTy(Sig.ClassTySig sig, Function<String, TyVarSymbol> scope) { 55 StringBuilder sb = new StringBuilder(sig.pkg()); 56 boolean first = true; 57 List<Type.ClassTy.SimpleClassTy> classes = new ArrayList<>(); 58 for (Sig.SimpleClassTySig s : sig.classes()) { 59 sb.append(first ? '/' : '$'); 60 sb.append(s.simpleName()); 61 ClassSymbol sym = new ClassSymbol(sb.toString()); 62 63 ImmutableList.Builder<Type> tyArgs = ImmutableList.builder(); 64 for (Sig.TySig arg : s.tyArgs()) { 65 tyArgs.add(bindTy(arg, scope)); 66 } 67 68 classes.add(Type.ClassTy.SimpleClassTy.create(sym, tyArgs.build(), ImmutableList.of())); 69 first = false; 70 } 71 return Type.ClassTy.create(classes); 72 } 73 wildTy(WildTySig sig, Function<String, TyVarSymbol> scope)74 private static Type wildTy(WildTySig sig, Function<String, TyVarSymbol> scope) { 75 switch (sig.boundKind()) { 76 case NONE: 77 return Type.WildUnboundedTy.create(ImmutableList.of()); 78 case LOWER: 79 return Type.WildLowerBoundedTy.create( 80 bindTy(((LowerBoundTySig) sig).bound(), scope), ImmutableList.of()); 81 case UPPER: 82 return Type.WildUpperBoundedTy.create( 83 bindTy(((UpperBoundTySig) sig).bound(), scope), ImmutableList.of()); 84 default: 85 throw new AssertionError(sig.boundKind()); 86 } 87 } 88 bindTy(Sig.TySig sig, Function<String, TyVarSymbol> scope)89 static Type bindTy(Sig.TySig sig, Function<String, TyVarSymbol> scope) { 90 switch (sig.kind()) { 91 case BASE_TY_SIG: 92 return Type.PrimTy.create(((Sig.BaseTySig) sig).type(), ImmutableList.of()); 93 case CLASS_TY_SIG: 94 return bindClassTy((Sig.ClassTySig) sig, scope); 95 case TY_VAR_SIG: 96 return Type.TyVar.create(scope.apply(((Sig.TyVarSig) sig).name()), ImmutableList.of()); 97 case ARRAY_TY_SIG: 98 return bindArrayTy((Sig.ArrayTySig) sig, scope); 99 case WILD_TY_SIG: 100 return wildTy((WildTySig) sig, scope); 101 case VOID_TY_SIG: 102 return Type.VOID; 103 default: 104 throw new AssertionError(sig.kind()); 105 } 106 } 107 bindArrayTy(Sig.ArrayTySig arrayTySig, Function<String, TyVarSymbol> scope)108 private static Type bindArrayTy(Sig.ArrayTySig arrayTySig, Function<String, TyVarSymbol> scope) { 109 return Type.ArrayTy.create(bindTy(arrayTySig.elementType(), scope), ImmutableList.of()); 110 } 111 bindValue(Type type, ElementValue value)112 public static Const bindValue(Type type, ElementValue value) { 113 switch (value.kind()) { 114 case ENUM: 115 return bindEnumValue((EnumConstValue) value); 116 case CONST: 117 return bindConstValue(type, ((ConstValue) value).value()); 118 case ARRAY: 119 return bindArrayValue(type, (ArrayValue) value); 120 case CLASS: 121 return new TurbineClassValue( 122 bindTy( 123 new SigParser(((ConstTurbineClassValue) value).className()).parseType(), 124 x -> { 125 throw new IllegalStateException(x); 126 })); 127 case ANNOTATION: 128 return bindAnnotationValue(type, ((ElementValue.AnnotationValue) value).annotation()); 129 } 130 throw new AssertionError(value.kind()); 131 } 132 bindAnnotationValue(Type type, AnnotationInfo value)133 static AnnotationValue bindAnnotationValue(Type type, AnnotationInfo value) { 134 ClassSymbol sym = asClassSymbol(value.typeName()); 135 ImmutableMap.Builder<String, Const> values = ImmutableMap.builder(); 136 for (Map.Entry<String, ElementValue> e : value.elementValuePairs().entrySet()) { 137 values.put(e.getKey(), bindValue(type, e.getValue())); 138 } 139 return new AnnotationValue(sym, values.build()); 140 } 141 bindAnnotations(List<AnnotationInfo> input)142 static ImmutableList<AnnoInfo> bindAnnotations(List<AnnotationInfo> input) { 143 ImmutableList.Builder<AnnoInfo> result = ImmutableList.builder(); 144 for (AnnotationInfo annotation : input) { 145 AnnotationValue anno = bindAnnotationValue(Type.VOID, annotation); 146 result.add(new AnnoInfo(null, anno.sym(), null, anno.values())); 147 } 148 return result.build(); 149 } 150 asClassSymbol(String s)151 private static ClassSymbol asClassSymbol(String s) { 152 return new ClassSymbol(s.substring(1, s.length() - 1)); 153 } 154 bindArrayValue(Type type, ArrayValue value)155 private static Const bindArrayValue(Type type, ArrayValue value) { 156 ImmutableList.Builder<Const> elements = ImmutableList.builder(); 157 for (ElementValue element : value.elements()) { 158 elements.add(bindValue(type, element)); 159 } 160 return new ArrayInitValue(elements.build()); 161 } 162 bindConstValue(Type type, Const.Value value)163 public static Const.Value bindConstValue(Type type, Const.Value value) { 164 if (type.tyKind() != Type.TyKind.PRIM_TY) { 165 return value; 166 } 167 // TODO(b/32626659): this is not bug-compatible with javac 168 switch (((Type.PrimTy) type).primkind()) { 169 case CHAR: 170 return new Const.CharValue(value.asChar().value()); 171 case SHORT: 172 return new Const.ShortValue(value.asShort().value()); 173 case INT: 174 return new Const.IntValue(value.asInteger().value()); 175 case LONG: 176 return new Const.LongValue(value.asLong().value()); 177 case FLOAT: 178 return new Const.FloatValue(value.asFloat().value()); 179 case DOUBLE: 180 return new Const.DoubleValue(value.asDouble().value()); 181 case BOOLEAN: 182 // boolean constants are encoded as integers 183 return new Const.BooleanValue(value.asInteger().value() != 0); 184 case BYTE: 185 return new Const.ByteValue(value.asByte().value()); 186 case STRING: 187 case NULL: 188 return value; 189 } 190 throw new AssertionError(type); 191 } 192 bindEnumValue(EnumConstValue value)193 private static Const bindEnumValue(EnumConstValue value) { 194 return new EnumConstantValue( 195 new FieldSymbol(asClassSymbol(value.typeName()), value.constName())); 196 } 197 198 /** 199 * Returns a {@link ModuleInfo} given a module-info class file. Currently only the module's name, 200 * version, and flags are populated, since the directives are not needed by turbine at compile 201 * time. 202 */ bindModuleInfo(String path, Supplier<byte[]> bytes)203 public static ModuleInfo bindModuleInfo(String path, Supplier<byte[]> bytes) { 204 ClassFile classFile = ClassReader.read(path, bytes.get()); 205 ClassFile.ModuleInfo module = classFile.module(); 206 return new ModuleInfo( 207 module.name(), 208 module.version(), 209 module.flags(), 210 /* annos= */ ImmutableList.of(), 211 /* requires= */ ImmutableList.of(), 212 /* exports= */ ImmutableList.of(), 213 /* opens= */ ImmutableList.of(), 214 /* uses= */ ImmutableList.of(), 215 /* provides= */ ImmutableList.of()); 216 } 217 } 218