blob: cf9eca49a4d79ad89c5278582992f691f5ed777f [file] [log] [blame]
// 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.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.conversion.IRConverter;
import com.android.tools.r8.ir.synthetic.SynthesizedCode;
import com.android.tools.r8.origin.SynthesizedOrigin;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.base.Suppliers;
import com.google.common.primitives.Longs;
import java.nio.ByteBuffer;
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;
import java.util.zip.CRC32;
/**
* Represents lambda class generated for a lambda descriptor in context of lambda instantiation
* point.
*
* <p>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.
*
* <p>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.
*
* <p>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 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.getFactory();
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);
}
// Generate unique lambda class type for lambda descriptor and instantiation point context.
static DexType createLambdaClassType(
LambdaRewriter rewriter, DexType accessedFrom, LambdaDescriptor match) {
return createLambdaClassType(rewriter.getFactory(), accessedFrom, match);
}
public static DexType createLambdaClassType(
DexItemFactory factory, 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 factory.createType(lambdaClassDescriptor.toString());
}
final DexProgramClass getOrCreateLambdaClass() {
return lazyDexClass.get();
}
private DexProgramClass synthesizeLambdaClass() {
DexMethod mainMethod =
rewriter.getFactory().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.getFactory().objectType,
buildInterfaces(),
rewriter.getFactory().createString("lambda"),
null,
Collections.emptyList(),
null,
Collections.emptyList(),
DexAnnotationSet.empty(),
synthesizeStaticFields(),
synthesizeInstanceFields(),
synthesizeDirectMethods(),
synthesizeVirtualMethods(mainMethod),
rewriter.getFactory().getSkipNameValidationForTesting(),
LambdaClass::computeChecksumForSynthesizedClass);
rewriter.getAppInfo().addSynthesizedClass(clazz);
// The method addSynthesizedFrom() may be called concurrently. To avoid a Concurrent-
// ModificationException we must use synchronization.
synchronized (synthesizedFrom) {
synthesizedFrom.forEach(clazz::addSynthesizedFrom);
}
return clazz;
}
private static long computeChecksumForSynthesizedClass(DexProgramClass clazz) {
// Checksum of synthesized classes are compute based off the depending input. This might
// create false positives (ie: unchanged lambda class detected as changed even thought only
// an unrelated part from a synthesizedFrom class is changed).
// Ideally, we should use some hashcode of the dex program class that is deterministic across
// compiles.
Collection<DexProgramClass> synthesizedFrom = clazz.getSynthesizedFrom();
ByteBuffer buffer = ByteBuffer.allocate(synthesizedFrom.size() * Longs.BYTES);
for (DexProgramClass from : synthesizedFrom) {
buffer.putLong(from.getChecksum());
}
CRC32 crc = new CRC32();
byte[] array = buffer.array();
crc.update(array, 0, array.length);
return crc.getValue();
}
final DexField getCaptureField(int index) {
return rewriter
.getFactory()
.createField(
this.type,
descriptor.captures.values[index],
rewriter.getFactory().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 LambdaMainMethodSynthesizedCode(this, mainMethod));
// Synthesize bridge methods.
for (DexProto bridgeProto : descriptor.bridges) {
DexMethod bridgeMethod =
rewriter.getFactory().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 LambdaBridgeMethodSynthesizedCode(this, mainMethod, bridgeMethod));
}
return methods;
}
// Synthesize direct methods.
private DexEncodedMethod[] synthesizeDirectMethods() {
boolean stateless = isStateless();
DexEncodedMethod[] methods = new DexEncodedMethod[stateless ? 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 LambdaConstructorSynthesizedCode(this));
// 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 LambdaClassConstructorSynthesizedCode(this));
}
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_PUBLIC);
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.getAppView().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.getFactory().createProto(implProto.returnType, newParams);
return new InterfaceLambdaImplTarget(
rewriter.getFactory().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
.getFactory()
.createMethod(
implMethod.holder,
implMethod.proto,
rewriter
.getFactory()
.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.getFactory().createProto(implProto.returnType, accessorParams);
DexMethod accessorMethod =
rewriter
.getFactory()
.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
.getFactory()
.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.getFactory().createProto(returnType, implMethod.proto.parameters.values);
DexMethod accessorMethod =
rewriter
.getFactory()
.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
.getFactory()
.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 void ensureAccessibility(IRConverter converter);
DexClass definitionFor(DexType type) {
return rewriter.getAppInfo().app().definitionFor(type);
}
DexProgramClass programDefinitionFor(DexType type) {
return rewriter.getAppInfo().app().programDefinitionFor(type);
}
boolean holderIsInterface() {
InternalOptions options = rewriter.getAppView().options();
if (!options.isGeneratingClassFiles()) {
// When generating dex the value of this flag on invokes does not matter (unused).
// We cannot know if definitionFor(implMethod.holder) is null or not in that case,
// so we cannot set the flag and just return false.
return false;
}
// The only case where we do Lambda desugaring with Cf to Cf is in L8.
// If the compilation is not coreLibraryCompilation, then the assertion
// implMethodHolder != null may fail, hence the assertion.
assert options.isDesugaredLibraryCompilation() || options.enableCfInterfaceMethodDesugaring;
DexMethod implMethod = descriptor.implHandle.asMethod();
DexClass implMethodHolder = definitionFor(implMethod.holder);
if (implMethodHolder == null) {
assert options
.desugaredLibraryConfiguration
.getBackportCoreLibraryMember()
.containsKey(implMethod.holder);
return false;
}
return implMethodHolder.isInterface();
}
}
// 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
void ensureAccessibility(IRConverter converter) {}
}
// 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
void ensureAccessibility(IRConverter converter) {
// 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();
}
}
}
// 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
void ensureAccessibility(IRConverter converter) {
// 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);
DexEncodedMethod.setDebugInfoWithFakeThisParameter(
newMethod.getCode(), callTarget.getArity(), rewriter.getAppView());
implMethodHolder.setDirectMethod(i, newMethod);
return;
}
}
assert false
: "Unexpected failure to find direct lambda target for: " + implMethod.qualifiedName();
}
}
// 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
void ensureAccessibility(IRConverter converter) {
// 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;
}
}
}
}
// 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
void ensureAccessibility(IRConverter converter) {
// 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);
}
converter.optimizeSynthesizedMethod(accessorEncodedMethod);
}
}
}