| // Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| package com.android.tools.r8.ir.desugar; |
| |
| import com.android.tools.r8.dex.Constants; |
| import com.android.tools.r8.errors.Unimplemented; |
| import com.android.tools.r8.errors.Unreachable; |
| import com.android.tools.r8.graph.ClassAccessFlags; |
| import com.android.tools.r8.graph.DexAnnotationSet; |
| import com.android.tools.r8.graph.DexClass; |
| import com.android.tools.r8.graph.DexCode; |
| import com.android.tools.r8.graph.DexEncodedField; |
| import com.android.tools.r8.graph.DexEncodedMethod; |
| import com.android.tools.r8.graph.DexField; |
| import com.android.tools.r8.graph.DexItemFactory; |
| import com.android.tools.r8.graph.DexMethod; |
| import com.android.tools.r8.graph.DexMethodHandle; |
| import com.android.tools.r8.graph.DexProgramClass; |
| import com.android.tools.r8.graph.DexProto; |
| import com.android.tools.r8.graph.DexString; |
| import com.android.tools.r8.graph.DexType; |
| import com.android.tools.r8.graph.DexTypeList; |
| import com.android.tools.r8.graph.DexValue.DexValueNull; |
| import com.android.tools.r8.graph.FieldAccessFlags; |
| import com.android.tools.r8.graph.MethodAccessFlags; |
| import com.android.tools.r8.graph.ParameterAnnotationsList; |
| import com.android.tools.r8.ir.code.Invoke; |
| import com.android.tools.r8.ir.code.Invoke.Type; |
| import com.android.tools.r8.ir.synthetic.SynthesizedCode; |
| import com.android.tools.r8.origin.SynthesizedOrigin; |
| import com.google.common.base.Suppliers; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| import java.util.function.Supplier; |
| |
| /** |
| * Represents lambda class generated for a lambda descriptor in context |
| * of lambda instantiation point. |
| * |
| * Even though call sites, and thus lambda descriptors, are canonicalized |
| * across the application, the context may require several lambda classes |
| * to be generated for the same lambda descriptor. |
| * |
| * One reason is that we always generate a lambda class in the same package |
| * lambda instantiation point is located in, so if same call site is used in |
| * two classes from different packages (which can happen if same public method |
| * is being references via method reference expression) we generate separate |
| * lambda classes in those packages. |
| * |
| * Another reason is that if we generate an accessor, we generate it in the |
| * class referencing the call site, and thus two such classes will require two |
| * separate lambda classes. |
| */ |
| final class LambdaClass { |
| |
| final LambdaRewriter rewriter; |
| final DexType type; |
| final LambdaDescriptor descriptor; |
| final DexMethod constructor; |
| final DexMethod classConstructor; |
| final DexMethod createInstanceMethod; |
| final DexField lambdaField; |
| final Target target; |
| final AtomicBoolean addToMainDexList = new AtomicBoolean(false); |
| private final Collection<DexProgramClass> synthesizedFrom = new ArrayList<>(1); |
| private final Supplier<DexProgramClass> lazyDexClass = |
| Suppliers.memoize(this::synthesizeLambdaClass); // NOTE: thread-safe. |
| |
| LambdaClass( |
| LambdaRewriter rewriter, |
| DexType accessedFrom, |
| DexType lambdaClassType, |
| LambdaDescriptor descriptor) { |
| assert rewriter != null; |
| assert lambdaClassType != null; |
| assert descriptor != null; |
| |
| this.rewriter = rewriter; |
| this.type = lambdaClassType; |
| this.descriptor = descriptor; |
| |
| DexItemFactory factory = rewriter.factory; |
| DexProto constructorProto = factory.createProto( |
| factory.voidType, descriptor.captures.values); |
| this.constructor = factory.createMethod( |
| lambdaClassType, constructorProto, rewriter.constructorName); |
| |
| this.target = createTarget(accessedFrom); |
| |
| boolean stateless = isStateless(); |
| this.classConstructor = !stateless ? null |
| : factory.createMethod(lambdaClassType, constructorProto, rewriter.classConstructorName); |
| this.lambdaField = |
| !stateless |
| ? null |
| : factory.createField(lambdaClassType, lambdaClassType, rewriter.instanceFieldName); |
| this.createInstanceMethod = |
| stateless |
| ? null |
| : factory.createMethod( |
| lambdaClassType, |
| factory.createProto(lambdaClassType, descriptor.captures.values), |
| rewriter.createInstanceMethodName); |
| |
| // We have to register this new class as a subtype of object. |
| rewriter.converter.appView.appInfo().registerNewType(type, factory.objectType); |
| } |
| |
| // Generate unique lambda class type for lambda descriptor and instantiation point context. |
| static DexType createLambdaClassType( |
| LambdaRewriter rewriter, DexType accessedFrom, LambdaDescriptor match) { |
| StringBuilder lambdaClassDescriptor = new StringBuilder("L"); |
| |
| // We always create lambda class in the same package where it is referenced. |
| String packageDescriptor = accessedFrom.getPackageDescriptor(); |
| if (!packageDescriptor.isEmpty()) { |
| lambdaClassDescriptor.append(packageDescriptor).append('/'); |
| } |
| |
| // Lambda class name prefix |
| lambdaClassDescriptor.append(LambdaRewriter.LAMBDA_CLASS_NAME_PREFIX); |
| |
| // If the lambda class should match 1:1 the class it is accessed from, we |
| // just add the name of this type to make lambda class name unique. |
| // It also helps link the class lambda originated from in some cases. |
| if (match.delegatesToLambdaImplMethod() || match.needsAccessor(accessedFrom)) { |
| lambdaClassDescriptor.append(accessedFrom.getName()).append('$'); |
| } |
| |
| // Add unique lambda descriptor id |
| lambdaClassDescriptor.append(match.uniqueId).append(';'); |
| return rewriter.factory.createType(lambdaClassDescriptor.toString()); |
| } |
| |
| final DexProgramClass getOrCreateLambdaClass() { |
| return lazyDexClass.get(); |
| } |
| |
| DexMethod getCreateInstanceMethod() { |
| assert createInstanceMethod != null; |
| return createInstanceMethod; |
| } |
| |
| private DexProgramClass synthesizeLambdaClass() { |
| DexMethod mainMethod = |
| rewriter.factory.createMethod(type, descriptor.erasedProto, descriptor.name); |
| |
| DexProgramClass clazz = |
| new DexProgramClass( |
| type, |
| null, |
| new SynthesizedOrigin("lambda desugaring", getClass()), |
| // Make the synthesized class public, as it might end up being accessed from a different |
| // classloader (package private access is not allowed across classloaders, b/72538146). |
| ClassAccessFlags.fromDexAccessFlags( |
| Constants.ACC_FINAL | Constants.ACC_SYNTHETIC | Constants.ACC_PUBLIC), |
| rewriter.factory.objectType, |
| buildInterfaces(), |
| rewriter.factory.createString("lambda"), |
| null, |
| Collections.emptyList(), |
| null, |
| Collections.emptyList(), |
| DexAnnotationSet.empty(), |
| synthesizeStaticFields(), |
| synthesizeInstanceFields(), |
| synthesizeDirectMethods(), |
| synthesizeVirtualMethods(mainMethod), |
| rewriter.factory.getSkipNameValidationForTesting()); |
| // Optimize main method. |
| rewriter.converter.appView.appInfo().addSynthesizedClass(clazz); |
| rewriter.converter.optimizeSynthesizedMethod(clazz.lookupVirtualMethod(mainMethod)); |
| |
| // The method addSynthesizedFrom() may be called concurrently. To avoid a Concurrent- |
| // ModificationException we must use synchronization. |
| synchronized (synthesizedFrom) { |
| synthesizedFrom.forEach(clazz::addSynthesizedFrom); |
| } |
| return clazz; |
| } |
| |
| final DexField getCaptureField(int index) { |
| return rewriter.factory.createField(this.type, |
| descriptor.captures.values[index], rewriter.factory.createString("f$" + index)); |
| } |
| |
| final boolean isStateless() { |
| return descriptor.isStateless(); |
| } |
| |
| void addSynthesizedFrom(DexProgramClass clazz) { |
| assert clazz != null; |
| synchronized (synthesizedFrom) { |
| if (synthesizedFrom.add(clazz)) { |
| // The lambda class may already have been synthesized, and we therefore need to update the |
| // synthesized lambda class as well. |
| getOrCreateLambdaClass().addSynthesizedFrom(clazz); |
| } |
| } |
| } |
| |
| // Synthesize virtual methods. |
| private DexEncodedMethod[] synthesizeVirtualMethods(DexMethod mainMethod) { |
| DexEncodedMethod[] methods = new DexEncodedMethod[1 + descriptor.bridges.size()]; |
| int index = 0; |
| |
| // Synthesize main method. |
| methods[index++] = |
| new DexEncodedMethod( |
| mainMethod, |
| MethodAccessFlags.fromSharedAccessFlags( |
| Constants.ACC_PUBLIC | Constants.ACC_FINAL, false), |
| DexAnnotationSet.empty(), |
| ParameterAnnotationsList.empty(), |
| new SynthesizedCode( |
| callerPosition -> |
| new LambdaMainMethodSourceCode(this, mainMethod, callerPosition))); |
| |
| // Synthesize bridge methods. |
| for (DexProto bridgeProto : descriptor.bridges) { |
| DexMethod bridgeMethod = rewriter.factory.createMethod(type, bridgeProto, descriptor.name); |
| methods[index++] = |
| new DexEncodedMethod( |
| bridgeMethod, |
| MethodAccessFlags.fromSharedAccessFlags( |
| Constants.ACC_PUBLIC |
| | Constants.ACC_FINAL |
| | Constants.ACC_SYNTHETIC |
| | Constants.ACC_BRIDGE, |
| false), |
| DexAnnotationSet.empty(), |
| ParameterAnnotationsList.empty(), |
| new SynthesizedCode( |
| callerPosition -> |
| new LambdaBridgeMethodSourceCode( |
| this, mainMethod, bridgeMethod, callerPosition))); |
| } |
| return methods; |
| } |
| |
| // Synthesize direct methods. |
| private DexEncodedMethod[] synthesizeDirectMethods() { |
| boolean stateless = isStateless(); |
| boolean enableStatefulLambdaCreateInstanceMethod = |
| rewriter.converter.appView.options().testing.enableStatefulLambdaCreateInstanceMethod; |
| DexEncodedMethod[] methods = |
| new DexEncodedMethod[(stateless || enableStatefulLambdaCreateInstanceMethod) ? 2 : 1]; |
| |
| // Constructor. |
| methods[0] = |
| new DexEncodedMethod( |
| constructor, |
| MethodAccessFlags.fromSharedAccessFlags( |
| (stateless ? Constants.ACC_PRIVATE : Constants.ACC_PUBLIC) |
| | Constants.ACC_SYNTHETIC, |
| true), |
| DexAnnotationSet.empty(), |
| ParameterAnnotationsList.empty(), |
| new SynthesizedCode( |
| callerPosition -> new LambdaConstructorSourceCode(this, callerPosition))); |
| |
| // Class constructor for stateless lambda classes. |
| if (stateless) { |
| methods[1] = |
| new DexEncodedMethod( |
| classConstructor, |
| MethodAccessFlags.fromSharedAccessFlags( |
| Constants.ACC_SYNTHETIC | Constants.ACC_STATIC, true), |
| DexAnnotationSet.empty(), |
| ParameterAnnotationsList.empty(), |
| new SynthesizedCode( |
| callerPosition -> new LambdaClassConstructorSourceCode(this, callerPosition))); |
| } else if (enableStatefulLambdaCreateInstanceMethod) { |
| methods[1] = |
| new DexEncodedMethod( |
| createInstanceMethod, |
| MethodAccessFlags.fromSharedAccessFlags( |
| Constants.ACC_SYNTHETIC | Constants.ACC_STATIC | Constants.ACC_PUBLIC, false), |
| DexAnnotationSet.empty(), |
| ParameterAnnotationsList.empty(), |
| new SynthesizedCode( |
| callerPosition -> new LambdaCreateInstanceSourceCode(this, callerPosition))); |
| } |
| return methods; |
| } |
| |
| // Synthesize instance fields to represent captured values. |
| private DexEncodedField[] synthesizeInstanceFields() { |
| DexType[] fieldTypes = descriptor.captures.values; |
| int fieldCount = fieldTypes.length; |
| DexEncodedField[] fields = new DexEncodedField[fieldCount]; |
| for (int i = 0; i < fieldCount; i++) { |
| FieldAccessFlags accessFlags = |
| FieldAccessFlags.fromSharedAccessFlags( |
| Constants.ACC_FINAL | Constants.ACC_SYNTHETIC | Constants.ACC_PRIVATE); |
| fields[i] = new DexEncodedField( |
| getCaptureField(i), accessFlags, DexAnnotationSet.empty(), null); |
| } |
| return fields; |
| } |
| |
| // Synthesize static fields to represent singleton instance. |
| private DexEncodedField[] synthesizeStaticFields() { |
| if (!isStateless()) { |
| return DexEncodedField.EMPTY_ARRAY; |
| } |
| |
| // Create instance field for stateless lambda. |
| assert this.lambdaField != null; |
| DexEncodedField[] fields = new DexEncodedField[1]; |
| fields[0] = |
| new DexEncodedField( |
| this.lambdaField, |
| FieldAccessFlags.fromSharedAccessFlags( |
| Constants.ACC_PUBLIC |
| | Constants.ACC_FINAL |
| | Constants.ACC_SYNTHETIC |
| | Constants.ACC_STATIC), |
| DexAnnotationSet.empty(), |
| DexValueNull.NULL); |
| return fields; |
| } |
| |
| // Build a list of implemented interfaces. |
| private DexTypeList buildInterfaces() { |
| List<DexType> interfaces = descriptor.interfaces; |
| return interfaces.isEmpty() |
| ? DexTypeList.empty() |
| : new DexTypeList(interfaces.toArray(DexType.EMPTY_ARRAY)); |
| } |
| |
| // Creates a delegation target for this particular lambda class. Note that we |
| // should always be able to create targets for the lambdas we support. |
| private Target createTarget(DexType accessedFrom) { |
| if (descriptor.delegatesToLambdaImplMethod()) { |
| return createLambdaImplMethodTarget(accessedFrom); |
| } |
| |
| // Method referenced directly, without lambda$ method. |
| switch (descriptor.implHandle.type) { |
| case INVOKE_SUPER: |
| throw new Unimplemented("Method references to super methods are not yet supported"); |
| case INVOKE_INTERFACE: |
| return createInterfaceMethodTarget(accessedFrom); |
| case INVOKE_CONSTRUCTOR: |
| return createConstructorTarget(accessedFrom); |
| case INVOKE_STATIC: |
| return createStaticMethodTarget(accessedFrom); |
| case INVOKE_DIRECT: |
| case INVOKE_INSTANCE: |
| return createInstanceMethodTarget(accessedFrom); |
| default: |
| throw new Unreachable("Unexpected method handle type in " + descriptor.implHandle); |
| } |
| } |
| |
| private Target createLambdaImplMethodTarget(DexType accessedFrom) { |
| DexMethodHandle implHandle = descriptor.implHandle; |
| assert implHandle != null; |
| DexMethod implMethod = implHandle.asMethod(); |
| |
| // Lambda$ method. We must always find it. |
| assert implMethod.holder == accessedFrom; |
| assert descriptor.targetFoundInClass(accessedFrom); |
| assert descriptor.getAccessibility() != null; |
| // When coming from javac these are also private, but we don't assert that, as the |
| // accessibility could have been modified (e.g. due to -allowaccessmodification). |
| |
| assert descriptor.getAccessibility().isSynthetic(); |
| |
| if (implHandle.type.isInvokeStatic()) { |
| return new StaticLambdaImplTarget(); |
| } |
| |
| assert implHandle.type.isInvokeInstance() || implHandle.type.isInvokeDirect(); |
| |
| // If the lambda$ method is an instance-private method on an interface we convert it into a |
| // public static method as it will be placed on the companion class. |
| if (implHandle.type.isInvokeDirect() |
| && rewriter.converter.appView.definitionFor(implMethod.holder).isInterface()) { |
| DexProto implProto = implMethod.proto; |
| DexType[] implParams = implProto.parameters.values; |
| DexType[] newParams = new DexType[implParams.length + 1]; |
| newParams[0] = implMethod.holder; |
| System.arraycopy(implParams, 0, newParams, 1, implParams.length); |
| |
| DexProto newProto = rewriter.factory.createProto(implProto.returnType, newParams); |
| return new InterfaceLambdaImplTarget( |
| rewriter.factory.createMethod(implMethod.holder, newProto, implMethod.name)); |
| } else { |
| // Otherwise we need to ensure the method can be reached publicly by virtual dispatch. |
| // To avoid potential conflicts on the name of the lambda method once dispatch becomes virtual |
| // we add the method-holder name as suffix to the lambda-method name. |
| return new InstanceLambdaImplTarget( |
| rewriter.factory.createMethod(implMethod.holder, implMethod.proto, |
| rewriter.factory.createString( |
| implMethod.name.toString() + "$" + implMethod.holder.getName()))); |
| } |
| } |
| |
| // Create targets for instance method referenced directly without |
| // lambda$ methods. It may require creation of accessors in some cases. |
| private Target createInstanceMethodTarget(DexType accessedFrom) { |
| assert descriptor.implHandle.type.isInvokeInstance() || |
| descriptor.implHandle.type.isInvokeDirect(); |
| |
| if (!descriptor.needsAccessor(accessedFrom)) { |
| return new NoAccessorMethodTarget(Invoke.Type.VIRTUAL); |
| } |
| // We need to generate an accessor method in `accessedFrom` class/interface |
| // for accessing the original instance impl-method. Note that impl-method's |
| // holder does not have to be the same as `accessedFrom`. |
| DexMethod implMethod = descriptor.implHandle.asMethod(); |
| DexProto implProto = implMethod.proto; |
| DexType[] implParams = implProto.parameters.values; |
| |
| // The accessor method will be static, package private, and take the |
| // receiver as the first argument. The receiver must be captured and |
| // be the first captured value in case there are more than one. |
| DexType[] accessorParams = new DexType[1 + implParams.length]; |
| accessorParams[0] = descriptor.getImplReceiverType(); |
| System.arraycopy(implParams, 0, accessorParams, 1, implParams.length); |
| DexProto accessorProto = rewriter.factory.createProto(implProto.returnType, accessorParams); |
| DexMethod accessorMethod = rewriter.factory.createMethod( |
| accessedFrom, accessorProto, generateUniqueLambdaMethodName()); |
| |
| return new ClassMethodWithAccessorTarget(accessorMethod); |
| } |
| |
| // Create targets for static method referenced directly without |
| // lambda$ methods. It may require creation of accessors in some cases. |
| private Target createStaticMethodTarget(DexType accessedFrom) { |
| assert descriptor.implHandle.type.isInvokeStatic(); |
| |
| if (!descriptor.needsAccessor(accessedFrom)) { |
| return new NoAccessorMethodTarget(Invoke.Type.STATIC); |
| } |
| |
| // We need to generate an accessor method in `accessedFrom` class/interface |
| // for accessing the original static impl-method. The accessor method will be |
| // static, package private with exactly same signature and the original method. |
| DexMethod accessorMethod = rewriter.factory.createMethod(accessedFrom, |
| descriptor.implHandle.asMethod().proto, generateUniqueLambdaMethodName()); |
| return new ClassMethodWithAccessorTarget(accessorMethod); |
| } |
| |
| // Create targets for constructor referenced directly without lambda$ methods. |
| // It may require creation of accessors in some cases. |
| private Target createConstructorTarget(DexType accessedFrom) { |
| DexMethodHandle implHandle = descriptor.implHandle; |
| assert implHandle != null; |
| assert implHandle.type.isInvokeConstructor(); |
| |
| if (!descriptor.needsAccessor(accessedFrom)) { |
| return new NoAccessorMethodTarget(Invoke.Type.DIRECT); |
| } |
| |
| // We need to generate an accessor method in `accessedFrom` class/interface for |
| // instantiating the class and calling constructor on it. The accessor method will |
| // be static, package private with exactly same parameters as the constructor, |
| // and return the newly created instance. |
| DexMethod implMethod = implHandle.asMethod(); |
| DexType returnType = implMethod.holder; |
| DexProto accessorProto = rewriter.factory.createProto( |
| returnType, implMethod.proto.parameters.values); |
| DexMethod accessorMethod = rewriter.factory.createMethod(accessedFrom, |
| accessorProto, generateUniqueLambdaMethodName()); |
| return new ClassMethodWithAccessorTarget(accessorMethod); |
| } |
| |
| // Create targets for interface methods. |
| private Target createInterfaceMethodTarget(DexType accessedFrom) { |
| assert descriptor.implHandle.type.isInvokeInterface(); |
| assert !descriptor.needsAccessor(accessedFrom); |
| return new NoAccessorMethodTarget(Invoke.Type.INTERFACE); |
| } |
| |
| private DexString generateUniqueLambdaMethodName() { |
| return rewriter.factory.createString( |
| LambdaRewriter.EXPECTED_LAMBDA_METHOD_PREFIX + descriptor.uniqueId); |
| } |
| |
| // Represents information about the method lambda class need to delegate the call to. It may |
| // be the same method as specified in lambda descriptor or a newly synthesized accessor. |
| // Also provides action for ensuring accessibility of the referenced symbols. |
| abstract class Target { |
| |
| final DexMethod callTarget; |
| final Invoke.Type invokeType; |
| |
| Target(DexMethod callTarget, Invoke.Type invokeType) { |
| assert callTarget != null; |
| assert invokeType != null; |
| this.callTarget = callTarget; |
| this.invokeType = invokeType; |
| } |
| |
| // Ensure access of the referenced symbol(s). |
| abstract boolean ensureAccessibility(); |
| |
| DexClass definitionFor(DexType type) { |
| return rewriter.converter.appView.appInfo().app().definitionFor(type); |
| } |
| |
| DexProgramClass programDefinitionFor(DexType type) { |
| return rewriter.converter.appView.appInfo().app().programDefinitionFor(type); |
| } |
| } |
| |
| // Used for targeting methods referenced directly without creating accessors. |
| private final class NoAccessorMethodTarget extends Target { |
| |
| NoAccessorMethodTarget(Invoke.Type invokeType) { |
| super(descriptor.implHandle.asMethod(), invokeType); |
| } |
| |
| @Override |
| boolean ensureAccessibility() { |
| return true; |
| } |
| } |
| |
| // Used for static private lambda$ methods. Only needs access relaxation. |
| private final class StaticLambdaImplTarget extends Target { |
| |
| StaticLambdaImplTarget() { |
| super(descriptor.implHandle.asMethod(), Invoke.Type.STATIC); |
| } |
| |
| @Override |
| boolean ensureAccessibility() { |
| // We already found the static method to be called, just relax its accessibility. |
| assert descriptor.getAccessibility() != null; |
| descriptor.getAccessibility().unsetPrivate(); |
| DexClass implMethodHolder = definitionFor(descriptor.implHandle.asMethod().holder); |
| if (implMethodHolder.isInterface()) { |
| descriptor.getAccessibility().setPublic(); |
| } |
| return true; |
| } |
| } |
| |
| // Used for instance private lambda$ methods on interfaces which need to be converted to public |
| // static methods. They can't remain instance methods as they will end up on the companion class. |
| private class InterfaceLambdaImplTarget extends Target { |
| |
| InterfaceLambdaImplTarget(DexMethod staticMethod) { |
| super(staticMethod, Type.STATIC); |
| } |
| |
| @Override |
| boolean ensureAccessibility() { |
| // For all instantiation points for which the compiler creates lambda$ |
| // methods, it creates these methods in the same class/interface. |
| DexMethod implMethod = descriptor.implHandle.asMethod(); |
| DexClass implMethodHolder = definitionFor(implMethod.holder); |
| |
| List<DexEncodedMethod> directMethods = implMethodHolder.directMethods(); |
| for (int i = 0; i < directMethods.size(); i++) { |
| DexEncodedMethod encodedMethod = directMethods.get(i); |
| if (implMethod.match(encodedMethod)) { |
| // We need to create a new static method with the same code to be able to safely |
| // relax its accessibility without making it virtual. |
| MethodAccessFlags newAccessFlags = encodedMethod.accessFlags.copy(); |
| newAccessFlags.setStatic(); |
| newAccessFlags.unsetPrivate(); |
| // Always make the method public to provide access when r8 minification is allowed to move |
| // the lambda class accessing this method to another package (-allowaccessmodification). |
| newAccessFlags.setPublic(); |
| DexEncodedMethod newMethod = |
| new DexEncodedMethod( |
| callTarget, |
| newAccessFlags, |
| encodedMethod.annotations, |
| encodedMethod.parameterAnnotationsList, |
| encodedMethod.getCode()); |
| newMethod.copyMetadata(encodedMethod); |
| rewriter.methodMapping.put(encodedMethod.method, callTarget); |
| DexCode dexCode = newMethod.getCode().asDexCode(); |
| dexCode.setDebugInfo(dexCode.debugInfoWithFakeThisParameter(rewriter.factory)); |
| assert (dexCode.getDebugInfo() == null) |
| || (callTarget.getArity() == dexCode.getDebugInfo().parameters.length); |
| implMethodHolder.setDirectMethod(i, newMethod); |
| return true; |
| } |
| } |
| assert false |
| : "Unexpected failure to find direct lambda target for: " + implMethod.qualifiedName(); |
| return false; |
| } |
| } |
| |
| // Used for instance private lambda$ methods which need to be converted to public methods. |
| private class InstanceLambdaImplTarget extends Target { |
| |
| InstanceLambdaImplTarget(DexMethod staticMethod) { |
| super(staticMethod, Type.VIRTUAL); |
| } |
| |
| @Override |
| boolean ensureAccessibility() { |
| // For all instantiation points for which the compiler creates lambda$ |
| // methods, it creates these methods in the same class/interface. |
| DexMethod implMethod = descriptor.implHandle.asMethod(); |
| DexClass implMethodHolder = definitionFor(implMethod.holder); |
| |
| List<DexEncodedMethod> oldDirectMethods = implMethodHolder.directMethods(); |
| for (int i = 0; i < oldDirectMethods.size(); i++) { |
| DexEncodedMethod encodedMethod = oldDirectMethods.get(i); |
| if (implMethod.match(encodedMethod)) { |
| // We need to create a new method with the same code to be able to safely relax its |
| // accessibility and make it virtual. |
| MethodAccessFlags newAccessFlags = encodedMethod.accessFlags.copy(); |
| newAccessFlags.unsetPrivate(); |
| newAccessFlags.setPublic(); |
| DexEncodedMethod newMethod = |
| new DexEncodedMethod( |
| callTarget, |
| newAccessFlags, |
| encodedMethod.annotations, |
| encodedMethod.parameterAnnotationsList, |
| encodedMethod.getCode()); |
| newMethod.copyMetadata(encodedMethod); |
| rewriter.methodMapping.put(encodedMethod.method, callTarget); |
| // Move the method from the direct methods to the virtual methods set. |
| implMethodHolder.removeDirectMethod(i); |
| implMethodHolder.appendVirtualMethod(newMethod); |
| return true; |
| } |
| } |
| return false; |
| } |
| } |
| |
| // Used for instance/static methods or constructors accessed via |
| // synthesized accessor method. Needs accessor method to be created. |
| private class ClassMethodWithAccessorTarget extends Target { |
| |
| ClassMethodWithAccessorTarget(DexMethod accessorMethod) { |
| super(accessorMethod, Invoke.Type.STATIC); |
| } |
| |
| @Override |
| boolean ensureAccessibility() { |
| // Create a static accessor with proper accessibility. |
| DexProgramClass accessorClass = programDefinitionFor(callTarget.holder); |
| assert accessorClass != null; |
| |
| // Always make the method public to provide access when r8 minification is allowed to move |
| // the lambda class accessing this method to another package (-allowaccessmodification). |
| MethodAccessFlags accessorFlags = |
| MethodAccessFlags.fromSharedAccessFlags( |
| Constants.ACC_SYNTHETIC | Constants.ACC_STATIC | Constants.ACC_PUBLIC, |
| false); |
| DexEncodedMethod accessorEncodedMethod = |
| new DexEncodedMethod( |
| callTarget, |
| accessorFlags, |
| DexAnnotationSet.empty(), |
| ParameterAnnotationsList.empty(), |
| new SynthesizedCode( |
| callerPosition -> |
| new AccessorMethodSourceCode(LambdaClass.this, callerPosition))); |
| |
| // We may arrive here concurrently so we need must update the methods of the class atomically. |
| synchronized (accessorClass) { |
| accessorClass.appendDirectMethod(accessorEncodedMethod); |
| } |
| |
| rewriter.converter.optimizeSynthesizedMethod(accessorEncodedMethod); |
| return true; |
| } |
| } |
| } |