Build all synthetic lambda code in CF.
Bug: 169305577
Change-Id: I48c7753f90e9a2ba5e10121cb4b862b02a073aff
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index 50083f2..141484c 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -412,7 +412,9 @@
Origin origin,
boolean shouldApplyCodeRewritings) {
if (!verifyFrames(method, appView, origin, shouldApplyCodeRewritings)) {
- instructions.removeIf(CfInstruction::isFrame);
+ ArrayList<CfInstruction> copy = new ArrayList<>(instructions);
+ copy.removeIf(CfInstruction::isFrame);
+ setInstructions(copy);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
index b283f96..d7b3d0a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
@@ -1,129 +1,47 @@
-// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// Copyright (c) 2020, 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.errors.Unreachable;
+import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexMethodHandle;
-import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.code.Invoke;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.ValueType;
-import com.android.tools.r8.ir.conversion.IRBuilder;
-import java.util.ArrayList;
-import java.util.List;
+import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
// Source code representing synthesized accessor method.
-final class AccessorMethodSourceCode extends SynthesizedLambdaSourceCode {
- AccessorMethodSourceCode(LambdaClass lambda, Position callerPosition) {
- super(
- lambda, lambda.target.callTarget, callerPosition, null /* no receiver for static method */);
- // We should never need an accessor for interface methods since
- // they are supposed to be public.
- assert !descriptor().implHandle.type.isInvokeInterface();
- assert checkSignatures();
- }
+public class AccessorMethodSourceCode {
- private boolean checkSignatures() {
- DexMethodHandle implHandle = descriptor().implHandle;
- assert implHandle != null;
-
- DexType[] accessorParams = proto.parameters.values;
- DexMethod implMethod = implHandle.asMethod();
- DexProto implProto = implMethod.proto;
- DexType[] implParams = implProto.parameters.values;
-
- int index = 0;
- if (implHandle.type.isInvokeInstance() || implHandle.type.isInvokeDirect()) {
- assert accessorParams[index] == descriptor().getImplReceiverType();
- index++;
- }
-
- for (DexType implParam : implParams) {
- assert accessorParams[index] == implParam;
- index++;
- }
- assert index == accessorParams.length;
-
- assert delegatingToConstructor()
- ? this.proto.returnType == implMethod.holder
- : this.proto.returnType == implProto.returnType;
- return true;
- }
-
- // Are we delegating to a constructor?
- private boolean delegatingToConstructor() {
- return descriptor().implHandle.type.isInvokeConstructor();
- }
-
- private Invoke.Type inferInvokeType() {
- switch (descriptor().implHandle.type) {
+ public static CfCode build(LambdaClass lambda, DexMethod accessor) {
+ DexMethod target = lambda.descriptor.implHandle.asMethod();
+ ForwardMethodBuilder forwardMethodBuilder =
+ ForwardMethodBuilder.builder(lambda.appView.dexItemFactory()).setStaticSource(accessor);
+ switch (lambda.descriptor.implHandle.type) {
case INVOKE_INSTANCE:
- return Invoke.Type.VIRTUAL;
+ {
+ forwardMethodBuilder.setVirtualTarget(target, false);
+ break;
+ }
case INVOKE_STATIC:
- return Invoke.Type.STATIC;
+ {
+ forwardMethodBuilder.setStaticTarget(target, false);
+ break;
+ }
case INVOKE_DIRECT:
+ {
+ forwardMethodBuilder.setDirectTarget(target, false);
+ break;
+ }
case INVOKE_CONSTRUCTOR:
- return Invoke.Type.DIRECT;
+ {
+ forwardMethodBuilder.setConstructorTarget(target, lambda.appView.dexItemFactory());
+ break;
+ }
case INVOKE_INTERFACE:
throw new Unreachable("Accessor for an interface method?");
default:
throw new Unreachable();
}
- }
-
- @Override
- protected void prepareInstructions() {
- DexMethod implMethod = descriptor().implHandle.asMethod();
- DexType[] accessorParams = proto.parameters.values;
-
- // Prepare call arguments.
- List<ValueType> argValueTypes = new ArrayList<>();
- List<Integer> argRegisters = new ArrayList<>();
-
- // If we are delegating to a constructor, we need to create the instance
- // first. This instance will be the first argument to the call.
- if (delegatingToConstructor()) {
- int instance = nextRegister(ValueType.OBJECT);
- add(builder -> builder.addNewInstance(instance, implMethod.holder));
- argValueTypes.add(ValueType.OBJECT);
- argRegisters.add(instance);
- }
-
- for (int i = 0; i < accessorParams.length; i++) {
- DexType param = accessorParams[i];
- argValueTypes.add(ValueType.fromDexType(param));
- argRegisters.add(getParamRegister(i));
- }
-
- // Method call to the original impl-method.
- // Mirroring assert in constructor, we never need accessors to interfaces.
- assert !descriptor().implHandle.type.isInvokeInterface();
- add(
- builder ->
- builder.addInvoke(
- inferInvokeType(),
- implMethod,
- implMethod.proto,
- argValueTypes,
- argRegisters,
- false /* isInterface */));
-
- // Does the method have return value?
- if (proto.returnType == factory().voidType) {
- add(IRBuilder::addReturn);
- } else if (delegatingToConstructor()) {
- // Return newly created instance
- add(builder -> builder.addReturn(argRegisters.get(0)));
- } else {
- ValueType valueType = ValueType.fromDexType(proto.returnType);
- int tempValue = nextRegister(valueType);
- add(builder -> builder.addMoveResult(tempValue));
- add(builder -> builder.addReturn(tempValue));
- }
+ return forwardMethodBuilder.build();
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 6dc372e..99bad27 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexItemFactory;
@@ -1397,7 +1398,7 @@
private interface TemplateMethodFactory {
- Code create(InternalOptions options, DexMethod method);
+ CfCode create(InternalOptions options, DexMethod method);
}
private interface MethodInvokeRewriter {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
index 4d5c7be..01fb238 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
@@ -22,10 +22,8 @@
import com.android.tools.r8.graph.DexValue.DexValueType;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.ir.conversion.IRConverter;
-import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
-import com.android.tools.r8.ir.synthetic.SynthesizedCode;
+import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
import com.google.common.base.Predicates;
import java.util.HashSet;
import java.util.LinkedList;
@@ -111,12 +109,13 @@
DexProgramClass clazz,
List<DexEncodedMethod> methodsWithCovariantReturnTypeAnnotation,
List<DexEncodedMethod> covariantReturnTypeMethods) {
- for (DexEncodedMethod method : clazz.virtualMethods()) {
- if (methodHasCovariantReturnTypeAnnotation(method)) {
- methodsWithCovariantReturnTypeAnnotation.add(method);
- buildCovariantReturnTypeMethodsForMethod(clazz, method, covariantReturnTypeMethods);
- }
- }
+ clazz.forEachProgramVirtualMethod(
+ method -> {
+ if (methodHasCovariantReturnTypeAnnotation(method.getDefinition())) {
+ methodsWithCovariantReturnTypeAnnotation.add(method.getDefinition());
+ buildCovariantReturnTypeMethodsForMethod(method, covariantReturnTypeMethods);
+ }
+ });
}
private boolean methodHasCovariantReturnTypeAnnotation(DexEncodedMethod method) {
@@ -132,13 +131,11 @@
// variantReturnTypes annotations on the given method. Adds the newly constructed, synthetic
// methods to the list covariantReturnTypeMethods.
private void buildCovariantReturnTypeMethodsForMethod(
- DexProgramClass clazz,
- DexEncodedMethod method,
- List<DexEncodedMethod> covariantReturnTypeMethods) {
- assert methodHasCovariantReturnTypeAnnotation(method);
- for (DexType covariantReturnType : getCovariantReturnTypes(clazz, method)) {
+ ProgramMethod method, List<DexEncodedMethod> covariantReturnTypeMethods) {
+ assert methodHasCovariantReturnTypeAnnotation(method.getDefinition());
+ for (DexType covariantReturnType : getCovariantReturnTypes(method)) {
DexEncodedMethod covariantReturnTypeMethod =
- buildCovariantReturnTypeMethod(clazz, method, covariantReturnType);
+ buildCovariantReturnTypeMethod(method, covariantReturnType);
covariantReturnTypeMethods.add(covariantReturnTypeMethod);
}
}
@@ -149,32 +146,35 @@
//
// Note: any "synchronized" or "strictfp" modifier could be dropped safely.
private DexEncodedMethod buildCovariantReturnTypeMethod(
- DexProgramClass clazz, DexEncodedMethod method, DexType covariantReturnType) {
+ ProgramMethod method, DexType covariantReturnType) {
+ DexProgramClass methodHolder = method.getHolder();
+ DexMethod methodReference = method.getReference();
+ DexEncodedMethod methodDefinition = method.getDefinition();
DexProto newProto =
factory.createProto(
- covariantReturnType, method.method.proto.parameters, method.method.proto.shorty);
- MethodAccessFlags newAccessFlags = method.accessFlags.copy();
+ covariantReturnType, methodReference.proto.parameters, methodReference.proto.shorty);
+ MethodAccessFlags newAccessFlags = methodDefinition.accessFlags.copy();
newAccessFlags.setBridge();
newAccessFlags.setSynthetic();
- DexMethod newMethod = factory.createMethod(method.holder(), newProto, method.method.name);
- ForwardMethodSourceCode.Builder forwardSourceCodeBuilder =
- ForwardMethodSourceCode.builder(newMethod);
- forwardSourceCodeBuilder
- .setReceiver(clazz.type)
- .setTargetReceiver(method.holder())
- .setTarget(method.method)
- .setInvokeType(Invoke.Type.VIRTUAL)
- .setCastResult();
+ DexMethod newMethod =
+ factory.createMethod(methodHolder.getType(), newProto, methodReference.getName());
+ ForwardMethodBuilder forwardMethodBuilder =
+ ForwardMethodBuilder.builder(factory)
+ .setNonStaticSource(newMethod)
+ .setVirtualTarget(methodReference, methodHolder.isInterface())
+ .setCastResult();
DexEncodedMethod newVirtualMethod =
new DexEncodedMethod(
newMethod,
newAccessFlags,
- method.annotations().keepIf(x -> !isCovariantReturnTypeAnnotation(x.annotation)),
- method.parameterAnnotationsList.keepIf(Predicates.alwaysTrue()),
- new SynthesizedCode(forwardSourceCodeBuilder::build),
+ methodDefinition
+ .annotations()
+ .keepIf(x -> !isCovariantReturnTypeAnnotation(x.annotation)),
+ methodDefinition.parameterAnnotationsList.keepIf(Predicates.alwaysTrue()),
+ forwardMethodBuilder.build(),
true);
- // Optimize to generate DexCode instead of SynthesizedCode.
- ProgramMethod programMethod = new ProgramMethod(clazz, newVirtualMethod);
+ // Optimize to generate DexCode instead of CfCode.
+ ProgramMethod programMethod = new ProgramMethod(methodHolder, newVirtualMethod);
converter.optimizeSynthesizedMethod(programMethod);
return newVirtualMethod;
}
@@ -187,12 +187,15 @@
// @Override
// public Foo foo() { ... return new SubOfSubOfFoo(); }
// then this method returns the set { SubOfFoo, SubOfSubOfFoo }.
- private Set<DexType> getCovariantReturnTypes(DexClass clazz, DexEncodedMethod method) {
+ private Set<DexType> getCovariantReturnTypes(ProgramMethod method) {
Set<DexType> covariantReturnTypes = new HashSet<>();
- for (DexAnnotation annotation : method.annotations().annotations) {
+ for (DexAnnotation annotation : method.getDefinition().annotations().annotations) {
if (isCovariantReturnTypeAnnotation(annotation.annotation)) {
getCovariantReturnTypesFromAnnotation(
- clazz, method, annotation.annotation, covariantReturnTypes);
+ method.getHolder(),
+ method.getDefinition(),
+ annotation.annotation,
+ covariantReturnTypes);
}
}
return covariantReturnTypes;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index 67d389f..069f4b3 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -32,8 +32,7 @@
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.ir.code.Invoke.Type;
-import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
-import com.android.tools.r8.ir.synthetic.SynthesizedCode;
+import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
import com.android.tools.r8.origin.SynthesizedOrigin;
import com.android.tools.r8.utils.Pair;
import com.google.common.collect.BiMap;
@@ -260,15 +259,13 @@
DexMethod origMethod = direct.method;
DexMethod newMethod = rewriter.staticAsMethodOfDispatchClass(origMethod);
- ForwardMethodSourceCode.Builder forwardSourceCodeBuilder =
- ForwardMethodSourceCode.builder(newMethod);
// Create a forwarding method to the library static interface method. The method is added
// to the dispatch class, however, the targeted method is still on the interface, so the
// interface bit should be set to true.
- forwardSourceCodeBuilder
- .setTarget(origMethod)
- .setInvokeType(Type.STATIC)
- .setIsInterface(true);
+ ForwardMethodBuilder forwardMethodBuilder =
+ ForwardMethodBuilder.builder(appView.dexItemFactory())
+ .setStaticSource(newMethod)
+ .setStaticTarget(origMethod, true);
DexEncodedMethod newEncodedMethod =
new DexEncodedMethod(
newMethod,
@@ -276,7 +273,7 @@
Constants.ACC_PUBLIC | Constants.ACC_STATIC | Constants.ACC_SYNTHETIC, false),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
- new SynthesizedCode(forwardSourceCodeBuilder::build),
+ forwardMethodBuilder.build(),
true);
newEncodedMethod.getMutableOptimizationInfo().markNeverInline();
dispatchMethods.add(newEncodedMethod);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaAccessorMethodWithSynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaAccessorMethodWithSynthesizedCode.java
deleted file mode 100644
index a733540..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaAccessorMethodWithSynthesizedCode.java
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (c) 2020, 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.errors.Unreachable;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexMethodHandle;
-import com.android.tools.r8.graph.UseRegistry;
-import java.util.function.Consumer;
-
-public class LambdaAccessorMethodWithSynthesizedCode extends LambdaSynthesizedCode {
-
- public LambdaAccessorMethodWithSynthesizedCode(LambdaClass lambda) {
- super(lambda);
- }
-
- @Override
- public SourceCodeProvider getSourceCodeProvider() {
- return callerPosition -> new AccessorMethodSourceCode(lambda, callerPosition);
- }
-
- @Override
- public Consumer<UseRegistry> getRegistryCallback() {
- return registry -> {
- DexMethodHandle handle = lambda.descriptor.implHandle;
- DexMethod target = handle.asMethod();
- switch (handle.type) {
- case INVOKE_STATIC:
- registry.registerInvokeStatic(target);
- break;
- case INVOKE_INSTANCE:
- registry.registerInvokeVirtual(target);
- break;
- case INVOKE_CONSTRUCTOR:
- case INVOKE_DIRECT:
- registry.registerInvokeDirect(target);
- break;
- default:
- throw new Unreachable("Unexpected handle type: " + handle.type);
- }
- };
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaBridgeMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaBridgeMethodSourceCode.java
index f0eb914..ddcdc6e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaBridgeMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaBridgeMethodSourceCode.java
@@ -4,71 +4,21 @@
package com.android.tools.r8.ir.desugar;
+import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.code.Invoke;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.ValueType;
-import com.android.tools.r8.ir.conversion.IRBuilder;
-import java.util.ArrayList;
-import java.util.List;
+import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
// Source code representing synthesized lambda bridge method.
-final class LambdaBridgeMethodSourceCode extends SynthesizedLambdaSourceCode {
- private final DexMethod mainMethod;
+final class LambdaBridgeMethodSourceCode {
- LambdaBridgeMethodSourceCode(
- LambdaClass lambda, DexMethod mainMethod, DexMethod bridgeMethod, Position callerPosition) {
- super(lambda, bridgeMethod, callerPosition);
- this.mainMethod = mainMethod;
- }
-
- @Override
- protected void prepareInstructions() {
- DexType[] currentParams = proto.parameters.values;
- DexType[] enforcedParams = descriptor().enforcedProto.parameters.values;
-
- // Prepare call arguments.
- List<ValueType> argValueTypes = new ArrayList<>();
- List<Integer> argRegisters = new ArrayList<>();
-
- // Always add a receiver representing 'this' of the lambda class.
- argValueTypes.add(ValueType.OBJECT);
- argRegisters.add(getReceiverRegister());
-
- // Prepare arguments.
- for (int i = 0; i < currentParams.length; i++) {
- DexType expectedParamType = enforcedParams[i];
- argValueTypes.add(ValueType.fromDexType(expectedParamType));
- argRegisters.add(enforceParameterType(
- getParamRegister(i), currentParams[i], expectedParamType));
- }
-
- // Method call to the main functional interface method.
- add(
- builder ->
- builder.addInvoke(
- Invoke.Type.VIRTUAL,
- this.mainMethod,
- this.mainMethod.proto,
- argValueTypes,
- argRegisters,
- false /* isInterface */));
-
- // Does the method have return value?
- if (proto.returnType == factory().voidType) {
- add(IRBuilder::addReturn);
- } else {
- ValueType valueType = ValueType.fromDexType(proto.returnType);
- int tempValue = nextRegister(valueType);
- add(builder -> builder.addMoveResult(tempValue));
- // We lack precise sub-type information, but there should not be a need to cast to object.
- if (proto.returnType != mainMethod.proto.returnType
- && proto.returnType != factory().objectType) {
- add(builder -> builder.addCheckCast(tempValue, proto.returnType));
- }
- add(builder -> builder.addReturn(tempValue));
- }
+ public static CfCode build(
+ LambdaClass lambdaClass, DexMethod bridgeMethod, DexMethod mainMethod) {
+ return ForwardMethodBuilder.builder(lambdaClass.appView.dexItemFactory())
+ .setNonStaticSource(bridgeMethod)
+ .setVirtualTarget(mainMethod, false)
+ .setCastArguments(lambdaClass.appView.appInfoForDesugaring())
+ .setCastResult()
+ .build();
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaBridgeMethodSynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaBridgeMethodSynthesizedCode.java
deleted file mode 100644
index 6e109dd..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaBridgeMethodSynthesizedCode.java
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (c) 2019, 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.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.UseRegistry;
-import java.util.function.Consumer;
-
-class LambdaBridgeMethodSynthesizedCode extends LambdaSynthesizedCode {
-
- private final DexMethod mainMethod;
- private final DexMethod bridgeMethod;
-
- LambdaBridgeMethodSynthesizedCode(
- LambdaClass lambda, DexMethod mainMethod, DexMethod bridgeMethod) {
- super(lambda);
- this.mainMethod = mainMethod;
- this.bridgeMethod = bridgeMethod;
- }
-
- @Override
- public SourceCodeProvider getSourceCodeProvider() {
- return callerPosition ->
- new LambdaBridgeMethodSourceCode(lambda, mainMethod, bridgeMethod, callerPosition);
- }
-
- @Override
- public Consumer<UseRegistry> getRegistryCallback() {
- return registry -> {
- registry.registerInvokeVirtual(mainMethod);
-
- DexType bridgeMethodReturnType = bridgeMethod.proto.returnType;
- if (!bridgeMethodReturnType.isVoidType()
- && bridgeMethodReturnType != mainMethod.proto.returnType
- && bridgeMethodReturnType != dexItemFactory().objectType) {
- registry.registerCheckCast(bridgeMethodReturnType);
- }
- };
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index cc6fec7..73b78aa2 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -234,7 +234,7 @@
Constants.ACC_PUBLIC | Constants.ACC_FINAL, false),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
- new LambdaMainMethodSynthesizedCode(this, mainMethod),
+ LambdaMainMethodSourceCode.build(this, mainMethod),
true);
// Synthesize bridge methods.
@@ -252,7 +252,7 @@
false),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
- new LambdaBridgeMethodSynthesizedCode(this, mainMethod, bridgeMethod),
+ LambdaBridgeMethodSourceCode.build(this, bridgeMethod, mainMethod),
true);
}
return methods;
@@ -273,7 +273,7 @@
true),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
- new LambdaConstructorSynthesizedCode(this),
+ LambdaConstructorSourceCode.build(this),
true);
// Class constructor for stateless lambda classes.
@@ -285,7 +285,7 @@
Constants.ACC_SYNTHETIC | Constants.ACC_STATIC, true),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
- new LambdaClassConstructorSynthesizedCode(this),
+ LambdaClassConstructorSourceCode.build(this),
true);
}
return methods;
@@ -748,13 +748,14 @@
MethodAccessFlags.fromSharedAccessFlags(
Constants.ACC_SYNTHETIC | Constants.ACC_STATIC | Constants.ACC_PUBLIC,
false);
+
DexEncodedMethod accessorEncodedMethod =
new DexEncodedMethod(
callTarget,
accessorFlags,
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
- new LambdaAccessorMethodWithSynthesizedCode(LambdaClass.this),
+ AccessorMethodSourceCode.build(LambdaClass.this, callTarget),
true);
// We may arrive here concurrently so we need must update the methods of the class atomically.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java
index 6b85039..fd7a8b9 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java
@@ -4,40 +4,34 @@
package com.android.tools.r8.ir.desugar;
-import com.android.tools.r8.ir.code.Invoke;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.ValueType;
-import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.cf.code.CfFieldInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfNew;
+import com.android.tools.r8.cf.code.CfReturnVoid;
+import com.android.tools.r8.cf.code.CfStackInstruction;
+import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
+import com.android.tools.r8.graph.CfCode;
import com.google.common.collect.ImmutableList;
+import org.objectweb.asm.Opcodes;
// Source code representing synthesized lambda class constructor.
// Used for stateless lambdas to instantiate singleton instance.
-final class LambdaClassConstructorSourceCode extends SynthesizedLambdaSourceCode {
+final class LambdaClassConstructorSourceCode {
- LambdaClassConstructorSourceCode(LambdaClass lambda, Position callerPosition) {
- super(lambda, lambda.classConstructor, callerPosition, null /* Class initializer is static */);
- assert lambda.lambdaField != null;
- }
-
- @Override
- protected void prepareInstructions() {
- // Create and initialize an instance.
- int instance = nextRegister(ValueType.OBJECT);
- add(builder -> builder.addNewInstance(instance, lambda.type));
- add(
- builder ->
- builder.addInvoke(
- Invoke.Type.DIRECT,
- lambda.constructor,
- lambda.constructor.proto,
- ImmutableList.of(ValueType.OBJECT),
- ImmutableList.of(instance),
- false /* isInterface */));
-
- // Assign to a field.
- add(builder -> builder.addStaticPut(instance, lambda.lambdaField));
-
- // Final return.
- add(IRBuilder::addReturn);
+ public static CfCode build(LambdaClass lambda) {
+ int maxStack = 2;
+ int maxLocals = 0;
+ return new CfCode(
+ lambda.type,
+ maxStack,
+ maxLocals,
+ ImmutableList.of(
+ new CfNew(lambda.type),
+ new CfStackInstruction(Opcode.Dup),
+ new CfInvoke(Opcodes.INVOKESPECIAL, lambda.constructor, false),
+ new CfFieldInstruction(Opcodes.PUTSTATIC, lambda.lambdaField, lambda.lambdaField),
+ new CfReturnVoid()),
+ ImmutableList.of(),
+ ImmutableList.of());
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSynthesizedCode.java
deleted file mode 100644
index 2dbac91..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSynthesizedCode.java
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) 2019, 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.graph.UseRegistry;
-import java.util.function.Consumer;
-
-class LambdaClassConstructorSynthesizedCode extends LambdaSynthesizedCode {
-
- LambdaClassConstructorSynthesizedCode(LambdaClass lambda) {
- super(lambda);
- }
-
- @Override
- public SourceCodeProvider getSourceCodeProvider() {
- return callerPosition -> new LambdaClassConstructorSourceCode(lambda, callerPosition);
- }
-
- @Override
- public Consumer<UseRegistry> getRegistryCallback() {
- return registry -> {
- registry.registerNewInstance(lambda.type);
- registry.registerInvokeDirect(lambda.constructor);
- registry.registerStaticFieldWrite(lambda.lambdaField);
- };
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSourceCode.java
index a8161d4..65ead77 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSourceCode.java
@@ -4,70 +4,56 @@
package com.android.tools.r8.ir.desugar;
+import com.android.tools.r8.cf.code.CfFieldInstruction;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfLoad;
+import com.android.tools.r8.cf.code.CfReturnVoid;
+import com.android.tools.r8.cf.code.CfTryCatch;
+import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.code.Invoke;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.conversion.IRBuilder;
-import java.util.Collections;
-
// Source code representing synthesized lambda constructor.
-final class LambdaConstructorSourceCode extends SynthesizedLambdaSourceCode {
+import com.android.tools.r8.ir.code.ValueType;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import org.objectweb.asm.Opcodes;
- LambdaConstructorSourceCode(LambdaClass lambda, Position callerPosition) {
- super(lambda, lambda.constructor, callerPosition);
- }
+final class LambdaConstructorSourceCode {
- @Override
- protected void prepareInstructions() {
+ public static CfCode build(LambdaClass lambda) {
+ int maxStack = 1;
+ ImmutableList<CfTryCatch> tryCatchRanges = ImmutableList.of();
+ ImmutableList<CfCode.LocalVariableInfo> localVariables = ImmutableList.of();
+ Builder<CfInstruction> instructions = ImmutableList.builder();
// Super constructor call (always java.lang.Object.<init>()).
- DexMethod objectInitMethod = factory().objectMembers.constructor;
- add(
- builder -> {
- assert builder.getReceiverValue() != null;
- builder.addInvoke(
- Invoke.Type.DIRECT,
- objectInitMethod,
- objectInitMethod.proto,
- Collections.singletonList(builder.getReceiverValue()),
- false /* isInterface */);
- });
-
+ instructions.add(new CfLoad(ValueType.OBJECT, 0));
+ instructions.add(
+ new CfInvoke(
+ Opcodes.INVOKESPECIAL,
+ lambda.appView.dexItemFactory().objectMembers.constructor,
+ false));
// Assign capture fields.
- DexType[] capturedTypes = captures();
- int capturedValues = capturedTypes.length;
- if (capturedValues > 0) {
- for (int i = 0; i < capturedValues; i++) {
- DexField field = lambda.getCaptureField(i);
- int idx = i;
- add(builder -> builder.addInstancePut(getParamRegister(idx), getReceiverRegister(), field));
- }
+ DexType[] capturedTypes = lambda.descriptor.captures.values;
+ int maxLocals = 1;
+ for (int i = 0; i < capturedTypes.length; i++) {
+ DexField field = lambda.getCaptureField(i);
+ assert field.type == capturedTypes[i];
+ ValueType type = ValueType.fromDexType(field.type);
+ instructions.add(new CfLoad(ValueType.OBJECT, 0));
+ instructions.add(new CfLoad(type, maxLocals));
+ instructions.add(new CfFieldInstruction(Opcodes.PUTFIELD, field, field));
+ maxLocals += type.requiredRegisters();
+ maxStack += type.requiredRegisters();
}
-
// Final return.
- add(IRBuilder::addReturn);
- }
-
- @Override
- public int hashCode() {
- // We want all zero-parameter constructor source code instances to
- // be treated as equal, since it only has one call to super constructor,
- // which is always java.lang.Object.<init>().
- return captures().length == 0
- ? System.identityHashCode(factory().objectMembers.constructor)
- : super.hashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (!(obj instanceof LambdaConstructorSourceCode)) {
- return false;
- }
- if (captures().length == 0) {
- // See comment in hashCode().
- return ((LambdaConstructorSourceCode) obj).captures().length == 0;
- }
- return super.equals(obj);
+ instructions.add(new CfReturnVoid());
+ return new CfCode(
+ lambda.constructor.holder,
+ maxStack,
+ maxLocals,
+ instructions.build(),
+ tryCatchRanges,
+ localVariables);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSynthesizedCode.java
deleted file mode 100644
index 1809d69..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSynthesizedCode.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) 2019, 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.graph.DexType;
-import com.android.tools.r8.graph.UseRegistry;
-import java.util.function.Consumer;
-
-class LambdaConstructorSynthesizedCode extends LambdaSynthesizedCode {
-
- LambdaConstructorSynthesizedCode(LambdaClass lambda) {
- super(lambda);
- }
-
- @Override
- public SourceCodeProvider getSourceCodeProvider() {
- return callerPosition -> new LambdaConstructorSourceCode(lambda, callerPosition);
- }
-
- @Override
- public Consumer<UseRegistry> getRegistryCallback() {
- return registry -> {
- registry.registerInvokeDirect(dexItemFactory().objectMembers.constructor);
- DexType[] capturedTypes = captures();
- for (int i = 0; i < capturedTypes.length; i++) {
- registry.registerInstanceFieldWrite(lambda.getCaptureField(i));
- }
- };
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
index ac55e68..30ea01c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
@@ -4,7 +4,21 @@
package com.android.tools.r8.ir.desugar;
+import com.android.tools.r8.cf.code.CfCheckCast;
+import com.android.tools.r8.cf.code.CfFieldInstruction;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfLoad;
+import com.android.tools.r8.cf.code.CfNew;
+import com.android.tools.r8.cf.code.CfNumberConversion;
+import com.android.tools.r8.cf.code.CfReturn;
+import com.android.tools.r8.cf.code.CfReturnVoid;
+import com.android.tools.r8.cf.code.CfStackInstruction;
+import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
+import com.android.tools.r8.cf.code.CfThrow;
+import com.android.tools.r8.cf.code.CfTryCatch;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
@@ -12,27 +26,25 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.ir.code.NumericType;
-import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.ValueType;
-import com.android.tools.r8.ir.conversion.IRBuilder;
import com.android.tools.r8.ir.desugar.LambdaClass.InvalidLambdaImplTarget;
-import com.android.tools.r8.ir.synthetic.ExceptionThrowingSourceCode;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.Lists;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
+import org.objectweb.asm.Opcodes;
// Source code representing synthesized lambda main method
-final class LambdaMainMethodSourceCode extends SynthesizedLambdaSourceCode {
+final class LambdaMainMethodSourceCode {
- LambdaMainMethodSourceCode(LambdaClass lambda, DexMethod mainMethod, Position callerPosition) {
- super(lambda, mainMethod, callerPosition);
- }
-
- private boolean checkSignatures(
- DexType[] captures, DexType[] enforcedParams, DexType enforcedReturnType,
- List<DexType> implReceiverAndArgs, DexType implReturnType) {
+ private static boolean checkSignatures(
+ DexType[] captures,
+ DexType[] enforcedParams,
+ DexType enforcedReturnType,
+ List<DexType> implReceiverAndArgs,
+ DexType implReturnType,
+ DexItemFactory factory) {
List<DexType> capturesAndParams = new ArrayList<>();
capturesAndParams.addAll(Lists.newArrayList(captures));
capturesAndParams.addAll(Lists.newArrayList(enforcedParams));
@@ -43,23 +55,19 @@
}
for (int i = 0; i < size; i++) {
- if (!isSameOrAdaptableTo(capturesAndParams.get(i), implReceiverAndArgs.get(i))) {
+ if (!isSameOrAdaptableTo(capturesAndParams.get(i), implReceiverAndArgs.get(i), factory)) {
assert false;
}
}
if (!enforcedReturnType.isVoidType()
- && !isSameOrAdaptableTo(implReturnType, enforcedReturnType)) {
+ && !isSameOrAdaptableTo(implReturnType, enforcedReturnType, factory)) {
assert false;
}
return true;
}
- private DexType getPrimitiveFromBoxed(DexType boxedPrimitive) {
- return factory().getPrimitiveFromBoxed(boxedPrimitive);
- }
-
- private DexType getBoxedForPrimitiveType(DexType primitive) {
+ private static DexType getBoxedForPrimitiveType(DexType primitive, DexItemFactory factory) {
switch (primitive.descriptor.content[0]) {
case 'Z': // byte
case 'B': // byte
@@ -69,19 +77,18 @@
case 'J': // long
case 'F': // float
case 'D': // double
- return factory().getBoxedForPrimitiveType(primitive);
+ return factory.getBoxedForPrimitiveType(primitive);
default:
throw new Unreachable("Invalid primitive type descriptor: " + primitive);
}
}
// Checks if the types are the same OR type `a` is adaptable to type `b`.
- private boolean isSameOrAdaptableTo(DexType a, DexType b) {
+ private static boolean isSameOrAdaptableTo(DexType a, DexType b, DexItemFactory factory) {
if (a == b) {
return true;
}
- DexItemFactory factory = factory();
if (a.isArrayType()) {
// Arrays are only adaptable to java.lang.Object or other arrays, note that we
// don't check element type inheritance in the second case since we assume the
@@ -100,7 +107,7 @@
}
// `a` is primitive and `b` is a supertype of the boxed type `a`.
- DexType boxedPrimitiveType = getBoxedForPrimitiveType(a);
+ DexType boxedPrimitiveType = getBoxedForPrimitiveType(a, factory);
if (b == boxedPrimitiveType ||
b == factory.objectType ||
b == factory.serializableType ||
@@ -120,7 +127,7 @@
}
// `a` is a boxed type for `a*` which can be
// widened to primitive type `b`.
- DexType unboxedA = getPrimitiveFromBoxed(a);
+ DexType unboxedA = factory.getPrimitiveFromBoxed(a);
return unboxedA != null &&
isSameOrAdaptableTo(unboxedA.descriptor.content[0], b.descriptor.content[0]);
}
@@ -135,7 +142,7 @@
// For two primitive types `a` is adjustable to `b` iff `a` is the same as `b`
// or can be converted to `b` via a primitive widening conversion.
- private boolean isSameOrAdaptableTo(byte from, byte to) {
+ private static boolean isSameOrAdaptableTo(byte from, byte to) {
if (from == to) {
return true;
}
@@ -159,33 +166,31 @@
}
}
- @Override
- protected void prepareInstructions() {
- DexType[] capturedTypes = captures();
- DexType[] erasedParams = descriptor().erasedProto.parameters.values;
- DexType erasedReturnType = descriptor().erasedProto.returnType;
- DexType[] enforcedParams = descriptor().enforcedProto.parameters.values;
- DexType enforcedReturnType = descriptor().enforcedProto.returnType;
-
+ public static CfCode build(LambdaClass lambda, DexMethod mainMethod) {
+ DexItemFactory factory = lambda.appView.dexItemFactory();
LambdaClass.Target target = lambda.target;
if (target instanceof InvalidLambdaImplTarget) {
- add(
- builder -> {
- InvalidLambdaImplTarget invalidTarget = (InvalidLambdaImplTarget) target;
- ExceptionThrowingSourceCode.build(builder, invalidTarget.exceptionType);
- });
- return;
+ InvalidLambdaImplTarget invalidTarget = (InvalidLambdaImplTarget) target;
+ DexType exceptionType = invalidTarget.exceptionType;
+ return buildThrowingCode(mainMethod, exceptionType, factory);
}
DexMethod methodToCall = target.callTarget;
+ DexType[] capturedTypes = lambda.descriptor.captures.values;
+ DexType[] erasedParams = lambda.descriptor.erasedProto.parameters.values;
+ DexType erasedReturnType = lambda.descriptor.erasedProto.returnType;
+ DexType[] enforcedParams = lambda.descriptor.enforcedProto.parameters.values;
+ DexType enforcedReturnType = lambda.descriptor.enforcedProto.returnType;
// Only constructor call should use direct invoke type since super
// and private methods require accessor methods.
boolean constructorTarget = target.invokeType == Invoke.Type.DIRECT;
- assert !constructorTarget || methodToCall.name == factory().constructorMethodName;
+ assert !constructorTarget || methodToCall.name == factory.constructorMethodName;
+ boolean targetWithReceiver =
+ target.invokeType == Invoke.Type.VIRTUAL || target.invokeType == Invoke.Type.INTERFACE;
List<DexType> implReceiverAndArgs = new ArrayList<>();
- if (target.invokeType == Invoke.Type.VIRTUAL || target.invokeType == Invoke.Type.INTERFACE) {
+ if (targetWithReceiver) {
implReceiverAndArgs.add(methodToCall.holder);
}
implReceiverAndArgs.addAll(Lists.newArrayList(methodToCall.proto.parameters.values));
@@ -195,87 +200,129 @@
|| target.invokeType == Invoke.Type.VIRTUAL
|| target.invokeType == Invoke.Type.DIRECT
|| target.invokeType == Invoke.Type.INTERFACE;
- assert checkSignatures(capturedTypes, enforcedParams,
- enforcedReturnType, implReceiverAndArgs,
- constructorTarget ? target.callTarget.holder : implReturnType);
+ assert checkSignatures(
+ capturedTypes,
+ enforcedParams,
+ enforcedReturnType,
+ implReceiverAndArgs,
+ constructorTarget ? target.callTarget.holder : implReturnType,
+ factory);
- // Prepare call arguments.
- List<ValueType> argValueTypes = new ArrayList<>();
- List<Integer> argRegisters = new ArrayList<>();
+ int maxStack = 0;
+ Builder<CfInstruction> instructions = ImmutableList.builder();
// If the target is a constructor, we need to create the instance first.
- // This instance will be the first argument to the call.
+ // This instance will be the first argument to the call and the dup will be on stack at return.
if (constructorTarget) {
- int instance = nextRegister(ValueType.OBJECT);
- add(builder -> builder.addNewInstance(instance, methodToCall.holder));
- argValueTypes.add(ValueType.OBJECT);
- argRegisters.add(instance);
+ instructions.add(new CfNew(methodToCall.holder));
+ instructions.add(new CfStackInstruction(Opcode.Dup));
+ maxStack += 2;
}
// Load captures if needed.
int capturedValues = capturedTypes.length;
for (int i = 0; i < capturedValues; i++) {
- ValueType valueType = ValueType.fromDexType(capturedTypes[i]);
- int register = nextRegister(valueType);
-
- argValueTypes.add(valueType);
- argRegisters.add(register);
-
- // Read field into tmp local.
DexField field = lambda.getCaptureField(i);
- add(builder -> builder.addInstanceGet(register, getReceiverRegister(), field));
+ ValueType valueType = ValueType.fromDexType(field.type);
+ instructions.add(new CfLoad(ValueType.OBJECT, 0));
+ instructions.add(new CfFieldInstruction(Opcodes.GETFIELD, field, field));
+ maxStack += valueType.requiredRegisters();
}
// Prepare arguments.
+ int maxLocals = 1; // Local 0 is the lambda/receiver.
for (int i = 0; i < erasedParams.length; i++) {
+ ValueType valueType = ValueType.fromDexType(mainMethod.getParameters().values[i]);
+ instructions.add(new CfLoad(valueType, maxLocals));
+ maxLocals += valueType.requiredRegisters();
DexType expectedParamType = implReceiverAndArgs.get(i + capturedValues);
- argValueTypes.add(ValueType.fromDexType(expectedParamType));
- argRegisters.add(prepareParameterValue(
- getParamRegister(i), erasedParams[i], enforcedParams[i], expectedParamType));
+ maxStack =
+ Math.max(
+ maxStack,
+ prepareParameterValue(
+ erasedParams[i], enforcedParams[i], expectedParamType, instructions, factory));
}
- // Method call to the method implementing lambda or method-ref.
- add(
- builder ->
- builder.addInvoke(
- target.invokeType,
- methodToCall,
- methodToCall.proto,
- argValueTypes,
- argRegisters,
- target.isInterface()));
+ instructions.add(
+ new CfInvoke(target.invokeType.getCfOpcode(), methodToCall, target.isInterface()));
+ DexType methodToCallReturnType = methodToCall.getReturnType();
+ if (!methodToCallReturnType.isVoidType()) {
+ maxStack =
+ Math.max(maxStack, ValueType.fromDexType(methodToCallReturnType).requiredRegisters());
+ }
- // Does the method have return value?
if (enforcedReturnType.isVoidType()) {
- add(IRBuilder::addReturn);
- } else if (constructorTarget) {
- // Return newly created instance
- int instanceRegister = argRegisters.get(0);
- int adjustedValue = prepareReturnValue(instanceRegister,
- erasedReturnType, enforcedReturnType, methodToCall.holder);
- add(builder -> builder.addReturn(adjustedValue));
+ if (!methodToCallReturnType.isVoidType()) {
+ instructions.add(
+ new CfStackInstruction(methodToCallReturnType.isWideType() ? Opcode.Pop2 : Opcode.Pop));
+ }
+ instructions.add(new CfReturnVoid());
} else {
- ValueType implValueType = ValueType.fromDexType(implReturnType);
- int tempValue = nextRegister(implValueType);
- add(builder -> builder.addMoveResult(tempValue));
- int adjustedValue = prepareReturnValue(tempValue,
- erasedReturnType, enforcedReturnType, methodToCall.proto.returnType);
- add(builder -> builder.addReturn(adjustedValue));
+ // Either the new instance or the called-method result is on top of stack.
+ assert constructorTarget || !methodToCallReturnType.isVoidType();
+ maxStack =
+ Math.max(
+ maxStack,
+ prepareReturnValue(
+ erasedReturnType,
+ enforcedReturnType,
+ constructorTarget ? methodToCall.holder : methodToCallReturnType,
+ instructions,
+ factory));
+ instructions.add(new CfReturn(ValueType.fromDexType(enforcedReturnType)));
}
+
+ ImmutableList<CfTryCatch> tryCatchRanges = ImmutableList.of();
+ ImmutableList<CfCode.LocalVariableInfo> localVariables = ImmutableList.of();
+ CfCode code =
+ new CfCode(
+ mainMethod.holder,
+ maxStack,
+ maxLocals,
+ instructions.build(),
+ tryCatchRanges,
+ localVariables);
+ return code;
+ }
+
+ private static CfCode buildThrowingCode(
+ DexMethod method, DexType exceptionType, DexItemFactory factory) {
+ DexProto initProto = factory.createProto(factory.voidType);
+ DexMethod initMethod =
+ factory.createMethod(exceptionType, initProto, factory.constructorMethodName);
+ int maxStack = 2;
+ int maxLocals = 1;
+ for (DexType param : method.proto.parameters.values) {
+ maxLocals += ValueType.fromDexType(param).requiredRegisters();
+ }
+ ImmutableList<CfTryCatch> tryCatchRanges = ImmutableList.of();
+ ImmutableList<CfCode.LocalVariableInfo> localVariables = ImmutableList.of();
+ return new CfCode(
+ method.holder,
+ maxStack,
+ maxLocals,
+ ImmutableList.of(
+ new CfNew(exceptionType),
+ new CfStackInstruction(Opcode.Dup),
+ new CfInvoke(Opcodes.INVOKESPECIAL, initMethod, false),
+ new CfThrow()),
+ tryCatchRanges,
+ localVariables);
}
// Adds necessary casts and transformations to adjust the value
// returned by impl-method to expected return type of the method.
- private int prepareReturnValue(int register,
- DexType erasedType, DexType enforcedType, DexType actualType) {
- // `actualType` must be adjusted to `enforcedType` first.
- register = adjustType(register, actualType, enforcedType, true);
-
+ private static int prepareReturnValue(
+ DexType erasedType,
+ DexType enforcedType,
+ DexType actualType,
+ Builder<CfInstruction> instructions,
+ DexItemFactory factory) {
// `erasedType` and `enforcedType` may only differ when they both
// are class types and `erasedType` is a base type of `enforcedType`,
// so no transformation is actually needed.
- assert LambdaDescriptor.isSameOrDerived(factory(), enforcedType, erasedType);
- return register;
+ assert LambdaDescriptor.isSameOrDerived(factory, enforcedType, erasedType);
+ return adjustType(actualType, enforcedType, true, instructions, factory);
}
// Adds necessary casts and transformations to adjust parameter
@@ -285,16 +332,50 @@
// be converted to enforced parameter type (`enforcedType`), which,
// in its turn, may need to be adjusted to the parameter type of
// the impl-method (`expectedType`).
- private int prepareParameterValue(int register,
- DexType erasedType, DexType enforcedType, DexType expectedType) {
- register = enforceParameterType(register, erasedType, enforcedType);
- register = adjustType(register, enforcedType, expectedType, false);
- return register;
+ private static int prepareParameterValue(
+ DexType erasedType,
+ DexType enforcedType,
+ DexType expectedType,
+ Builder<CfInstruction> instructions,
+ DexItemFactory factory) {
+ enforceParameterType(erasedType, enforcedType, instructions, factory);
+ return adjustType(enforcedType, expectedType, false, instructions, factory);
}
- private int adjustType(int register, DexType fromType, DexType toType, boolean returnType) {
+ private static void enforceParameterType(
+ DexType paramType,
+ DexType enforcedType,
+ Builder<CfInstruction> instructions,
+ DexItemFactory factory) {
+ // `paramType` must be either same as `enforcedType` or both must be class
+ // types and `enforcedType` must be a subclass of `paramType` in which case
+ // a cast need to be inserted.
+ if (paramType != enforcedType) {
+ assert LambdaDescriptor.isSameOrDerived(factory, enforcedType, paramType);
+ instructions.add(new CfCheckCast(enforcedType));
+ }
+ }
+
+ private static int adjustType(
+ DexType fromType,
+ DexType toType,
+ boolean returnType,
+ Builder<CfInstruction> instructions,
+ DexItemFactory factory) {
+ internalAdjustType(fromType, toType, returnType, instructions, factory);
+ return Math.max(
+ ValueType.fromDexType(fromType).requiredRegisters(),
+ ValueType.fromDexType(toType).requiredRegisters());
+ }
+
+ private static void internalAdjustType(
+ DexType fromType,
+ DexType toType,
+ boolean returnType,
+ Builder<CfInstruction> instructions,
+ DexItemFactory factory) {
if (fromType == toType) {
- return register;
+ return;
}
boolean fromTypePrimitive = fromType.isPrimitiveType();
@@ -302,7 +383,8 @@
// If both are primitive they must be convertible via primitive widening conversion.
if (fromTypePrimitive && toTypePrimitive) {
- return addPrimitiveWideningConversion(register, fromType, toType);
+ addPrimitiveWideningConversion(fromType, toType, instructions);
+ return;
}
// If the first one is a boxed primitive type and the second one is a primitive
@@ -310,62 +392,64 @@
// widening conversion.
if (toTypePrimitive) {
DexType boxedType = fromType;
- if (boxedType == factory().objectType) {
+ if (boxedType == factory.objectType) {
// We are in situation when from(=java.lang.Object) is being adjusted to a
// primitive type, in which case we assume it is of proper box type.
- boxedType = getBoxedForPrimitiveType(toType);
- register = castToBoxedType(register, boxedType);
+ boxedType = getBoxedForPrimitiveType(toType, factory);
+ instructions.add(new CfCheckCast(boxedType));
}
- DexType fromTypeAsPrimitive = getPrimitiveFromBoxed(boxedType);
+ DexType fromTypeAsPrimitive = factory.getPrimitiveFromBoxed(boxedType);
if (fromTypeAsPrimitive != null) {
- int unboxedRegister = addPrimitiveUnboxing(register, fromTypeAsPrimitive, boxedType);
- return addPrimitiveWideningConversion(unboxedRegister, fromTypeAsPrimitive, toType);
+ addPrimitiveUnboxing(fromTypeAsPrimitive, boxedType, instructions, factory);
+ addPrimitiveWideningConversion(fromTypeAsPrimitive, toType, instructions);
+ return;
}
}
// If the first one is a primitive type and the second one is a boxed
// type for this primitive type, just box the value.
if (fromTypePrimitive) {
- DexType boxedFromType = getBoxedForPrimitiveType(fromType);
- if (toType == boxedFromType ||
- toType == factory().objectType ||
- toType == factory().serializableType ||
- toType == factory().comparableType ||
- (boxedFromType != factory().booleanType &&
- boxedFromType != factory().charType &&
- toType == factory().boxedNumberType)) {
- return addPrimitiveBoxing(register, fromType, boxedFromType);
+ DexType boxedFromType = getBoxedForPrimitiveType(fromType, factory);
+ if (toType == boxedFromType
+ || toType == factory.objectType
+ || toType == factory.serializableType
+ || toType == factory.comparableType
+ || (boxedFromType != factory.booleanType
+ && boxedFromType != factory.charType
+ && toType == factory.boxedNumberType)) {
+ addPrimitiveBoxing(fromType, boxedFromType, instructions, factory);
+ return;
}
}
- if (fromType.isArrayType() && (toType == factory().objectType || toType.isArrayType())) {
+ if (fromType.isArrayType() && (toType == factory.objectType || toType.isArrayType())) {
// If `fromType` is an array and `toType` is java.lang.Object, no cast is needed.
// If both `fromType` and `toType` are arrays, no cast is needed since we assume
// the input code is verifiable.
- return register;
+ return;
}
if ((fromType.isClassType() && toType.isClassType())
- || (fromType == factory().objectType && toType.isArrayType())) {
- if (returnType) {
+ || (fromType == factory.objectType && toType.isArrayType())) {
+ if (returnType && toType != factory.objectType) {
// For return type adjustment in case `fromType` and `toType` are both reference types,
// `fromType` does NOT have to be deriving from `toType` and we need to add a cast.
// NOTE: we don't check `toType` for being actually a supertype, since we
// might not have full classpath with inheritance information to do that.
- int finalRegister = register;
- add(builder -> builder.addCheckCast(finalRegister, toType));
+ instructions.add(new CfCheckCast(toType));
}
- return register;
+ return;
}
throw new Unreachable("Unexpected type adjustment from "
+ fromType.toSourceString() + " to " + toType);
}
- private int addPrimitiveWideningConversion(int register, DexType fromType, DexType toType) {
+ private static void addPrimitiveWideningConversion(
+ DexType fromType, DexType toType, Builder<CfInstruction> instructions) {
assert fromType.isPrimitiveType() && toType.isPrimitiveType();
if (fromType == toType) {
- return register;
+ return;
}
NumericType from = NumericType.fromDexType(fromType);
@@ -379,14 +463,13 @@
if (from != NumericType.BYTE) {
break; // Only BYTE can be converted to SHORT via widening conversion.
}
- int result = nextRegister(ValueType.INT);
- add(builder -> builder.addConversion(to, NumericType.INT, result, register));
- return result;
+ instructions.add(new CfNumberConversion(NumericType.INT, to));
+ return;
}
case INT:
if (from == NumericType.BYTE || from == NumericType.CHAR || from == NumericType.SHORT) {
- return register; // No actual conversion is needed.
+ return;
}
break;
@@ -394,27 +477,24 @@
if (from == NumericType.FLOAT || from == NumericType.DOUBLE) {
break; // Not a widening conversion.
}
- int result = nextRegister(ValueType.LONG);
- add(builder -> builder.addConversion(to, NumericType.INT, result, register));
- return result;
+ instructions.add(new CfNumberConversion(NumericType.INT, to));
+ return;
}
case FLOAT: {
if (from == NumericType.DOUBLE) {
break; // Not a widening conversion.
}
- int result = nextRegister(ValueType.FLOAT);
NumericType type = (from == NumericType.LONG) ? NumericType.LONG : NumericType.INT;
- add(builder -> builder.addConversion(to, type, result, register));
- return result;
+ instructions.add(new CfNumberConversion(type, to));
+ return;
}
case DOUBLE: {
- int result = nextRegister(ValueType.DOUBLE);
NumericType type = (from == NumericType.FLOAT || from == NumericType.LONG)
? from : NumericType.INT;
- add(builder -> builder.addConversion(to, type, result, register));
- return result;
+ instructions.add(new CfNumberConversion(type, to));
+ return;
}
default:
// exception is thrown below
@@ -426,8 +506,7 @@
"converted to " + toType.toSourceString() + " via primitive widening conversion.");
}
- private DexMethod getUnboxMethod(byte primitive, DexType boxType) {
- DexItemFactory factory = factory();
+ private static DexMethod getUnboxMethod(byte primitive, DexType boxType, DexItemFactory factory) {
DexProto proto;
switch (primitive) {
case 'Z': // byte
@@ -459,53 +538,23 @@
}
}
- private int addPrimitiveUnboxing(int register, DexType primitiveType, DexType boxType) {
- DexMethod method = getUnboxMethod(primitiveType.descriptor.content[0], boxType);
-
- List<ValueType> argValueTypes = ImmutableList.of(ValueType.OBJECT);
- List<Integer> argRegisters = Collections.singletonList(register);
- add(
- builder ->
- builder.addInvoke(
- Invoke.Type.VIRTUAL,
- method,
- method.proto,
- argValueTypes,
- argRegisters,
- false /* isInterface */));
-
- ValueType valueType = ValueType.fromDexType(primitiveType);
- int result = nextRegister(valueType);
- add(builder -> builder.addMoveResult(result));
- return result;
+ private static void addPrimitiveUnboxing(
+ DexType primitiveType,
+ DexType boxType,
+ Builder<CfInstruction> instructions,
+ DexItemFactory factory) {
+ DexMethod method = getUnboxMethod(primitiveType.descriptor.content[0], boxType, factory);
+ instructions.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, method, false));
}
- private int castToBoxedType(int register, DexType boxType) {
- add(builder -> builder.addCheckCast(register, boxType));
- return register;
- }
-
- private int addPrimitiveBoxing(int register, DexType primitiveType, DexType boxType) {
+ private static void addPrimitiveBoxing(
+ DexType primitiveType,
+ DexType boxType,
+ Builder<CfInstruction> instructions,
+ DexItemFactory factory) {
// Generate factory method fo boxing.
- DexItemFactory factory = factory();
DexProto proto = factory.createProto(boxType, primitiveType);
DexMethod method = factory.createMethod(boxType, proto, factory.valueOfMethodName);
-
- ValueType valueType = ValueType.fromDexType(primitiveType);
- List<ValueType> argValueTypes = ImmutableList.of(valueType);
- List<Integer> argRegisters = Collections.singletonList(register);
- add(
- builder ->
- builder.addInvoke(
- Invoke.Type.STATIC,
- method,
- method.proto,
- argValueTypes,
- argRegisters,
- false /* isInterface */));
-
- int result = nextRegister(ValueType.OBJECT);
- add(builder -> builder.addMoveResult(result));
- return result;
+ instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, method, false));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSynthesizedCode.java
deleted file mode 100644
index 7c24b63..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSynthesizedCode.java
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright (c) 2019, 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.errors.Unreachable;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.UseRegistry;
-import com.android.tools.r8.ir.code.Invoke;
-import com.android.tools.r8.ir.code.Invoke.Type;
-import java.util.function.Consumer;
-
-public class LambdaMainMethodSynthesizedCode extends LambdaSynthesizedCode {
-
- private final DexMethod mainMethod;
-
- LambdaMainMethodSynthesizedCode(LambdaClass lambda, DexMethod mainMethod) {
- super(lambda);
- this.mainMethod = mainMethod;
- }
-
- @Override
- public SourceCodeProvider getSourceCodeProvider() {
- return callerPosition -> new LambdaMainMethodSourceCode(lambda, mainMethod, callerPosition);
- }
-
- @Override
- public Consumer<UseRegistry> getRegistryCallback() {
- return registry -> {
- LambdaClass.Target target = lambda.target;
- assert target.invokeType == Invoke.Type.STATIC
- || target.invokeType == Invoke.Type.VIRTUAL
- || target.invokeType == Invoke.Type.DIRECT
- || target.invokeType == Invoke.Type.INTERFACE;
-
- boolean constructorTarget = target.invokeType == Type.DIRECT;
- if (constructorTarget) {
- registry.registerNewInstance(target.callTarget.holder);
- }
-
- DexType[] capturedTypes = captures();
- for (int i = 0; i < capturedTypes.length; i++) {
- registry.registerInstanceFieldRead(lambda.getCaptureField(i));
- }
-
- switch (target.invokeType) {
- case DIRECT:
- registry.registerInvokeDirect(target.callTarget);
- break;
- case INTERFACE:
- registry.registerInvokeInterface(target.callTarget);
- break;
- case STATIC:
- registry.registerInvokeStatic(target.callTarget);
- break;
- case VIRTUAL:
- registry.registerInvokeVirtual(target.callTarget);
- break;
- default:
- throw new Unreachable();
- }
- };
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaSynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaSynthesizedCode.java
deleted file mode 100644
index 5643288..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaSynthesizedCode.java
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (c) 2019, 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.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexTypeList;
-import com.android.tools.r8.graph.UseRegistry;
-import com.android.tools.r8.ir.synthetic.SynthesizedCode;
-import java.util.function.Consumer;
-
-abstract class LambdaSynthesizedCode extends SynthesizedCode {
-
- final LambdaClass lambda;
-
- LambdaSynthesizedCode(LambdaClass lambda) {
- super(null);
- this.lambda = lambda;
- }
-
- final DexItemFactory dexItemFactory() {
- return lambda.appView.dexItemFactory();
- }
-
- final LambdaDescriptor descriptor() {
- return lambda.descriptor;
- }
-
- final DexType[] captures() {
- DexTypeList captures = descriptor().captures;
- assert captures != null;
- return captures.values;
- }
-
- @Override
- public abstract SourceCodeProvider getSourceCodeProvider();
-
- @Override
- public abstract Consumer<UseRegistry> getRegistryCallback();
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java
deleted file mode 100644
index fc12713..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java
+++ /dev/null
@@ -1,62 +0,0 @@
-// 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.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexTypeList;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.synthetic.SyntheticSourceCode;
-
-// Represents source code of synthesized lambda class methods.
-abstract class SynthesizedLambdaSourceCode extends SyntheticSourceCode {
-
- final DexMethod currentMethod;
- final LambdaClass lambda;
-
- SynthesizedLambdaSourceCode(
- LambdaClass lambda, DexMethod currentMethod, Position callerPosition, DexType receiver) {
- super(receiver, currentMethod, callerPosition);
- this.lambda = lambda;
- this.currentMethod = currentMethod;
- }
-
- SynthesizedLambdaSourceCode(
- LambdaClass lambda, DexMethod currentMethod, Position callerPosition) {
- this(lambda, currentMethod, callerPosition, lambda.type);
- }
-
- final LambdaDescriptor descriptor() {
- return lambda.descriptor;
- }
-
- final DexType[] captures() {
- DexTypeList captures = descriptor().captures;
- assert captures != null;
- return captures.values;
- }
-
- final DexItemFactory factory() {
- return lambda.appView.dexItemFactory();
- }
-
- final int enforceParameterType(int register, DexType paramType, DexType enforcedType) {
- // `paramType` must be either same as `enforcedType` or both must be class
- // types and `enforcedType` must be a subclass of `paramType` in which case
- // a cast need to be inserted.
- if (paramType != enforcedType) {
- assert LambdaDescriptor.isSameOrDerived(factory(), enforcedType, paramType);
- add(builder -> builder.addCheckCast(register, enforcedType));
- }
- return register;
- }
-
- @Override
- public String toString() {
- return currentMethod.toSourceString();
- }
-}
-
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java
new file mode 100644
index 0000000..8d8ca9f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java
@@ -0,0 +1,243 @@
+// Copyright (c) 2020, 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.synthetic;
+
+import com.android.tools.r8.cf.code.CfCheckCast;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfLoad;
+import com.android.tools.r8.cf.code.CfNew;
+import com.android.tools.r8.cf.code.CfReturn;
+import com.android.tools.r8.cf.code.CfReturnVoid;
+import com.android.tools.r8.cf.code.CfStackInstruction;
+import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
+import com.android.tools.r8.cf.code.CfTryCatch;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.ValueType;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import org.objectweb.asm.Opcodes;
+
+public class ForwardMethodBuilder {
+
+ public static ForwardMethodBuilder builder(DexItemFactory factory) {
+ return new ForwardMethodBuilder(factory);
+ }
+
+ private enum InvokeType {
+ STATIC,
+ VIRTUAL,
+ SPECIAL
+ }
+
+ private final DexItemFactory factory;
+
+ private DexMethod sourceMethod = null;
+ private DexMethod targetMethod = null;
+
+ private boolean staticSource = false;
+
+ private InvokeType invokeType = null;
+ private Boolean isInterface = null;
+ private boolean castResult = false;
+ private boolean isConstructorDelegate = false;
+ private AppInfoWithClassHierarchy appInfoForCastArguments = null;
+
+ private ForwardMethodBuilder(DexItemFactory factory) {
+ this.factory = factory;
+ }
+
+ public ForwardMethodBuilder setNonStaticSource(DexMethod method) {
+ sourceMethod = method;
+ staticSource = false;
+ return this;
+ }
+
+ public ForwardMethodBuilder setStaticSource(DexMethod method) {
+ sourceMethod = method;
+ staticSource = true;
+ return this;
+ }
+
+ public ForwardMethodBuilder setStaticTarget(DexMethod method, boolean isInterface) {
+ targetMethod = method;
+ invokeType = InvokeType.STATIC;
+ this.isInterface = isInterface;
+ return this;
+ }
+
+ public ForwardMethodBuilder setVirtualTarget(DexMethod method, boolean isInterface) {
+ targetMethod = method;
+ invokeType = InvokeType.VIRTUAL;
+ this.isInterface = isInterface;
+ return this;
+ }
+
+ public ForwardMethodBuilder setDirectTarget(DexMethod method, boolean isInterface) {
+ targetMethod = method;
+ invokeType = InvokeType.SPECIAL;
+ this.isInterface = isInterface;
+ return this;
+ }
+
+ public ForwardMethodBuilder setCastResult() {
+ castResult = true;
+ return this;
+ }
+
+ public ForwardMethodBuilder setCastArguments(AppInfoWithClassHierarchy appInfo) {
+ appInfoForCastArguments = appInfo;
+ return this;
+ }
+
+ public ForwardMethodBuilder setConstructorTarget(DexMethod method, DexItemFactory factory) {
+ assert method.isInstanceInitializer(factory);
+ targetMethod = method;
+ isConstructorDelegate = true;
+ invokeType = InvokeType.SPECIAL;
+ isInterface = false;
+ return this;
+ }
+
+ public CfCode build() {
+ assert validate();
+ int maxStack = 0;
+ int maxLocals = 0;
+ Builder<CfInstruction> instructions = ImmutableList.builder();
+ if (isConstructorDelegate) {
+ // A constructor delegate allocates a new instance of the type.
+ // It is dup'ed on the stack so it is ready to return after the invoke call.
+ assert isStaticSource();
+ assert invokeType == InvokeType.SPECIAL;
+ instructions.add(new CfNew(targetMethod.getHolderType()));
+ instructions.add(new CfStackInstruction(Opcode.Dup));
+ maxStack += 2;
+ } else if (!isStaticSource()) {
+ // If source is not static, load the receiver.
+ instructions.add(new CfLoad(ValueType.OBJECT, maxLocals));
+ maybeInsertArgumentCast(-1, sourceMethod.holder, instructions);
+ maxStack += 1;
+ maxLocals += 1;
+ }
+ DexType[] sourceParameters = getSourceParameters();
+ for (int i = 0; i < sourceParameters.length; i++) {
+ DexType parameter = sourceParameters[i];
+ ValueType parameterType = ValueType.fromDexType(parameter);
+ instructions.add(new CfLoad(parameterType, maxLocals));
+ maxLocals += parameterType.requiredRegisters();
+ maybeInsertArgumentCast(i, parameter, instructions);
+ }
+ instructions.add(new CfInvoke(getInvokeOpcode(), targetMethod, isInterface));
+ if (isSourceReturnVoid()) {
+ assert !isConstructorDelegate;
+ instructions.add(new CfReturnVoid());
+ } else {
+ if (!isConstructorDelegate && sourceMethod.getReturnType() != targetMethod.getReturnType()) {
+ assert castResult;
+ if (sourceMethod.getReturnType() != factory.objectType) {
+ instructions.add(new CfCheckCast(sourceMethod.getReturnType()));
+ }
+ }
+ instructions.add(new CfReturn(getSourceReturnType()));
+ }
+ ImmutableList<CfTryCatch> tryCatchRanges = ImmutableList.of();
+ ImmutableList<CfCode.LocalVariableInfo> localVariables = ImmutableList.of();
+ return new CfCode(
+ sourceMethod.holder,
+ maxStack,
+ maxLocals,
+ instructions.build(),
+ tryCatchRanges,
+ localVariables);
+ }
+
+ private void maybeInsertArgumentCast(
+ int argumentIndex, DexType sourceArgumentType, Builder<CfInstruction> instructions) {
+ if (appInfoForCastArguments == null) {
+ return;
+ }
+ // Shift argument index if mapping between static and non-static.
+ if (isStaticSource() != isStaticTarget()) {
+ argumentIndex += isStaticSource() ? -1 : 1;
+ }
+ // Argument -1 is the receiver.
+ DexType targetArgumentType =
+ argumentIndex == -1
+ ? targetMethod.holder
+ : targetMethod.getParameters().values[argumentIndex];
+ if (sourceArgumentType != targetArgumentType
+ && targetArgumentType != appInfoForCastArguments.dexItemFactory().objectType) {
+ assert appInfoForCastArguments.isSubtype(targetArgumentType, sourceArgumentType);
+ instructions.add(new CfCheckCast(targetArgumentType));
+ }
+ }
+
+ private int getInvokeOpcode() {
+ switch (invokeType) {
+ case STATIC:
+ return Opcodes.INVOKESTATIC;
+ case VIRTUAL:
+ return Opcodes.INVOKEVIRTUAL;
+ case SPECIAL:
+ return Opcodes.INVOKESPECIAL;
+ }
+ throw new Unreachable("Unexpected invoke type: " + invokeType);
+ }
+
+ private DexType[] getSourceParameters() {
+ return sourceMethod.getParameters().values;
+ }
+
+ private boolean isSourceReturnVoid() {
+ return sourceMethod.getReturnType().isVoidType();
+ }
+
+ private ValueType getSourceReturnType() {
+ assert !isSourceReturnVoid();
+ return ValueType.fromDexType(sourceMethod.getReturnType());
+ }
+
+ private boolean isStaticSource() {
+ return staticSource;
+ }
+
+ private boolean isStaticTarget() {
+ return invokeType == InvokeType.STATIC;
+ }
+
+ private int sourceArguments() {
+ return sourceMethod.getParameters().size() + (isStaticSource() ? 0 : 1);
+ }
+
+ private int targetArguments() {
+ // A constructor delegate will allocate the instance so that is subtracted from args.
+ return targetMethod.getParameters().size()
+ + (isStaticTarget() || isConstructorDelegate ? 0 : 1);
+ }
+
+ private boolean validate() {
+ assert sourceMethod != null;
+ assert targetMethod != null;
+ assert invokeType != null;
+ assert isInterface != null;
+ assert sourceArguments() == targetArguments();
+ if (isConstructorDelegate) {
+ assert isStaticSource();
+ assert !sourceMethod.getReturnType().isVoidType();
+ assert targetMethod.getReturnType().isVoidType();
+ assert invokeType == InvokeType.SPECIAL;
+ } else if (castResult) {
+ assert ValueType.fromDexType(sourceMethod.getReturnType())
+ == ValueType.fromDexType(targetMethod.getReturnType());
+ } else {
+ assert sourceMethod.getReturnType() == targetMethod.getReturnType();
+ }
+ return true;
+ }
+}