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