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&lt;Integer, Character&gt;}), 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