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.EnumConstantValue; 22 import com.google.turbine.binder.bound.ModuleInfo; 23 import com.google.turbine.binder.bound.TurbineAnnotationValue; 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 } 85 throw new AssertionError(sig.boundKind()); 86 } 87 bindTy(Sig.TySig sig, Function<String, TyVarSymbol> scope)88 static Type bindTy(Sig.TySig sig, Function<String, TyVarSymbol> scope) { 89 switch (sig.kind()) { 90 case BASE_TY_SIG: 91 return Type.PrimTy.create(((Sig.BaseTySig) sig).type(), ImmutableList.of()); 92 case CLASS_TY_SIG: 93 return bindClassTy((Sig.ClassTySig) sig, scope); 94 case TY_VAR_SIG: 95 return Type.TyVar.create(scope.apply(((Sig.TyVarSig) sig).name()), ImmutableList.of()); 96 case ARRAY_TY_SIG: 97 return bindArrayTy((Sig.ArrayTySig) sig, scope); 98 case WILD_TY_SIG: 99 return wildTy((WildTySig) sig, scope); 100 case VOID_TY_SIG: 101 return Type.VOID; 102 } 103 throw new AssertionError(sig.kind()); 104 } 105 bindArrayTy(Sig.ArrayTySig arrayTySig, Function<String, TyVarSymbol> scope)106 private static Type bindArrayTy(Sig.ArrayTySig arrayTySig, Function<String, TyVarSymbol> scope) { 107 return Type.ArrayTy.create(bindTy(arrayTySig.elementType(), scope), ImmutableList.of()); 108 } 109 bindValue(ElementValue value)110 public static Const bindValue(ElementValue value) { 111 switch (value.kind()) { 112 case ENUM: 113 return bindEnumValue((EnumConstValue) value); 114 case CONST: 115 return ((ConstValue) value).value(); 116 case ARRAY: 117 return bindArrayValue((ArrayValue) value); 118 case CLASS: 119 return new TurbineClassValue( 120 bindTy( 121 new SigParser(((ConstTurbineClassValue) value).className()).parseType(), 122 x -> { 123 throw new IllegalStateException(x); 124 })); 125 case ANNOTATION: 126 return bindAnnotationValue(((ElementValue.ConstTurbineAnnotationValue) value).annotation()); 127 } 128 throw new AssertionError(value.kind()); 129 } 130 bindAnnotationValue(AnnotationInfo value)131 static TurbineAnnotationValue bindAnnotationValue(AnnotationInfo value) { 132 ClassSymbol sym = asClassSymbol(value.typeName()); 133 ImmutableMap.Builder<String, Const> values = ImmutableMap.builder(); 134 for (Map.Entry<String, ElementValue> e : value.elementValuePairs().entrySet()) { 135 values.put(e.getKey(), bindValue(e.getValue())); 136 } 137 return new TurbineAnnotationValue(new AnnoInfo(null, sym, null, values.build())); 138 } 139 bindAnnotations(List<AnnotationInfo> input)140 static ImmutableList<AnnoInfo> bindAnnotations(List<AnnotationInfo> input) { 141 ImmutableList.Builder<AnnoInfo> result = ImmutableList.builder(); 142 for (AnnotationInfo annotation : input) { 143 TurbineAnnotationValue anno = bindAnnotationValue(annotation); 144 if (!shouldSkip(anno)) { 145 result.add(anno.info()); 146 } 147 } 148 return result.build(); 149 } 150 shouldSkip(TurbineAnnotationValue anno)151 private static boolean shouldSkip(TurbineAnnotationValue anno) { 152 // ct.sym contains fake annotations without corresponding class files. 153 return anno.sym().equals(ClassSymbol.PROFILE_ANNOTATION) 154 || anno.sym().equals(ClassSymbol.PROPRIETARY_ANNOTATION); 155 } 156 asClassSymbol(String s)157 private static ClassSymbol asClassSymbol(String s) { 158 return new ClassSymbol(s.substring(1, s.length() - 1)); 159 } 160 bindArrayValue(ArrayValue value)161 private static Const bindArrayValue(ArrayValue value) { 162 ImmutableList.Builder<Const> elements = ImmutableList.builder(); 163 for (ElementValue element : value.elements()) { 164 elements.add(bindValue(element)); 165 } 166 return new ArrayInitValue(elements.build()); 167 } 168 bindConstValue(Type type, Const.Value value)169 public static Const.Value bindConstValue(Type type, Const.Value value) { 170 if (type.tyKind() != Type.TyKind.PRIM_TY) { 171 return value; 172 } 173 // Deficient numberic types and booleans are all stored as ints in the class file, 174 // coerce them to the target type. 175 // TODO(b/32626659): this is not bug-compatible with javac 176 switch (((Type.PrimTy) type).primkind()) { 177 case CHAR: 178 return new Const.CharValue(value.asChar().value()); 179 case SHORT: 180 return new Const.ShortValue(value.asShort().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 default: 187 return value; 188 } 189 } 190 bindEnumValue(EnumConstValue value)191 private static Const bindEnumValue(EnumConstValue value) { 192 return new EnumConstantValue( 193 new FieldSymbol(asClassSymbol(value.typeName()), value.constName())); 194 } 195 196 /** 197 * Returns a {@link ModuleInfo} given a module-info class file. Currently only the module's name, 198 * version, and flags are populated, since the directives are not needed by turbine at compile 199 * time. 200 */ bindModuleInfo(String path, Supplier<byte[]> bytes)201 public static ModuleInfo bindModuleInfo(String path, Supplier<byte[]> bytes) { 202 ClassFile classFile = ClassReader.read(path, bytes.get()); 203 ClassFile.ModuleInfo module = classFile.module(); 204 return new ModuleInfo( 205 module.name(), 206 module.version(), 207 module.flags(), 208 /* annos= */ ImmutableList.of(), 209 /* requires= */ ImmutableList.of(), 210 /* exports= */ ImmutableList.of(), 211 /* opens= */ ImmutableList.of(), 212 /* uses= */ ImmutableList.of(), 213 /* provides= */ ImmutableList.of()); 214 } 215 } 216