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