1// Copyright 2016 The Bazel Authors. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14package com.google.devtools.build.android.desugar; 15 16import static com.google.common.base.Preconditions.checkArgument; 17import static com.google.common.base.Preconditions.checkNotNull; 18import static com.google.common.base.Preconditions.checkState; 19import static java.lang.invoke.MethodHandles.publicLookup; 20import static org.objectweb.asm.Opcodes.ASM6; 21 22import com.google.auto.value.AutoValue; 23import com.google.common.collect.ImmutableSet; 24import java.io.IOException; 25import java.lang.invoke.MethodHandle; 26import java.lang.invoke.MethodHandles.Lookup; 27import java.lang.invoke.MethodType; 28import java.lang.reflect.Constructor; 29import java.lang.reflect.Executable; 30import java.lang.reflect.Method; 31import java.lang.reflect.Modifier; 32import java.util.ArrayList; 33import java.util.Arrays; 34import java.util.HashMap; 35import java.util.Map; 36import javax.annotation.Nullable; 37import org.objectweb.asm.ClassVisitor; 38import org.objectweb.asm.Handle; 39import org.objectweb.asm.MethodVisitor; 40import org.objectweb.asm.Opcodes; 41import org.objectweb.asm.Type; 42import org.objectweb.asm.tree.AbstractInsnNode; 43import org.objectweb.asm.tree.FieldInsnNode; 44import org.objectweb.asm.tree.InsnNode; 45import org.objectweb.asm.tree.MethodNode; 46import org.objectweb.asm.tree.TypeInsnNode; 47 48/** 49 * Visitor that desugars classes with uses of lambdas into Java 7-looking code. This includes 50 * rewriting lambda-related invokedynamic instructions as well as fixing accessibility of methods 51 * that javac emits for lambda bodies. 52 * 53 * <p>Implementation note: {@link InvokeDynamicLambdaMethodCollector} needs to detect any class 54 * that this visitor may rewrite, as we conditionally apply this visitor based on it. 55 */ 56class LambdaDesugaring extends ClassVisitor { 57 58 private final ClassLoader targetLoader; 59 private final LambdaClassMaker lambdas; 60 private final ImmutableSet.Builder<String> aggregateInterfaceLambdaMethods; 61 private final Map<Handle, MethodReferenceBridgeInfo> bridgeMethods = new HashMap<>(); 62 private final ImmutableSet<MethodInfo> lambdaMethodsUsedInInvokeDyanmic; 63 private final boolean allowDefaultMethods; 64 65 private String internalName; 66 private boolean isInterface; 67 private int lambdaCount; 68 69 public LambdaDesugaring( 70 ClassVisitor dest, 71 ClassLoader targetLoader, 72 LambdaClassMaker lambdas, 73 ImmutableSet.Builder<String> aggregateInterfaceLambdaMethods, 74 ImmutableSet<MethodInfo> lambdaMethodsUsedInInvokeDyanmic, 75 boolean allowDefaultMethods) { 76 super(Opcodes.ASM6, dest); 77 this.targetLoader = targetLoader; 78 this.lambdas = lambdas; 79 this.aggregateInterfaceLambdaMethods = aggregateInterfaceLambdaMethods; 80 this.lambdaMethodsUsedInInvokeDyanmic = lambdaMethodsUsedInInvokeDyanmic; 81 this.allowDefaultMethods = allowDefaultMethods; 82 } 83 84 @Override 85 public void visit( 86 int version, 87 int access, 88 String name, 89 String signature, 90 String superName, 91 String[] interfaces) { 92 checkState(internalName == null, "not intended for reuse but reused for %s", name); 93 internalName = name; 94 isInterface = BitFlags.isSet(access, Opcodes.ACC_INTERFACE); 95 super.visit(version, access, name, signature, superName, interfaces); 96 } 97 98 @Override 99 public void visitEnd() { 100 for (Map.Entry<Handle, MethodReferenceBridgeInfo> bridge : bridgeMethods.entrySet()) { 101 Handle original = bridge.getKey(); 102 Handle neededMethod = bridge.getValue().bridgeMethod(); 103 checkState( 104 neededMethod.getTag() == Opcodes.H_INVOKESTATIC 105 || neededMethod.getTag() == Opcodes.H_INVOKEVIRTUAL, 106 "Cannot generate bridge method %s to reach %s", 107 neededMethod, 108 original); 109 checkState( 110 bridge.getValue().referenced() != null, 111 "Need referenced method %s to generate bridge %s", 112 original, 113 neededMethod); 114 115 int access = Opcodes.ACC_BRIDGE | Opcodes.ACC_SYNTHETIC | Opcodes.ACC_FINAL; 116 if (neededMethod.getTag() == Opcodes.H_INVOKESTATIC) { 117 access |= Opcodes.ACC_STATIC; 118 } 119 MethodVisitor bridgeMethod = 120 super.visitMethod( 121 access, 122 neededMethod.getName(), 123 neededMethod.getDesc(), 124 (String) null, 125 toInternalNames(bridge.getValue().referenced().getExceptionTypes())); 126 127 // Bridge is a factory method calling a constructor 128 if (original.getTag() == Opcodes.H_NEWINVOKESPECIAL) { 129 bridgeMethod.visitTypeInsn(Opcodes.NEW, original.getOwner()); 130 bridgeMethod.visitInsn(Opcodes.DUP); 131 } 132 133 int slot = 0; 134 if (neededMethod.getTag() != Opcodes.H_INVOKESTATIC) { 135 bridgeMethod.visitVarInsn(Opcodes.ALOAD, slot++); 136 } 137 Type neededType = Type.getMethodType(neededMethod.getDesc()); 138 for (Type arg : neededType.getArgumentTypes()) { 139 bridgeMethod.visitVarInsn(arg.getOpcode(Opcodes.ILOAD), slot); 140 slot += arg.getSize(); 141 } 142 bridgeMethod.visitMethodInsn( 143 invokeOpcode(original), 144 original.getOwner(), 145 original.getName(), 146 original.getDesc(), 147 original.isInterface()); 148 bridgeMethod.visitInsn(neededType.getReturnType().getOpcode(Opcodes.IRETURN)); 149 150 bridgeMethod.visitMaxs(0, 0); // rely on class writer to compute these 151 bridgeMethod.visitEnd(); 152 } 153 super.visitEnd(); 154 } 155 156 // If this method changes then InvokeDynamicLambdaMethodCollector may need changes well 157 @Override 158 public MethodVisitor visitMethod( 159 int access, String name, String desc, String signature, String[] exceptions) { 160 if (name.equals("$deserializeLambda$") && BitFlags.isSet(access, Opcodes.ACC_SYNTHETIC)) { 161 // Android doesn't do anything special for lambda serialization so drop the special 162 // deserialization hook that javac generates. This also makes sure we don't reference 163 // java/lang/invoke/SerializedLambda, which doesn't exist on Android. 164 return null; 165 } 166 if (name.startsWith("lambda$") 167 && BitFlags.isSet(access, Opcodes.ACC_SYNTHETIC) 168 && lambdaMethodsUsedInInvokeDyanmic.contains(MethodInfo.create(internalName, name, desc))) { 169 if (!allowDefaultMethods && isInterface && BitFlags.isSet(access, Opcodes.ACC_STATIC)) { 170 // There must be a lambda in the interface (which in the absence of hand-written default or 171 // static interface methods must mean it's in the <clinit> method or inside another lambda). 172 // We'll move this method out of this class, so just record and drop it here. 173 // (Note lambda body methods have unique names, so we don't need to remember desc here.) 174 aggregateInterfaceLambdaMethods.add(internalName + '#' + name); 175 return null; 176 } 177 if (BitFlags.isSet(access, Opcodes.ACC_PRIVATE)) { 178 // Make lambda body method accessible from lambda class 179 access &= ~Opcodes.ACC_PRIVATE; 180 if (allowDefaultMethods && isInterface) { 181 // java 8 requires interface methods to have exactly one of ACC_PUBLIC and ACC_PRIVATE 182 access |= Opcodes.ACC_PUBLIC; 183 } else { 184 // Method was private so it can be final, which should help VMs perform dispatch. 185 access |= Opcodes.ACC_FINAL; 186 } 187 } 188 // Guarantee unique lambda body method name to avoid accidental overriding. This wouldn't be 189 // be necessary for static methods but in visitOuterClass we don't know whether a potential 190 // outer lambda$ method is static or not, so we just always do it. 191 name = uniqueInPackage(internalName, name); 192 } 193 MethodVisitor dest = super.visitMethod(access, name, desc, signature, exceptions); 194 return dest != null 195 ? new InvokedynamicRewriter(dest, access, name, desc, signature, exceptions) 196 : null; 197 } 198 199 // If this method changes then InvokeDynamicLambdaMethodCollector may need changes well 200 @Override 201 public void visitOuterClass(String owner, String name, String desc) { 202 if (name != null && name.startsWith("lambda$")) { 203 // Reflect renaming of lambda$ methods. Proguard gets grumpy if we leave this inconsistent. 204 name = uniqueInPackage(owner, name); 205 } 206 super.visitOuterClass(owner, name, desc); 207 } 208 209 // When adding visitXxx methods here then InvokeDynamicLambdaMethodCollector may need changes well 210 211 static String uniqueInPackage(String owner, String name) { 212 String suffix = "$" + owner.substring(owner.lastIndexOf('/') + 1); 213 // For idempotency, we only attach the package-unique suffix if it isn't there already. This 214 // prevents a cumulative effect when processing a class more than once (which can happen with 215 // Bazel, e.g., when re-importing a deploy.jar). During reprocessing, invokedynamics are 216 // already removed, so lambda$ methods have regular call sites that we would also have to re- 217 // adjust if we just blindly appended something to lambda$ method names every time we see them. 218 return name.endsWith(suffix) ? name : name + suffix; 219 } 220 221 /** 222 * Makes {@link #visitEnd} generate a bridge method for the given method handle if the referenced 223 * method will be invisible to the generated lambda class. 224 * 225 * @return struct containing either {@code invokedMethod} or {@code invokedMethod} and a handle 226 * representing the bridge method that will be generated for {@code invokedMethod}. 227 */ 228 private MethodReferenceBridgeInfo queueUpBridgeMethodIfNeeded(Handle invokedMethod) 229 throws ClassNotFoundException { 230 if (invokedMethod.getName().startsWith("lambda$")) { 231 // We adjust lambda bodies to be visible 232 return MethodReferenceBridgeInfo.noBridge(invokedMethod); 233 } 234 235 // invokedMethod is a method reference if we get here 236 Executable invoked = findTargetMethod(invokedMethod); 237 if (isVisibleToLambdaClass(invoked, invokedMethod.getOwner())) { 238 // Referenced method is visible to the generated class, so nothing to do 239 return MethodReferenceBridgeInfo.noBridge(invokedMethod); 240 } 241 242 // We need a bridge method if we get here 243 checkState( 244 !isInterface, 245 "%s is an interface and shouldn't need bridge to %s", 246 internalName, 247 invokedMethod); 248 checkState( 249 !invokedMethod.isInterface(), 250 "%s's lambda classes can't see interface method: %s", 251 internalName, 252 invokedMethod); 253 MethodReferenceBridgeInfo result = bridgeMethods.get(invokedMethod); 254 if (result != null) { 255 return result; // we're already queued up a bridge method for this method reference 256 } 257 258 String name = uniqueInPackage(internalName, "bridge$lambda$" + bridgeMethods.size()); 259 Handle bridgeMethod; 260 switch (invokedMethod.getTag()) { 261 case Opcodes.H_INVOKESTATIC: 262 bridgeMethod = 263 new Handle( 264 invokedMethod.getTag(), internalName, name, invokedMethod.getDesc(), /*itf*/ false); 265 break; 266 case Opcodes.H_INVOKEVIRTUAL: 267 case Opcodes.H_INVOKESPECIAL: // we end up calling these using invokevirtual 268 bridgeMethod = 269 new Handle( 270 Opcodes.H_INVOKEVIRTUAL, 271 internalName, 272 name, 273 invokedMethod.getDesc(), /*itf*/ 274 false); 275 break; 276 case Opcodes.H_NEWINVOKESPECIAL: 277 { 278 // Call invisible constructor through generated bridge "factory" method, so we need to 279 // compute the descriptor for the bridge method from the constructor's descriptor 280 String desc = 281 Type.getMethodDescriptor( 282 Type.getObjectType(invokedMethod.getOwner()), 283 Type.getArgumentTypes(invokedMethod.getDesc())); 284 bridgeMethod = 285 new Handle(Opcodes.H_INVOKESTATIC, internalName, name, desc, /*itf*/ false); 286 break; 287 } 288 case Opcodes.H_INVOKEINTERFACE: 289 // Shouldn't get here 290 default: 291 throw new UnsupportedOperationException("Cannot bridge " + invokedMethod); 292 } 293 result = MethodReferenceBridgeInfo.bridge(invokedMethod, invoked, bridgeMethod); 294 MethodReferenceBridgeInfo old = bridgeMethods.put(invokedMethod, result); 295 checkState(old == null, "Already had bridge %s so we don't also want %s", old, result); 296 return result; 297 } 298 299 /** 300 * Checks whether the referenced method would be visible by an unrelated class in the same package 301 * as the currently visited class. 302 */ 303 private boolean isVisibleToLambdaClass(Executable invoked, String owner) { 304 int modifiers = invoked.getModifiers(); 305 if (Modifier.isPrivate(modifiers)) { 306 return false; 307 } 308 if (Modifier.isPublic(modifiers)) { 309 return true; 310 } 311 // invoked is protected or package-private, either way we need it to be in the same package 312 // because the additional visibility protected gives doesn't help lambda classes, which are in 313 // a different class hierarchy (and typically just extend Object) 314 return packageName(internalName).equals(packageName(owner)); 315 } 316 317 private Executable findTargetMethod(Handle invokedMethod) throws ClassNotFoundException { 318 Type descriptor = Type.getMethodType(invokedMethod.getDesc()); 319 Class<?> owner = loadFromInternal(invokedMethod.getOwner()); 320 if (invokedMethod.getTag() == Opcodes.H_NEWINVOKESPECIAL) { 321 for (Constructor<?> c : owner.getDeclaredConstructors()) { 322 if (Type.getType(c).equals(descriptor)) { 323 return c; 324 } 325 } 326 } else { 327 for (Method m : owner.getDeclaredMethods()) { 328 if (m.getName().equals(invokedMethod.getName()) && Type.getType(m).equals(descriptor)) { 329 return m; 330 } 331 } 332 } 333 throw new IllegalArgumentException("Referenced method not found: " + invokedMethod); 334 } 335 336 private Class<?> loadFromInternal(String internalName) throws ClassNotFoundException { 337 return targetLoader.loadClass(internalName.replace('/', '.')); 338 } 339 340 static int invokeOpcode(Handle invokedMethod) { 341 switch (invokedMethod.getTag()) { 342 case Opcodes.H_INVOKESTATIC: 343 return Opcodes.INVOKESTATIC; 344 case Opcodes.H_INVOKEVIRTUAL: 345 return Opcodes.INVOKEVIRTUAL; 346 case Opcodes.H_INVOKESPECIAL: 347 case Opcodes.H_NEWINVOKESPECIAL: // Must be preceded by NEW 348 return Opcodes.INVOKESPECIAL; 349 case Opcodes.H_INVOKEINTERFACE: 350 return Opcodes.INVOKEINTERFACE; 351 default: 352 throw new UnsupportedOperationException("Don't know how to call " + invokedMethod); 353 } 354 } 355 356 private static String[] toInternalNames(Class<?>[] classes) { 357 String[] result = new String[classes.length]; 358 for (int i = 0; i < classes.length; ++i) { 359 result[i] = Type.getInternalName(classes[i]); 360 } 361 return result; 362 } 363 364 private static String packageName(String internalClassName) { 365 int lastSlash = internalClassName.lastIndexOf('/'); 366 return lastSlash > 0 ? internalClassName.substring(0, lastSlash) : ""; 367 } 368 369 /** 370 * Desugaring that replaces invokedynamics for {@link java.lang.invoke.LambdaMetafactory} with 371 * static factory method invocations and triggers a class to be generated for each invokedynamic. 372 */ 373 private class InvokedynamicRewriter extends MethodNode { 374 375 private final MethodVisitor dest; 376 377 public InvokedynamicRewriter( 378 MethodVisitor dest, 379 int access, 380 String name, 381 String desc, 382 String signature, 383 String[] exceptions) { 384 super(ASM6, access, name, desc, signature, exceptions); 385 this.dest = checkNotNull(dest, "Null destination for %s.%s : %s", internalName, name, desc); 386 } 387 388 @Override 389 public void visitEnd() { 390 accept(dest); 391 } 392 393 @Override 394 public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) { 395 if (!"java/lang/invoke/LambdaMetafactory".equals(bsm.getOwner())) { 396 // Not an invokedynamic for a lambda expression 397 super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); 398 return; 399 } 400 401 try { 402 Lookup lookup = createLookup(internalName); 403 ArrayList<Object> args = new ArrayList<>(bsmArgs.length + 3); 404 args.add(lookup); 405 args.add(name); 406 args.add(MethodType.fromMethodDescriptorString(desc, targetLoader)); 407 for (Object bsmArg : bsmArgs) { 408 args.add(toJvmMetatype(lookup, bsmArg)); 409 } 410 411 // Both bootstrap methods in LambdaMetafactory expect a MethodHandle as their 5th argument 412 // so we can assume bsmArgs[1] (the 5th arg) to be a Handle. 413 MethodReferenceBridgeInfo bridgeInfo = queueUpBridgeMethodIfNeeded((Handle) bsmArgs[1]); 414 415 // Resolve the bootstrap method in "host configuration" (this tool's default classloader) 416 // since targetLoader may only contain stubs that we can't actually execute. 417 // generateLambdaClass() below will invoke the bootstrap method, so a stub isn't enough, 418 // and ultimately we don't care if the bootstrap method was even on the bootclasspath 419 // when this class was compiled (although it must've been since javac is unhappy otherwise). 420 MethodHandle bsmMethod = toMethodHandle(publicLookup(), bsm, /*target*/ false); 421 // Give generated classes to have more stable names (b/35643761). Use BSM's naming scheme 422 // but with separate counter for each surrounding class. 423 String lambdaClassName = internalName + "$$Lambda$" + (lambdaCount++); 424 Type[] capturedTypes = Type.getArgumentTypes(desc); 425 boolean needFactory = 426 capturedTypes.length != 0 427 && !attemptAllocationBeforeArgumentLoads(lambdaClassName, capturedTypes); 428 lambdas.generateLambdaClass( 429 internalName, 430 LambdaInfo.create( 431 lambdaClassName, 432 desc, 433 needFactory, 434 bridgeInfo.methodReference(), 435 bridgeInfo.bridgeMethod()), 436 bsmMethod, 437 args); 438 if (desc.startsWith("()")) { 439 // For stateless lambda classes we'll generate a singleton instance that we can just load 440 checkState(capturedTypes.length == 0); 441 super.visitFieldInsn( 442 Opcodes.GETSTATIC, 443 lambdaClassName, 444 LambdaClassFixer.SINGLETON_FIELD_NAME, 445 desc.substring("()".length())); 446 } else if (needFactory) { 447 // If we were unable to inline the allocation of the generated lambda class then 448 // invoke factory method of generated lambda class with the arguments on the stack 449 super.visitMethodInsn( 450 Opcodes.INVOKESTATIC, 451 lambdaClassName, 452 LambdaClassFixer.FACTORY_METHOD_NAME, 453 desc, 454 /*itf*/ false); 455 } else { 456 // Otherwise we inserted a new/dup pair of instructions above and now just need to invoke 457 // the constructor of generated lambda class with the arguments on the stack 458 super.visitMethodInsn( 459 Opcodes.INVOKESPECIAL, 460 lambdaClassName, 461 "<init>", 462 Type.getMethodDescriptor(Type.VOID_TYPE, capturedTypes), 463 /*itf*/ false); 464 } 465 } catch (IOException | ReflectiveOperationException e) { 466 throw new IllegalStateException( 467 "Couldn't desugar invokedynamic for " 468 + internalName 469 + "." 470 + name 471 + " using " 472 + bsm 473 + " with arguments " 474 + Arrays.toString(bsmArgs), 475 e); 476 } 477 } 478 479 /** 480 * Tries to insert a new/dup for the given class name before expected existing instructions that 481 * set up arguments for an invokedynamic factory method with the given types. 482 * 483 * <p>For lambda expressions and simple method references we can assume that arguments are set 484 * up with loads of the captured (effectively) final variables. But method references, can in 485 * general capture an expression, such as in {@code myObject.toString()::charAt} (a {@code 486 * Function<Integer, Character>}), which can also cause null checks to be inserted. In 487 * such more complicated cases this method may fail to insert a new/dup pair and returns {@code 488 * false}. 489 * 490 * @param internalName internal name of the class to instantiate 491 * @param paramTypes expected invokedynamic argument types, which also must be the parameters of 492 * {@code internalName}'s constructor. 493 * @return {@code true} if we were able to insert a new/dup, {@code false} otherwise 494 */ 495 private boolean attemptAllocationBeforeArgumentLoads(String internalName, Type[] paramTypes) { 496 checkArgument(paramTypes.length > 0, "Expected at least one param for %s", internalName); 497 // Walk backwards past loads corresponding to constructor arguments to find the instruction 498 // after which we need to insert our NEW/DUP pair 499 AbstractInsnNode insn = instructions.getLast(); 500 for (int i = paramTypes.length - 1; 0 <= i; --i) { 501 if (insn.getOpcode() == Opcodes.GETFIELD) { 502 // Lambdas in anonymous inner classes have to load outer scope variables from fields, 503 // which manifest as an ALOAD followed by one or more GETFIELDs 504 FieldInsnNode getfield = (FieldInsnNode) insn; 505 checkState( 506 getfield.desc.length() == 1 507 ? getfield.desc.equals(paramTypes[i].getDescriptor()) 508 : paramTypes[i].getDescriptor().length() > 1, 509 "Expected getfield for %s to set up parameter %s for %s but got %s : %s", 510 paramTypes[i], 511 i, 512 internalName, 513 getfield.name, 514 getfield.desc); 515 insn = insn.getPrevious(); 516 517 while (insn.getOpcode() == Opcodes.GETFIELD) { 518 // Nested inner classes can cause a cascade of getfields from the outermost one inwards 519 checkState( 520 ((FieldInsnNode) insn).desc.startsWith("L"), 521 "expect object type getfields to get to %s to set up parameter %s for %s, not: %s", 522 paramTypes[i], 523 i, 524 internalName, 525 ((FieldInsnNode) insn).desc); 526 insn = insn.getPrevious(); 527 } 528 529 checkState( 530 insn.getOpcode() == Opcodes.ALOAD, // should be a this pointer to be precise 531 "Expected aload before getfield for %s to set up parameter %s for %s but got %s", 532 getfield.name, 533 i, 534 internalName, 535 insn.getOpcode()); 536 } else if (!isPushForType(insn, paramTypes[i])) { 537 // Otherwise expect load of a (effectively) final local variable or a constant. Not seeing 538 // that means we're dealing with a method reference on some arbitrary expression, 539 // <expression>::m. In that case we give up and keep using the factory method for now, 540 // since inserting the NEW/DUP so the new object ends up in the right stack slot is hard 541 // in that case. Note this still covers simple cases such as this::m or x::m, where x is a 542 // local. 543 checkState( 544 paramTypes.length == 1, 545 "Expected a load for %s to set up parameter %s for %s but got %s", 546 paramTypes[i], 547 i, 548 internalName, 549 insn.getOpcode()); 550 return false; 551 } 552 insn = insn.getPrevious(); 553 } 554 555 TypeInsnNode newInsn = new TypeInsnNode(Opcodes.NEW, internalName); 556 if (insn == null) { 557 // Ran off the front of the instruction list 558 instructions.insert(newInsn); 559 } else { 560 instructions.insert(insn, newInsn); 561 } 562 instructions.insert(newInsn, new InsnNode(Opcodes.DUP)); 563 return true; 564 } 565 566 /** 567 * Returns whether a given instruction can be used to push argument of {@code type} on stack. 568 */ 569 private /* static */ boolean isPushForType(AbstractInsnNode insn, Type type) { 570 int opcode = insn.getOpcode(); 571 if (opcode == type.getOpcode(Opcodes.ILOAD)) { 572 return true; 573 } 574 // b/62060793: AsyncAwait rewrites bytecode to convert java methods into state machine with 575 // support of lambdas. Constant zero values are pushed on stack for all yet uninitialized 576 // local variables. And SIPUSH instruction is used to advance an internal state of a state 577 // machine. 578 switch (type.getSort()) { 579 case Type.BOOLEAN: 580 return opcode == Opcodes.ICONST_0 581 || opcode == Opcodes.ICONST_1; 582 583 case Type.BYTE: 584 case Type.CHAR: 585 case Type.SHORT: 586 case Type.INT: 587 return opcode == Opcodes.SIPUSH 588 || opcode == Opcodes.ICONST_0 589 || opcode == Opcodes.ICONST_1 590 || opcode == Opcodes.ICONST_2 591 || opcode == Opcodes.ICONST_3 592 || opcode == Opcodes.ICONST_4 593 || opcode == Opcodes.ICONST_5 594 || opcode == Opcodes.ICONST_M1; 595 596 case Type.LONG: 597 return opcode == Opcodes.LCONST_0 598 || opcode == Opcodes.LCONST_1; 599 600 case Type.FLOAT: 601 return opcode == Opcodes.FCONST_0 602 || opcode == Opcodes.FCONST_1 603 || opcode == Opcodes.FCONST_2; 604 605 case Type.DOUBLE: 606 return opcode == Opcodes.DCONST_0 607 || opcode == Opcodes.DCONST_1; 608 609 case Type.OBJECT: 610 case Type.ARRAY: 611 return opcode == Opcodes.ACONST_NULL; 612 613 default: 614 // Support for BIPUSH and LDC* opcodes is not implemented as there is no known use case. 615 return false; 616 } 617 } 618 619 private Lookup createLookup(String lookupClass) throws ReflectiveOperationException { 620 Class<?> clazz = loadFromInternal(lookupClass); 621 Constructor<Lookup> constructor = Lookup.class.getDeclaredConstructor(Class.class); 622 constructor.setAccessible(true); 623 return constructor.newInstance(clazz); 624 } 625 626 /** 627 * Produces a {@link MethodHandle} or {@link MethodType} using {@link #targetLoader} for the 628 * given ASM {@link Handle} or {@link Type}. {@code lookup} is only used for resolving {@link 629 * Handle}s. 630 */ 631 private Object toJvmMetatype(Lookup lookup, Object asm) throws ReflectiveOperationException { 632 if (asm instanceof Number) { 633 return asm; 634 } 635 if (asm instanceof Type) { 636 Type type = (Type) asm; 637 switch (type.getSort()) { 638 case Type.OBJECT: 639 return loadFromInternal(type.getInternalName()); 640 case Type.METHOD: 641 return MethodType.fromMethodDescriptorString(type.getDescriptor(), targetLoader); 642 default: 643 throw new IllegalArgumentException("Cannot convert: " + asm); 644 } 645 } 646 if (asm instanceof Handle) { 647 return toMethodHandle(lookup, (Handle) asm, /*target*/ true); 648 } 649 throw new IllegalArgumentException("Cannot convert: " + asm); 650 } 651 652 /** 653 * Produces a {@link MethodHandle} using either the context or {@link #targetLoader} class 654 * loader, depending on {@code target}. 655 */ 656 private MethodHandle toMethodHandle(Lookup lookup, Handle asmHandle, boolean target) 657 throws ReflectiveOperationException { 658 Class<?> owner = loadFromInternal(asmHandle.getOwner()); 659 MethodType signature = 660 MethodType.fromMethodDescriptorString( 661 asmHandle.getDesc(), 662 target ? targetLoader : Thread.currentThread().getContextClassLoader()); 663 switch (asmHandle.getTag()) { 664 case Opcodes.H_INVOKESTATIC: 665 return lookup.findStatic(owner, asmHandle.getName(), signature); 666 case Opcodes.H_INVOKEVIRTUAL: 667 case Opcodes.H_INVOKEINTERFACE: 668 return lookup.findVirtual(owner, asmHandle.getName(), signature); 669 case Opcodes.H_INVOKESPECIAL: // we end up calling these using invokevirtual 670 return lookup.findSpecial(owner, asmHandle.getName(), signature, owner); 671 case Opcodes.H_NEWINVOKESPECIAL: 672 return lookup.findConstructor(owner, signature); 673 default: 674 throw new UnsupportedOperationException("Cannot resolve " + asmHandle); 675 } 676 } 677 } 678 679 /** 680 * Record of how a lambda class can reach its referenced method through a possibly-different 681 * bridge method. 682 * 683 * <p>In a JVM, lambda classes are allowed to call the referenced methods directly, but we don't 684 * have that luxury when the generated lambda class is evaluated using normal visibility rules. 685 */ 686 @AutoValue 687 abstract static class MethodReferenceBridgeInfo { 688 public static MethodReferenceBridgeInfo noBridge(Handle methodReference) { 689 return new AutoValue_LambdaDesugaring_MethodReferenceBridgeInfo( 690 methodReference, (Executable) null, methodReference); 691 } 692 693 public static MethodReferenceBridgeInfo bridge( 694 Handle methodReference, Executable referenced, Handle bridgeMethod) { 695 checkArgument(!bridgeMethod.equals(methodReference)); 696 return new AutoValue_LambdaDesugaring_MethodReferenceBridgeInfo( 697 methodReference, checkNotNull(referenced), bridgeMethod); 698 } 699 700 public abstract Handle methodReference(); 701 702 /** Returns {@code null} iff {@link #bridgeMethod} equals {@link #methodReference}. */ 703 @Nullable 704 public abstract Executable referenced(); 705 706 public abstract Handle bridgeMethod(); 707 } 708} 709