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 static java.util.Objects.requireNonNull; 20 21 import com.google.common.collect.ImmutableList; 22 import com.google.common.collect.ImmutableMap; 23 import com.google.turbine.binder.bound.EnumConstantValue; 24 import com.google.turbine.binder.bound.ModuleInfo; 25 import com.google.turbine.binder.bound.TurbineAnnotationValue; 26 import com.google.turbine.binder.bound.TurbineClassValue; 27 import com.google.turbine.binder.sym.ClassSymbol; 28 import com.google.turbine.binder.sym.FieldSymbol; 29 import com.google.turbine.binder.sym.TyVarSymbol; 30 import com.google.turbine.bytecode.ClassFile; 31 import com.google.turbine.bytecode.ClassFile.AnnotationInfo; 32 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue; 33 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ArrayValue; 34 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstTurbineClassValue; 35 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstValue; 36 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.EnumConstValue; 37 import com.google.turbine.bytecode.ClassReader; 38 import com.google.turbine.bytecode.sig.Sig; 39 import com.google.turbine.bytecode.sig.Sig.LowerBoundTySig; 40 import com.google.turbine.bytecode.sig.Sig.UpperBoundTySig; 41 import com.google.turbine.bytecode.sig.Sig.WildTySig; 42 import com.google.turbine.bytecode.sig.SigParser; 43 import com.google.turbine.model.Const; 44 import com.google.turbine.model.Const.ArrayInitValue; 45 import com.google.turbine.model.Const.Value; 46 import com.google.turbine.type.AnnoInfo; 47 import com.google.turbine.type.Type; 48 import java.util.ArrayList; 49 import java.util.List; 50 import java.util.Map; 51 import java.util.function.Function; 52 import java.util.function.Supplier; 53 54 /** Bind {@link Type}s from bytecode. */ 55 public final class BytecodeBinder { 56 bindClassTy(Sig.ClassTySig sig, Function<String, TyVarSymbol> scope)57 static Type.ClassTy bindClassTy(Sig.ClassTySig sig, Function<String, TyVarSymbol> scope) { 58 StringBuilder sb = new StringBuilder(sig.pkg()); 59 boolean first = true; 60 List<Type.ClassTy.SimpleClassTy> classes = new ArrayList<>(); 61 for (Sig.SimpleClassTySig s : sig.classes()) { 62 sb.append(first ? '/' : '$'); 63 sb.append(s.simpleName()); 64 ClassSymbol sym = new ClassSymbol(sb.toString()); 65 66 ImmutableList.Builder<Type> tyArgs = ImmutableList.builder(); 67 for (Sig.TySig arg : s.tyArgs()) { 68 tyArgs.add(bindTy(arg, scope)); 69 } 70 71 classes.add(Type.ClassTy.SimpleClassTy.create(sym, tyArgs.build(), ImmutableList.of())); 72 first = false; 73 } 74 return Type.ClassTy.create(classes); 75 } 76 wildTy(WildTySig sig, Function<String, TyVarSymbol> scope)77 private static Type wildTy(WildTySig sig, Function<String, TyVarSymbol> scope) { 78 switch (sig.boundKind()) { 79 case NONE: 80 return Type.WildUnboundedTy.create(ImmutableList.of()); 81 case LOWER: 82 return Type.WildLowerBoundedTy.create( 83 bindTy(((LowerBoundTySig) sig).bound(), scope), ImmutableList.of()); 84 case UPPER: 85 return Type.WildUpperBoundedTy.create( 86 bindTy(((UpperBoundTySig) sig).bound(), scope), ImmutableList.of()); 87 } 88 throw new AssertionError(sig.boundKind()); 89 } 90 bindTy(Sig.TySig sig, Function<String, TyVarSymbol> scope)91 static Type bindTy(Sig.TySig sig, Function<String, TyVarSymbol> scope) { 92 switch (sig.kind()) { 93 case BASE_TY_SIG: 94 return Type.PrimTy.create(((Sig.BaseTySig) sig).type(), ImmutableList.of()); 95 case CLASS_TY_SIG: 96 return bindClassTy((Sig.ClassTySig) sig, scope); 97 case TY_VAR_SIG: 98 return Type.TyVar.create(scope.apply(((Sig.TyVarSig) sig).name()), ImmutableList.of()); 99 case ARRAY_TY_SIG: 100 return bindArrayTy((Sig.ArrayTySig) sig, scope); 101 case WILD_TY_SIG: 102 return wildTy((WildTySig) sig, scope); 103 case VOID_TY_SIG: 104 return Type.VOID; 105 } 106 throw new AssertionError(sig.kind()); 107 } 108 bindArrayTy(Sig.ArrayTySig arrayTySig, Function<String, TyVarSymbol> scope)109 private static Type bindArrayTy(Sig.ArrayTySig arrayTySig, Function<String, TyVarSymbol> scope) { 110 return Type.ArrayTy.create(bindTy(arrayTySig.elementType(), scope), ImmutableList.of()); 111 } 112 bindValue(ElementValue value)113 public static Const bindValue(ElementValue value) { 114 switch (value.kind()) { 115 case ENUM: 116 return bindEnumValue((EnumConstValue) value); 117 case CONST: 118 return ((ConstValue) value).value(); 119 case ARRAY: 120 return bindArrayValue((ArrayValue) value); 121 case CLASS: 122 return new TurbineClassValue( 123 bindTy( 124 new SigParser(((ConstTurbineClassValue) value).className()).parseType(), 125 x -> { 126 throw new IllegalStateException(x); 127 })); 128 case ANNOTATION: 129 return bindAnnotationValue(((ElementValue.ConstTurbineAnnotationValue) value).annotation()); 130 } 131 throw new AssertionError(value.kind()); 132 } 133 bindAnnotationValue(AnnotationInfo value)134 static TurbineAnnotationValue bindAnnotationValue(AnnotationInfo value) { 135 ClassSymbol sym = asClassSymbol(value.typeName()); 136 ImmutableMap.Builder<String, Const> values = ImmutableMap.builder(); 137 for (Map.Entry<String, ElementValue> e : value.elementValuePairs().entrySet()) { 138 values.put(e.getKey(), bindValue(e.getValue())); 139 } 140 return new TurbineAnnotationValue(new AnnoInfo(null, sym, null, values.buildOrThrow())); 141 } 142 bindAnnotations(List<AnnotationInfo> input)143 static ImmutableList<AnnoInfo> bindAnnotations(List<AnnotationInfo> input) { 144 ImmutableList.Builder<AnnoInfo> result = ImmutableList.builder(); 145 for (AnnotationInfo annotation : input) { 146 TurbineAnnotationValue anno = bindAnnotationValue(annotation); 147 if (!shouldSkip(anno)) { 148 result.add(anno.info()); 149 } 150 } 151 return result.build(); 152 } 153 shouldSkip(TurbineAnnotationValue anno)154 private static boolean shouldSkip(TurbineAnnotationValue anno) { 155 // ct.sym contains fake annotations without corresponding class files. 156 return anno.sym().equals(ClassSymbol.PROFILE_ANNOTATION) 157 || anno.sym().equals(ClassSymbol.PROPRIETARY_ANNOTATION); 158 } 159 asClassSymbol(String s)160 private static ClassSymbol asClassSymbol(String s) { 161 return new ClassSymbol(s.substring(1, s.length() - 1)); 162 } 163 bindArrayValue(ArrayValue value)164 private static Const bindArrayValue(ArrayValue value) { 165 ImmutableList.Builder<Const> elements = ImmutableList.builder(); 166 for (ElementValue element : value.elements()) { 167 elements.add(bindValue(element)); 168 } 169 return new ArrayInitValue(elements.build()); 170 } 171 bindConstValue(Type type, Const.Value value)172 public static Const.Value bindConstValue(Type type, Const.Value value) { 173 if (type.tyKind() != Type.TyKind.PRIM_TY) { 174 return value; 175 } 176 // Deficient numberic types and booleans are all stored as ints in the class file, 177 // coerce them to the target type. 178 // TODO(b/32626659): this is not bug-compatible with javac 179 switch (((Type.PrimTy) type).primkind()) { 180 case CHAR: 181 return new Const.CharValue((char) asInt(value)); 182 case SHORT: 183 return new Const.ShortValue((short) asInt(value)); 184 case BOOLEAN: 185 // boolean constants are encoded as integers 186 return new Const.BooleanValue(asInt(value) != 0); 187 case BYTE: 188 return new Const.ByteValue((byte) asInt(value)); 189 default: 190 return value; 191 } 192 } 193 asInt(Value value)194 private static int asInt(Value value) { 195 return ((Const.IntValue) value).value(); 196 } 197 bindEnumValue(EnumConstValue value)198 private static Const bindEnumValue(EnumConstValue value) { 199 return new EnumConstantValue( 200 new FieldSymbol(asClassSymbol(value.typeName()), value.constName())); 201 } 202 203 /** 204 * Returns a {@link ModuleInfo} given a module-info class file. Currently only the module's name, 205 * version, and flags are populated, since the directives are not needed by turbine at compile 206 * time. 207 */ bindModuleInfo(String path, Supplier<byte[]> bytes)208 public static ModuleInfo bindModuleInfo(String path, Supplier<byte[]> bytes) { 209 ClassFile classFile = ClassReader.read(path, bytes.get()); 210 ClassFile.ModuleInfo module = classFile.module(); 211 requireNonNull(module, path); 212 return new ModuleInfo( 213 module.name(), 214 module.version(), 215 module.flags(), 216 /* annos= */ ImmutableList.of(), 217 /* requires= */ ImmutableList.of(), 218 /* exports= */ ImmutableList.of(), 219 /* opens= */ ImmutableList.of(), 220 /* uses= */ ImmutableList.of(), 221 /* provides= */ ImmutableList.of()); 222 } 223 BytecodeBinder()224 private BytecodeBinder() {} 225 } 226