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 com.google.common.base.MoreObjects.firstNonNull; 20 import static com.google.common.base.Verify.verify; 21 22 import com.google.common.base.Supplier; 23 import com.google.common.base.Suppliers; 24 import com.google.common.collect.ImmutableList; 25 import com.google.common.collect.ImmutableMap; 26 import com.google.common.collect.ImmutableSet; 27 import com.google.turbine.binder.bound.AnnotationMetadata; 28 import com.google.turbine.binder.bound.BoundClass; 29 import com.google.turbine.binder.bound.HeaderBoundClass; 30 import com.google.turbine.binder.bound.TypeBoundClass; 31 import com.google.turbine.binder.env.Env; 32 import com.google.turbine.binder.sym.ClassSymbol; 33 import com.google.turbine.binder.sym.FieldSymbol; 34 import com.google.turbine.binder.sym.MethodSymbol; 35 import com.google.turbine.binder.sym.TyVarSymbol; 36 import com.google.turbine.bytecode.ClassFile; 37 import com.google.turbine.bytecode.ClassFile.AnnotationInfo; 38 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue; 39 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ArrayValue; 40 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstTurbineClassValue; 41 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.EnumConstValue; 42 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.Kind; 43 import com.google.turbine.bytecode.ClassFile.MethodInfo.ParameterInfo; 44 import com.google.turbine.bytecode.ClassReader; 45 import com.google.turbine.bytecode.sig.Sig; 46 import com.google.turbine.bytecode.sig.Sig.ClassSig; 47 import com.google.turbine.bytecode.sig.Sig.ClassTySig; 48 import com.google.turbine.bytecode.sig.Sig.TySig; 49 import com.google.turbine.bytecode.sig.SigParser; 50 import com.google.turbine.model.Const; 51 import com.google.turbine.model.TurbineElementType; 52 import com.google.turbine.model.TurbineFlag; 53 import com.google.turbine.model.TurbineTyKind; 54 import com.google.turbine.type.AnnoInfo; 55 import com.google.turbine.type.Type; 56 import com.google.turbine.type.Type.ClassTy; 57 import com.google.turbine.type.Type.IntersectionTy; 58 import java.lang.annotation.RetentionPolicy; 59 import java.util.Map; 60 import java.util.function.Function; 61 import javax.annotation.Nullable; 62 63 /** 64 * A bound class backed by a class file. 65 * 66 * <p>Implements all of the phase-specific bound class interfaces, and lazily fills in data from the 67 * classfile needed to implement them. This is safe because the types in bytecode are already fully 68 * resolved and canonicalized so there are no cycles. The laziness also minimizes the amount of work 69 * done on the classpath. 70 */ 71 public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBoundClass { 72 73 private final ClassSymbol sym; 74 private final Env<ClassSymbol, BytecodeBoundClass> env; 75 private final Supplier<ClassFile> classFile; 76 private final String jarFile; 77 78 public BytecodeBoundClass( 79 ClassSymbol sym, 80 Supplier<byte[]> bytes, 81 Env<ClassSymbol, BytecodeBoundClass> env, 82 String jarFile) { 83 this.sym = sym; 84 this.env = env; 85 this.jarFile = jarFile; 86 this.classFile = 87 Suppliers.memoize( 88 new Supplier<ClassFile>() { 89 @Override 90 public ClassFile get() { 91 ClassFile cf = ClassReader.read(jarFile + "!" + sym.binaryName(), bytes.get()); 92 verify( 93 cf.name().equals(sym.binaryName()), 94 "expected class data for %s, saw %s instead", 95 sym.binaryName(), 96 cf.name()); 97 return cf; 98 } 99 }); 100 } 101 102 private final Supplier<TurbineTyKind> kind = 103 Suppliers.memoize( 104 new Supplier<TurbineTyKind>() { 105 @Override 106 public TurbineTyKind get() { 107 int access = access(); 108 if ((access & TurbineFlag.ACC_ANNOTATION) == TurbineFlag.ACC_ANNOTATION) { 109 return TurbineTyKind.ANNOTATION; 110 } 111 if ((access & TurbineFlag.ACC_INTERFACE) == TurbineFlag.ACC_INTERFACE) { 112 return TurbineTyKind.INTERFACE; 113 } 114 if ((access & TurbineFlag.ACC_ENUM) == TurbineFlag.ACC_ENUM) { 115 return TurbineTyKind.ENUM; 116 } 117 return TurbineTyKind.CLASS; 118 } 119 }); 120 121 @Override 122 public TurbineTyKind kind() { 123 return kind.get(); 124 } 125 126 private final Supplier<ClassSymbol> owner = 127 Suppliers.memoize( 128 new Supplier<ClassSymbol>() { 129 @Override 130 public ClassSymbol get() { 131 for (ClassFile.InnerClass inner : classFile.get().innerClasses()) { 132 if (sym.binaryName().equals(inner.innerClass())) { 133 return new ClassSymbol(inner.outerClass()); 134 } 135 } 136 return null; 137 } 138 }); 139 140 @Nullable 141 @Override 142 public ClassSymbol owner() { 143 return owner.get(); 144 } 145 146 private final Supplier<ImmutableMap<String, ClassSymbol>> children = 147 Suppliers.memoize( 148 new Supplier<ImmutableMap<String, ClassSymbol>>() { 149 @Override 150 public ImmutableMap<String, ClassSymbol> get() { 151 ImmutableMap.Builder<String, ClassSymbol> result = ImmutableMap.builder(); 152 for (ClassFile.InnerClass inner : classFile.get().innerClasses()) { 153 if (inner.innerName() == null) { 154 // anonymous class 155 continue; 156 } 157 if (sym.binaryName().equals(inner.outerClass())) { 158 result.put(inner.innerName(), new ClassSymbol(inner.innerClass())); 159 } 160 } 161 return result.build(); 162 } 163 }); 164 165 @Override 166 public ImmutableMap<String, ClassSymbol> children() { 167 return children.get(); 168 } 169 170 private final Supplier<Integer> access = 171 Suppliers.memoize( 172 new Supplier<Integer>() { 173 @Override 174 public Integer get() { 175 int access = classFile.get().access(); 176 for (ClassFile.InnerClass inner : classFile.get().innerClasses()) { 177 if (sym.binaryName().equals(inner.innerClass())) { 178 access = inner.access(); 179 } 180 } 181 return access; 182 } 183 }); 184 185 @Override 186 public int access() { 187 return access.get(); 188 } 189 190 private final Supplier<ClassSig> sig = 191 Suppliers.memoize( 192 new Supplier<ClassSig>() { 193 @Override 194 public ClassSig get() { 195 String signature = classFile.get().signature(); 196 if (signature == null) { 197 return null; 198 } 199 return new SigParser(signature).parseClassSig(); 200 } 201 }); 202 203 private final Supplier<ImmutableMap<String, TyVarSymbol>> tyParams = 204 Suppliers.memoize( 205 new Supplier<ImmutableMap<String, TyVarSymbol>>() { 206 @Override 207 public ImmutableMap<String, TyVarSymbol> get() { 208 ClassSig csig = sig.get(); 209 if (csig == null || csig.tyParams().isEmpty()) { 210 return ImmutableMap.of(); 211 } 212 ImmutableMap.Builder<String, TyVarSymbol> result = ImmutableMap.builder(); 213 for (Sig.TyParamSig p : csig.tyParams()) { 214 result.put(p.name(), new TyVarSymbol(sym, p.name())); 215 } 216 return result.build(); 217 } 218 }); 219 220 @Override 221 public ImmutableMap<String, TyVarSymbol> typeParameters() { 222 return tyParams.get(); 223 } 224 225 private final Supplier<ClassSymbol> superclass = 226 Suppliers.memoize( 227 new Supplier<ClassSymbol>() { 228 @Override 229 public ClassSymbol get() { 230 String superclass = classFile.get().superName(); 231 if (superclass == null) { 232 return null; 233 } 234 return new ClassSymbol(superclass); 235 } 236 }); 237 238 @Override 239 public ClassSymbol superclass() { 240 return superclass.get(); 241 } 242 243 private final Supplier<ImmutableList<ClassSymbol>> interfaces = 244 Suppliers.memoize( 245 new Supplier<ImmutableList<ClassSymbol>>() { 246 @Override 247 public ImmutableList<ClassSymbol> get() { 248 ImmutableList.Builder<ClassSymbol> result = ImmutableList.builder(); 249 for (String i : classFile.get().interfaces()) { 250 result.add(new ClassSymbol(i)); 251 } 252 return result.build(); 253 } 254 }); 255 256 @Override 257 public ImmutableList<ClassSymbol> interfaces() { 258 return interfaces.get(); 259 } 260 261 private final Supplier<ClassTy> superClassType = 262 Suppliers.memoize( 263 new Supplier<ClassTy>() { 264 @Override 265 public ClassTy get() { 266 if (superclass() == null) { 267 return null; 268 } 269 if (sig.get() == null || sig.get().superClass() == null) { 270 return ClassTy.asNonParametricClassTy(superclass()); 271 } 272 return BytecodeBinder.bindClassTy( 273 sig.get().superClass(), makeScope(env, sym, ImmutableMap.of())); 274 } 275 }); 276 277 @Override 278 public ClassTy superClassType() { 279 return superClassType.get(); 280 } 281 282 private final Supplier<ImmutableList<Type>> interfaceTypes = 283 Suppliers.memoize( 284 new Supplier<ImmutableList<Type>>() { 285 @Override 286 public ImmutableList<Type> get() { 287 if (interfaces().isEmpty()) { 288 return ImmutableList.of(); 289 } 290 ImmutableList.Builder<Type> result = ImmutableList.builder(); 291 if (sig.get() == null || sig.get().interfaces() == null) { 292 for (ClassSymbol sym : interfaces()) { 293 result.add(ClassTy.asNonParametricClassTy(sym)); 294 } 295 } else { 296 Function<String, TyVarSymbol> scope = makeScope(env, sym, ImmutableMap.of()); 297 for (ClassTySig classTySig : sig.get().interfaces()) { 298 result.add(BytecodeBinder.bindClassTy(classTySig, scope)); 299 } 300 } 301 return result.build(); 302 } 303 }); 304 305 @Override 306 public ImmutableList<Type> interfaceTypes() { 307 return interfaceTypes.get(); 308 } 309 310 private final Supplier<ImmutableMap<TyVarSymbol, TyVarInfo>> typeParameterTypes = 311 Suppliers.memoize( 312 new Supplier<ImmutableMap<TyVarSymbol, TyVarInfo>>() { 313 @Override 314 public ImmutableMap<TyVarSymbol, TyVarInfo> get() { 315 if (sig.get() == null) { 316 return ImmutableMap.of(); 317 } 318 ImmutableMap.Builder<TyVarSymbol, TyVarInfo> tparams = ImmutableMap.builder(); 319 Function<String, TyVarSymbol> scope = makeScope(env, sym, typeParameters()); 320 for (Sig.TyParamSig p : sig.get().tyParams()) { 321 tparams.put(typeParameters().get(p.name()), bindTyParam(p, scope)); 322 } 323 return tparams.build(); 324 } 325 }); 326 327 private static TyVarInfo bindTyParam(Sig.TyParamSig sig, Function<String, TyVarSymbol> scope) { 328 ImmutableList.Builder<Type> bounds = ImmutableList.builder(); 329 if (sig.classBound() != null) { 330 bounds.add(BytecodeBinder.bindTy(sig.classBound(), scope)); 331 } 332 for (Sig.TySig t : sig.interfaceBounds()) { 333 bounds.add(BytecodeBinder.bindTy(t, scope)); 334 } 335 return new TyVarInfo(IntersectionTy.create(bounds.build()), ImmutableList.of()); 336 } 337 338 @Override 339 public ImmutableMap<TyVarSymbol, TyVarInfo> typeParameterTypes() { 340 return typeParameterTypes.get(); 341 } 342 343 private final Supplier<ImmutableList<FieldInfo>> fields = 344 Suppliers.memoize( 345 new Supplier<ImmutableList<FieldInfo>>() { 346 @Override 347 public ImmutableList<FieldInfo> get() { 348 ImmutableList.Builder<FieldInfo> fields = ImmutableList.builder(); 349 for (ClassFile.FieldInfo cfi : classFile.get().fields()) { 350 FieldSymbol fieldSym = new FieldSymbol(sym, cfi.name()); 351 Type type = 352 BytecodeBinder.bindTy( 353 new SigParser(cfi.descriptor()).parseType(), 354 makeScope(env, sym, ImmutableMap.of())); 355 int access = cfi.access(); 356 Const.Value value = cfi.value(); 357 if (value != null) { 358 value = BytecodeBinder.bindConstValue(type, value); 359 } 360 fields.add(new FieldInfo(fieldSym, type, access, ImmutableList.of(), null, value)); 361 } 362 return fields.build(); 363 } 364 }); 365 366 @Override 367 public ImmutableList<FieldInfo> fields() { 368 return fields.get(); 369 } 370 371 private final Supplier<ImmutableList<MethodInfo>> methods = 372 Suppliers.memoize( 373 new Supplier<ImmutableList<MethodInfo>>() { 374 @Override 375 public ImmutableList<MethodInfo> get() { 376 ImmutableList.Builder<MethodInfo> methods = ImmutableList.builder(); 377 for (ClassFile.MethodInfo m : classFile.get().methods()) { 378 methods.add(bindMethod(m)); 379 } 380 return methods.build(); 381 } 382 }); 383 384 private MethodInfo bindMethod(ClassFile.MethodInfo m) { 385 MethodSymbol methodSymbol = new MethodSymbol(sym, m.name()); 386 Sig.MethodSig sig = new SigParser(firstNonNull(m.signature(), m.descriptor())).parseMethodSig(); 387 388 ImmutableMap<String, TyVarSymbol> tyParams; 389 { 390 ImmutableMap.Builder<String, TyVarSymbol> result = ImmutableMap.builder(); 391 for (Sig.TyParamSig p : sig.tyParams()) { 392 result.put(p.name(), new TyVarSymbol(methodSymbol, p.name())); 393 } 394 tyParams = result.build(); 395 } 396 397 ImmutableMap<TyVarSymbol, TyVarInfo> tyParamTypes; 398 { 399 ImmutableMap.Builder<TyVarSymbol, TyVarInfo> tparams = ImmutableMap.builder(); 400 Function<String, TyVarSymbol> scope = makeScope(env, sym, tyParams); 401 for (Sig.TyParamSig p : sig.tyParams()) { 402 tparams.put(tyParams.get(p.name()), bindTyParam(p, scope)); 403 } 404 tyParamTypes = tparams.build(); 405 } 406 407 Function<String, TyVarSymbol> scope = makeScope(env, sym, tyParams); 408 409 Type ret = null; 410 if (sig.returnType() != null) { 411 ret = BytecodeBinder.bindTy(sig.returnType(), scope); 412 } 413 414 ImmutableList.Builder<ParamInfo> formals = ImmutableList.builder(); 415 int idx = 0; 416 for (Sig.TySig tySig : sig.params()) { 417 String name = null; 418 int access = 0; 419 if (idx < m.parameters().size()) { 420 ParameterInfo paramInfo = m.parameters().get(idx); 421 name = paramInfo.name(); 422 access = paramInfo.access(); 423 } 424 ImmutableList<AnnoInfo> annotations = 425 (idx < m.parameterAnnotations().size()) 426 ? BytecodeBinder.bindAnnotations(m.parameterAnnotations().get(idx)) 427 : ImmutableList.of(); 428 formals.add(new ParamInfo(BytecodeBinder.bindTy(tySig, scope), name, annotations, access)); 429 idx++; 430 } 431 432 ImmutableList.Builder<Type> exceptions = ImmutableList.builder(); 433 for (TySig e : sig.exceptions()) { 434 exceptions.add(BytecodeBinder.bindTy(e, scope)); 435 } 436 437 Const defaultValue = 438 m.defaultValue() != null ? BytecodeBinder.bindValue(ret, m.defaultValue()) : null; 439 440 ImmutableList<AnnoInfo> annotations = BytecodeBinder.bindAnnotations(m.annotations()); 441 442 return new MethodInfo( 443 methodSymbol, 444 tyParamTypes, 445 ret, 446 formals.build(), 447 exceptions.build(), 448 m.access(), 449 defaultValue, 450 /* decl= */ null, 451 annotations, 452 /* receiver= */ null); 453 } 454 455 @Override 456 public ImmutableList<MethodInfo> methods() { 457 return methods.get(); 458 } 459 460 private final Supplier<AnnotationMetadata> annotationMetadata = 461 Suppliers.memoize( 462 new Supplier<AnnotationMetadata>() { 463 @Override 464 public AnnotationMetadata get() { 465 if ((access() & TurbineFlag.ACC_ANNOTATION) != TurbineFlag.ACC_ANNOTATION) { 466 return null; 467 } 468 RetentionPolicy retention = null; 469 ImmutableSet<TurbineElementType> target = null; 470 ClassSymbol repeatable = null; 471 for (ClassFile.AnnotationInfo annotation : classFile.get().annotations()) { 472 switch (annotation.typeName()) { 473 case "Ljava/lang/annotation/Retention;": 474 retention = bindRetention(annotation); 475 break; 476 case "Ljava/lang/annotation/Target;": 477 target = bindTarget(annotation); 478 break; 479 case "Ljava/lang/annotation/Repeatable;": 480 repeatable = bindRepeatable(annotation); 481 break; 482 default: 483 break; 484 } 485 } 486 return new AnnotationMetadata(retention, target, repeatable); 487 } 488 }); 489 490 private RetentionPolicy bindRetention(AnnotationInfo annotation) { 491 ElementValue val = annotation.elementValuePairs().get("value"); 492 if (val.kind() != Kind.ENUM) { 493 return null; 494 } 495 EnumConstValue enumVal = (EnumConstValue) val; 496 if (!enumVal.typeName().equals("Ljava/lang/annotation/RetentionPolicy;")) { 497 return null; 498 } 499 return RetentionPolicy.valueOf(enumVal.constName()); 500 } 501 502 private static ImmutableSet<TurbineElementType> bindTarget(AnnotationInfo annotation) { 503 ImmutableSet.Builder<TurbineElementType> result = ImmutableSet.builder(); 504 ElementValue val = annotation.elementValuePairs().get("value"); 505 switch (val.kind()) { 506 case ARRAY: 507 for (ElementValue element : ((ArrayValue) val).elements()) { 508 if (element.kind() == Kind.ENUM) { 509 bindTargetElement(result, (EnumConstValue) element); 510 } 511 } 512 break; 513 case ENUM: 514 bindTargetElement(result, (EnumConstValue) val); 515 break; 516 default: 517 break; 518 } 519 return result.build(); 520 } 521 522 private static void bindTargetElement( 523 ImmutableSet.Builder<TurbineElementType> target, EnumConstValue enumVal) { 524 if (enumVal.typeName().equals("Ljava/lang/annotation/ElementType;")) { 525 target.add(TurbineElementType.valueOf(enumVal.constName())); 526 } 527 } 528 529 private static ClassSymbol bindRepeatable(AnnotationInfo annotation) { 530 ElementValue val = annotation.elementValuePairs().get("value"); 531 switch (val.kind()) { 532 case CLASS: 533 String className = ((ConstTurbineClassValue) val).className(); 534 return new ClassSymbol(className.substring(1, className.length() - 1)); 535 default: 536 break; 537 } 538 return null; 539 } 540 541 @Override 542 public AnnotationMetadata annotationMetadata() { 543 return annotationMetadata.get(); 544 } 545 546 private final Supplier<ImmutableList<AnnoInfo>> annotations = 547 Suppliers.memoize( 548 new Supplier<ImmutableList<AnnoInfo>>() { 549 @Override 550 public ImmutableList<AnnoInfo> get() { 551 return BytecodeBinder.bindAnnotations(classFile.get().annotations()); 552 } 553 }); 554 555 @Override 556 public ImmutableList<AnnoInfo> annotations() { 557 return annotations.get(); 558 } 559 560 /** 561 * Create a scope for resolving type variable symbols declared in the class, and any enclosing 562 * instances. 563 */ 564 private static Function<String, TyVarSymbol> makeScope( 565 final Env<ClassSymbol, BytecodeBoundClass> env, 566 final ClassSymbol sym, 567 final Map<String, TyVarSymbol> typeVariables) { 568 return new Function<String, TyVarSymbol>() { 569 @Override 570 public TyVarSymbol apply(String input) { 571 TyVarSymbol result = typeVariables.get(input); 572 if (result != null) { 573 return result; 574 } 575 ClassSymbol curr = sym; 576 while (curr != null) { 577 BytecodeBoundClass info = env.get(curr); 578 if (info == null) { 579 throw new AssertionError(curr); 580 } 581 result = info.typeParameters().get(input); 582 if (result != null) { 583 return result; 584 } 585 curr = info.owner(); 586 } 587 throw new AssertionError(input); 588 } 589 }; 590 } 591 592 /** The jar file the symbol was loaded from. */ 593 public String jarFile() { 594 return jarFile; 595 } 596 597 /** The class file the symbol was loaded from. */ 598 public ClassFile classFile() { 599 return classFile.get(); 600 } 601 } 602