• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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&lt;Integer, Character&gt;}), 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