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.ImmutableListMultimap; 23 import com.google.common.collect.ImmutableMap; 24 import com.google.common.collect.Iterables; 25 import com.google.turbine.binder.bound.EnumConstantValue; 26 import com.google.turbine.binder.bound.ModuleInfo; 27 import com.google.turbine.binder.bound.TurbineAnnotationValue; 28 import com.google.turbine.binder.bound.TurbineClassValue; 29 import com.google.turbine.binder.sym.ClassSymbol; 30 import com.google.turbine.binder.sym.FieldSymbol; 31 import com.google.turbine.binder.sym.TyVarSymbol; 32 import com.google.turbine.bytecode.ClassFile; 33 import com.google.turbine.bytecode.ClassFile.AnnotationInfo; 34 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue; 35 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ArrayValue; 36 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstTurbineClassValue; 37 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstValue; 38 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.EnumConstValue; 39 import com.google.turbine.bytecode.ClassFile.TypeAnnotationInfo; 40 import com.google.turbine.bytecode.ClassReader; 41 import com.google.turbine.bytecode.sig.Sig; 42 import com.google.turbine.bytecode.sig.Sig.LowerBoundTySig; 43 import com.google.turbine.bytecode.sig.Sig.UpperBoundTySig; 44 import com.google.turbine.bytecode.sig.Sig.WildTySig; 45 import com.google.turbine.bytecode.sig.SigParser; 46 import com.google.turbine.model.Const; 47 import com.google.turbine.model.Const.ArrayInitValue; 48 import com.google.turbine.model.Const.Value; 49 import com.google.turbine.type.AnnoInfo; 50 import com.google.turbine.type.Type; 51 import java.util.ArrayDeque; 52 import java.util.ArrayList; 53 import java.util.LinkedHashMap; 54 import java.util.List; 55 import java.util.Map; 56 import java.util.function.Supplier; 57 import org.jspecify.annotations.Nullable; 58 59 /** Bind {@link Type}s from bytecode. */ 60 public final class BytecodeBinder { 61 62 /** Context that is required to create types from type signatures in bytecode. */ 63 interface Scope { 64 /** Look up a type variable by name on an enclosing method or class. */ apply(String input)65 TyVarSymbol apply(String input); 66 67 /** 68 * Returns the enclosing class for a nested class, or {@code null}. 69 * 70 * <p>Locating type annotations on nested classes requires knowledge of their enclosing types. 71 */ outer(ClassSymbol sym)72 @Nullable ClassSymbol outer(ClassSymbol sym); 73 } 74 bindClassTy( Sig.ClassTySig sig, Scope scope, ImmutableList<TypeAnnotationInfo> annotations)75 public static Type.ClassTy bindClassTy( 76 Sig.ClassTySig sig, Scope scope, ImmutableList<TypeAnnotationInfo> annotations) { 77 return bindClassTy( 78 sig, scope, typeAnnotationsByPath(annotations, scope), TypeAnnotationInfo.TypePath.root()); 79 } 80 bindClassTy( Sig.ClassTySig sig, Scope scope, ImmutableListMultimap<TypeAnnotationInfo.TypePath, AnnoInfo> annotations, TypeAnnotationInfo.TypePath typePath)81 private static Type.ClassTy bindClassTy( 82 Sig.ClassTySig sig, 83 Scope scope, 84 ImmutableListMultimap<TypeAnnotationInfo.TypePath, AnnoInfo> annotations, 85 TypeAnnotationInfo.TypePath typePath) { 86 StringBuilder sb = new StringBuilder(); 87 if (!sig.pkg().isEmpty()) { 88 sb.append(sig.pkg()).append('/'); 89 } 90 boolean first = true; 91 Map<ClassSymbol, Sig.SimpleClassTySig> syms = new LinkedHashMap<>(); 92 for (Sig.SimpleClassTySig s : sig.classes()) { 93 if (!first) { 94 sb.append('$'); 95 } 96 sb.append(s.simpleName()); 97 ClassSymbol sym = new ClassSymbol(sb.toString()); 98 syms.put(sym, s); 99 first = false; 100 } 101 ArrayDeque<ClassSymbol> outers = new ArrayDeque<>(); 102 for (ClassSymbol curr = Iterables.getLast(syms.keySet()); 103 curr != null; 104 curr = scope.outer(curr)) { 105 outers.addFirst(curr); 106 } 107 List<Type.ClassTy.SimpleClassTy> classes = new ArrayList<>(); 108 for (ClassSymbol curr : outers) { 109 ImmutableList.Builder<Type> tyArgs = ImmutableList.builder(); 110 Sig.SimpleClassTySig s = syms.get(curr); 111 if (s != null) { 112 for (int i = 0; i < s.tyArgs().size(); i++) { 113 tyArgs.add(bindTy(s.tyArgs().get(i), scope, annotations, typePath.typeArgument(i))); 114 } 115 } 116 classes.add( 117 Type.ClassTy.SimpleClassTy.create(curr, tyArgs.build(), annotations.get(typePath))); 118 typePath = typePath.nested(); 119 } 120 return Type.ClassTy.create(classes); 121 } 122 wildTy( WildTySig sig, Scope scope, ImmutableListMultimap<TypeAnnotationInfo.TypePath, AnnoInfo> annotations, TypeAnnotationInfo.TypePath typePath)123 private static Type wildTy( 124 WildTySig sig, 125 Scope scope, 126 ImmutableListMultimap<TypeAnnotationInfo.TypePath, AnnoInfo> annotations, 127 TypeAnnotationInfo.TypePath typePath) { 128 switch (sig.boundKind()) { 129 case NONE: 130 return Type.WildUnboundedTy.create(annotations.get(typePath)); 131 case LOWER: 132 return Type.WildLowerBoundedTy.create( 133 bindTy(((LowerBoundTySig) sig).bound(), scope, annotations, typePath.wild()), 134 annotations.get(typePath)); 135 case UPPER: 136 return Type.WildUpperBoundedTy.create( 137 bindTy(((UpperBoundTySig) sig).bound(), scope, annotations, typePath.wild()), 138 annotations.get(typePath)); 139 } 140 throw new AssertionError(sig.boundKind()); 141 } 142 bindTy( Sig.TySig sig, Scope scope, ImmutableList<TypeAnnotationInfo> annotations)143 public static Type bindTy( 144 Sig.TySig sig, Scope scope, ImmutableList<TypeAnnotationInfo> annotations) { 145 return bindTy( 146 sig, scope, typeAnnotationsByPath(annotations, scope), TypeAnnotationInfo.TypePath.root()); 147 } 148 bindTy( Sig.TySig sig, Scope scope, ImmutableListMultimap<TypeAnnotationInfo.TypePath, AnnoInfo> annotations, TypeAnnotationInfo.TypePath typePath)149 static Type bindTy( 150 Sig.TySig sig, 151 Scope scope, 152 ImmutableListMultimap<TypeAnnotationInfo.TypePath, AnnoInfo> annotations, 153 TypeAnnotationInfo.TypePath typePath) { 154 switch (sig.kind()) { 155 case BASE_TY_SIG: 156 return Type.PrimTy.create(((Sig.BaseTySig) sig).type(), annotations.get(typePath)); 157 case CLASS_TY_SIG: 158 return bindClassTy((Sig.ClassTySig) sig, scope, annotations, typePath); 159 case TY_VAR_SIG: 160 return Type.TyVar.create( 161 scope.apply(((Sig.TyVarSig) sig).name()), annotations.get(typePath)); 162 case ARRAY_TY_SIG: 163 return bindArrayTy((Sig.ArrayTySig) sig, scope, annotations, typePath); 164 case WILD_TY_SIG: 165 return wildTy((WildTySig) sig, scope, annotations, typePath); 166 case VOID_TY_SIG: 167 return Type.VOID; 168 } 169 throw new AssertionError(sig.kind()); 170 } 171 bindArrayTy( Sig.ArrayTySig arrayTySig, Scope scope, ImmutableListMultimap<TypeAnnotationInfo.TypePath, AnnoInfo> annotations, TypeAnnotationInfo.TypePath typePath)172 private static Type bindArrayTy( 173 Sig.ArrayTySig arrayTySig, 174 Scope scope, 175 ImmutableListMultimap<TypeAnnotationInfo.TypePath, AnnoInfo> annotations, 176 TypeAnnotationInfo.TypePath typePath) { 177 return Type.ArrayTy.create( 178 bindTy(arrayTySig.elementType(), scope, annotations, typePath.array()), 179 annotations.get(typePath)); 180 } 181 typeAnnotationsByPath( ImmutableList<TypeAnnotationInfo> typeAnnotations, Scope scope)182 private static ImmutableListMultimap<TypeAnnotationInfo.TypePath, AnnoInfo> typeAnnotationsByPath( 183 ImmutableList<TypeAnnotationInfo> typeAnnotations, Scope scope) { 184 if (typeAnnotations.isEmpty()) { 185 return ImmutableListMultimap.of(); 186 } 187 ImmutableListMultimap.Builder<TypeAnnotationInfo.TypePath, AnnoInfo> result = 188 ImmutableListMultimap.builder(); 189 for (TypeAnnotationInfo typeAnnotation : typeAnnotations) { 190 result.put(typeAnnotation.path(), bindAnnotationValue(typeAnnotation.anno(), scope).info()); 191 } 192 return result.build(); 193 } 194 195 /** 196 * Similar to {@link Type.ClassTy#asNonParametricClassTy}, but handles any provided type 197 * annotations and attaches them to the corresponding {@link Type.ClassTy.SimpleClassTy}. 198 */ asNonParametricClassTy( ClassSymbol sym, ImmutableList<TypeAnnotationInfo> annotations, Scope scope)199 public static Type.ClassTy asNonParametricClassTy( 200 ClassSymbol sym, ImmutableList<TypeAnnotationInfo> annotations, Scope scope) { 201 return asNonParametricClassTy(sym, scope, typeAnnotationsByPath(annotations, scope)); 202 } 203 asNonParametricClassTy( ClassSymbol sym, Scope scope, ImmutableListMultimap<TypeAnnotationInfo.TypePath, AnnoInfo> annotations)204 private static Type.ClassTy asNonParametricClassTy( 205 ClassSymbol sym, 206 Scope scope, 207 ImmutableListMultimap<TypeAnnotationInfo.TypePath, AnnoInfo> annotations) { 208 if (annotations.isEmpty()) { 209 // fast path if there are no type annotations 210 return Type.ClassTy.asNonParametricClassTy(sym); 211 } 212 ArrayDeque<ClassSymbol> outers = new ArrayDeque<>(); 213 for (ClassSymbol curr = sym; curr != null; curr = scope.outer(curr)) { 214 outers.addFirst(curr); 215 } 216 List<Type.ClassTy.SimpleClassTy> classes = new ArrayList<>(); 217 TypeAnnotationInfo.TypePath typePath = TypeAnnotationInfo.TypePath.root(); 218 for (ClassSymbol curr : outers) { 219 classes.add( 220 Type.ClassTy.SimpleClassTy.create(curr, ImmutableList.of(), annotations.get(typePath))); 221 typePath = typePath.nested(); 222 } 223 return Type.ClassTy.create(classes); 224 } 225 bindValue(ElementValue value, Scope scope)226 public static Const bindValue(ElementValue value, Scope scope) { 227 switch (value.kind()) { 228 case ENUM: 229 return bindEnumValue((EnumConstValue) value); 230 case CONST: 231 return ((ConstValue) value).value(); 232 case ARRAY: 233 return bindArrayValue((ArrayValue) value, scope); 234 case CLASS: 235 return new TurbineClassValue( 236 bindTy( 237 new SigParser(((ConstTurbineClassValue) value).className()).parseType(), 238 new Scope() { 239 @Override 240 public TyVarSymbol apply(String x) { 241 throw new IllegalStateException(x); 242 } 243 244 @Override 245 public @Nullable ClassSymbol outer(ClassSymbol sym) { 246 return scope.outer(sym); 247 } 248 }, 249 /* annotations= */ ImmutableList.of())); 250 case ANNOTATION: 251 return bindAnnotationValue( 252 ((ElementValue.ConstTurbineAnnotationValue) value).annotation(), scope); 253 } 254 throw new AssertionError(value.kind()); 255 } 256 257 static TurbineAnnotationValue bindAnnotationValue(AnnotationInfo value, Scope scope) { 258 ClassSymbol sym = asClassSymbol(value.typeName()); 259 ImmutableMap.Builder<String, Const> values = ImmutableMap.builder(); 260 for (Map.Entry<String, ElementValue> e : value.elementValuePairs().entrySet()) { 261 values.put(e.getKey(), bindValue(e.getValue(), scope)); 262 } 263 return new TurbineAnnotationValue(new AnnoInfo(null, sym, null, values.buildOrThrow())); 264 } 265 266 static ImmutableList<AnnoInfo> bindAnnotations(List<AnnotationInfo> input, Scope scope) { 267 ImmutableList.Builder<AnnoInfo> result = ImmutableList.builder(); 268 for (AnnotationInfo annotation : input) { 269 TurbineAnnotationValue anno = bindAnnotationValue(annotation, scope); 270 if (!shouldSkip(anno)) { 271 result.add(anno.info()); 272 } 273 } 274 return result.build(); 275 } 276 277 private static boolean shouldSkip(TurbineAnnotationValue anno) { 278 // ct.sym contains fake annotations without corresponding class files. 279 return anno.sym().equals(ClassSymbol.PROFILE_ANNOTATION) 280 || anno.sym().equals(ClassSymbol.PROPRIETARY_ANNOTATION); 281 } 282 283 private static ClassSymbol asClassSymbol(String s) { 284 return new ClassSymbol(s.substring(1, s.length() - 1)); 285 } 286 287 private static Const bindArrayValue(ArrayValue value, Scope scope) { 288 ImmutableList.Builder<Const> elements = ImmutableList.builder(); 289 for (ElementValue element : value.elements()) { 290 elements.add(bindValue(element, scope)); 291 } 292 return new ArrayInitValue(elements.build()); 293 } 294 295 public static Const.Value bindConstValue(Type type, Const.Value value) { 296 if (type.tyKind() != Type.TyKind.PRIM_TY) { 297 return value; 298 } 299 // Deficient numeric types and booleans are all stored as ints in the class file, 300 // coerce them to the target type. 301 switch (((Type.PrimTy) type).primkind()) { 302 case CHAR: 303 return new Const.CharValue((char) asInt(value)); 304 case SHORT: 305 return new Const.ShortValue((short) asInt(value)); 306 case BOOLEAN: 307 // boolean constants are encoded as integers, see also JDK-8171132 308 return new Const.BooleanValue(asInt(value) != 0); 309 case BYTE: 310 return new Const.ByteValue((byte) asInt(value)); 311 default: 312 return value; 313 } 314 } 315 316 private static int asInt(Value value) { 317 return ((Const.IntValue) value).value(); 318 } 319 320 private static Const bindEnumValue(EnumConstValue value) { 321 return new EnumConstantValue( 322 new FieldSymbol(asClassSymbol(value.typeName()), value.constName())); 323 } 324 325 /** 326 * Returns a {@link ModuleInfo} given a module-info class file. Currently only the module's name, 327 * version, and flags are populated, since the directives are not needed by turbine at compile 328 * time. 329 */ 330 public static ModuleInfo bindModuleInfo(String path, Supplier<byte[]> bytes) { 331 ClassFile classFile = ClassReader.read(path, bytes.get()); 332 ClassFile.ModuleInfo module = classFile.module(); 333 requireNonNull(module, path); 334 return new ModuleInfo( 335 module.name(), 336 module.version(), 337 module.flags(), 338 /* annos= */ ImmutableList.of(), 339 /* requires= */ ImmutableList.of(), 340 /* exports= */ ImmutableList.of(), 341 /* opens= */ ImmutableList.of(), 342 /* uses= */ ImmutableList.of(), 343 /* provides= */ ImmutableList.of()); 344 } 345 346 private BytecodeBinder() {} 347 } 348