Merge "Fix --tool=D8|R8 selection for JCTF"
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index e21b00a..188440d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -18,11 +18,14 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.ir.code.MoveType;
import com.android.tools.r8.ir.code.ValueNumberGenerator;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.InliningConstraint;
import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
+import com.android.tools.r8.ir.synthetic.SynthesizedCode;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
@@ -305,6 +308,38 @@
return builder.build();
}
+ public DexEncodedMethod toForwardingMethod(DexClass holder, DexItemFactory itemFactory) {
+ assert accessFlags.isPublic();
+ DexMethod newMethod = itemFactory.createMethod(holder.type, method.proto, method.name);
+ Invoke.Type type = accessFlags.isStatic() ? Invoke.Type.STATIC : Invoke.Type.SUPER;
+ Builder builder = builder(this);
+ builder.setMethod(newMethod);
+ if (accessFlags.isAbstract()) {
+ // If the forwarding target is abstract, we can just create an abstract method. While it
+ // will not actually forward, it will create the same exception when hit at runtime.
+ builder.accessFlags.setAbstract();
+ } else {
+ // Create code that forwards the call to the target.
+ builder.setCode(new SynthesizedCode(
+ new ForwardMethodSourceCode(
+ accessFlags.isStatic() ? null : holder.type,
+ method.proto,
+ accessFlags.isStatic() ? null : method.holder,
+ method,
+ type),
+ registry -> {
+ if (accessFlags.isStatic()) {
+ registry.registerInvokeStatic(method);
+ } else {
+ registry.registerInvokeSuper(method);
+ }
+ }));
+ }
+ builder.accessFlags.setSynthetic();
+ accessFlags.unsetFinal();
+ return builder.build();
+ }
+
public String codeToString() {
return code == null ? "<no code>" : code.toString();
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 368cb22..6713083 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -52,6 +52,7 @@
}
}
+ @Override
public void addDependencies(MixedSectionCollection collector) {
// We only have a class data item if there are methods or fields.
if (hasMethodsOrFields()) {
@@ -119,4 +120,20 @@
public DexEncodedArray getStaticValues() {
return staticValues;
}
+
+ public void addVirtualMethod(DexEncodedMethod virtualMethod) {
+ assert !virtualMethod.accessFlags.isStatic();
+ assert !virtualMethod.accessFlags.isPrivate();
+ assert !virtualMethod.accessFlags.isConstructor();
+ virtualMethods = Arrays.copyOf(virtualMethods, virtualMethods.length + 1);
+ virtualMethods[virtualMethods.length - 1] = virtualMethod;
+ }
+
+ public void addStaticMethod(DexEncodedMethod staticMethod) {
+ assert staticMethod.accessFlags.isStatic();
+ assert !staticMethod.accessFlags.isPrivate();
+ assert !staticMethod.accessFlags.isConstructor();
+ directMethods = Arrays.copyOf(directMethods, directMethods.length + 1);
+ directMethods[directMethods.length - 1] = staticMethod;
+ }
}
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 bee210c..c957b58 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
@@ -18,6 +18,7 @@
// Source code representing synthesized accessor method.
final class AccessorMethodSourceCode extends SynthesizedLambdaSourceCode {
+
AccessorMethodSourceCode(LambdaClass lambda) {
super(/* no receiver for static method */ null, lambda, lambda.target.callTarget);
// We should never need an accessor for interface methods since
@@ -81,7 +82,7 @@
}
@Override
- void prepareInstructions() {
+ protected void prepareInstructions() {
DexMethod implMethod = descriptor().implHandle.asMethod();
DexType[] accessorParams = proto.parameters.values;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
index ecda01e..236c9e5 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -11,6 +11,8 @@
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.synthetic.ForwardMethodSourceCode;
+import com.android.tools.r8.ir.synthetic.SynthesizedCode;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.IdentityHashMap;
@@ -22,6 +24,7 @@
// Default and static method interface desugaring processor for classes.
// Adds default interface methods into the class when needed.
final class ClassProcessor {
+
private final InterfaceMethodRewriter rewriter;
// Set of already processed classes.
private final Set<DexClass> processedClasses = Sets.newIdentityHashSet();
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 d35566a..cc4f1f2 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
@@ -14,6 +14,7 @@
// Source code representing synthesized lambda bridge method.
final class LambdaBridgeMethodSourceCode extends SynthesizedLambdaSourceCode {
+
private final DexMethod mainMethod;
LambdaBridgeMethodSourceCode(LambdaClass lambda, DexMethod mainMethod, DexMethod bridgeMethod) {
@@ -22,7 +23,7 @@
}
@Override
- void prepareInstructions() {
+ protected void prepareInstructions() {
DexType[] currentParams = proto.parameters.values;
DexType[] enforcedParams = descriptor().enforcedProto.parameters.values;
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 e92f78f..a4801c9 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
@@ -12,8 +12,8 @@
import com.android.tools.r8.graph.DexAnnotationSetRefList;
import com.android.tools.r8.graph.DexApplication.Builder;
import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexClassPromise;
+import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
@@ -27,6 +27,7 @@
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.synthetic.SynthesizedCode;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -49,6 +50,7 @@
* separate lambda classes.
*/
final class LambdaClass {
+
final LambdaRewriter rewriter;
final DexType type;
final LambdaDescriptor descriptor;
@@ -370,6 +372,7 @@
// 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;
@@ -386,6 +389,7 @@
// Used for targeting methods referenced directly without creating accessors.
private final class NoAccessorMethodTarget extends Target {
+
NoAccessorMethodTarget(Invoke.Type invokeType) {
super(descriptor.implHandle.asMethod(), invokeType);
}
@@ -398,6 +402,7 @@
// Used for static private lambda$ methods. Only needs access relaxation.
private final class StaticLambdaImplTarget extends Target {
+
StaticLambdaImplTarget() {
super(descriptor.implHandle.asMethod(), Invoke.Type.STATIC);
}
@@ -420,6 +425,7 @@
// Used for instance private lambda$ methods. Needs to be converted to
// a package-private static method.
private class InstanceLambdaImplTarget extends Target {
+
InstanceLambdaImplTarget(DexMethod staticMethod) {
super(staticMethod, Invoke.Type.STATIC);
}
@@ -452,7 +458,7 @@
dexCode.setDebugInfo(dexCode.debugInfoWithAdditionalFirstParameter(null));
assert (dexCode.getDebugInfo() == null)
|| (callTarget.proto.parameters.values.length
- == dexCode.getDebugInfo().parameters.length);
+ == dexCode.getDebugInfo().parameters.length);
directMethods[i] = newMethod;
return true;
}
@@ -464,6 +470,7 @@
// 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);
}
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 db8e1f5..539fcf0 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
@@ -13,13 +13,14 @@
// Source code representing synthesized lambda class constructor.
// Used for stateless lambdas to instantiate singleton instance.
final class LambdaClassConstructorSourceCode extends SynthesizedLambdaSourceCode {
+
LambdaClassConstructorSourceCode(LambdaClass lambda) {
super(null /* Class initializer is static */, lambda, lambda.classConstructor);
assert lambda.instanceField != null;
}
@Override
- void prepareInstructions() {
+ protected void prepareInstructions() {
// Create and initialize an instance.
int instance = nextRegister(MoveType.OBJECT);
add(builder -> builder.addNewInstance(instance, lambda.type));
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 b4aef78..b736403 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
@@ -14,12 +14,13 @@
// Source code representing synthesized lambda constructor.
final class LambdaConstructorSourceCode extends SynthesizedLambdaSourceCode {
+
LambdaConstructorSourceCode(LambdaClass lambda) {
super(lambda, lambda.constructor);
}
@Override
- void prepareInstructions() {
+ protected void prepareInstructions() {
// Super constructor call (always java.lang.Object.<init>()).
DexMethod objectInitMethod = lambda.rewriter.objectInitMethod;
add(builder -> builder.addInvoke(Invoke.Type.DIRECT, objectInitMethod,
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 9e4c570..5eb4c30 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
@@ -25,6 +25,7 @@
// Source code representing synthesized lambda main method
final class LambdaMainMethodSourceCode extends SynthesizedLambdaSourceCode {
+
LambdaMainMethodSourceCode(LambdaClass lambda, DexMethod mainMethod) {
super(lambda, mainMethod);
}
@@ -177,7 +178,7 @@
}
@Override
- void prepareInstructions() {
+ protected void prepareInstructions() {
DexType[] capturedTypes = captures();
DexType[] erasedParams = descriptor().erasedProto.parameters.values;
DexType erasedReturnType = descriptor().erasedProto.returnType;
@@ -462,7 +463,7 @@
private int addPrimitiveBoxing(int register, DexType primitiveType, DexType boxType) {
// Generate factory method fo boxing.
DexItemFactory factory = factory();
- DexProto proto = factory.createProto(boxType, new DexType[] { primitiveType });
+ DexProto proto = factory.createProto(boxType, new DexType[]{primitiveType});
DexMethod method = factory.createMethod(boxType, proto, factory.valueOfMethodName);
MoveType moveType = MoveType.fromDexType(primitiveType);
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
index 7a63c32..9a3531f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java
@@ -8,9 +8,11 @@
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.synthetic.SingleBlockSourceCode;
// Represents source code of synthesized lambda class methods.
abstract class SynthesizedLambdaSourceCode extends SingleBlockSourceCode {
+
final DexMethod currentMethod;
final LambdaClass lambda;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ForwardMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java
similarity index 87%
rename from src/main/java/com/android/tools/r8/ir/desugar/ForwardMethodSourceCode.java
rename to src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java
index 808456b..bcc9462 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ForwardMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java
@@ -2,7 +2,7 @@
// 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;
+package com.android.tools.r8.ir.synthetic;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.graph.DexMethod;
@@ -16,12 +16,13 @@
import java.util.List;
// Source code representing simple forwarding method.
-final class ForwardMethodSourceCode extends SingleBlockSourceCode {
+public final class ForwardMethodSourceCode extends SingleBlockSourceCode {
+
private final DexType targetReceiver;
private final DexMethod target;
private final Invoke.Type invokeType;
- ForwardMethodSourceCode(DexType receiver, DexProto proto,
+ public ForwardMethodSourceCode(DexType receiver, DexProto proto,
DexType targetReceiver, DexMethod target, Invoke.Type invokeType) {
super(receiver, proto);
assert (targetReceiver == null) == (invokeType == Invoke.Type.STATIC);
@@ -31,8 +32,14 @@
this.invokeType = invokeType;
assert checkSignatures();
- if (invokeType != Invoke.Type.STATIC) {
- throw new Unimplemented("Invoke type " + invokeType + " is not yet supported.");
+ switch (invokeType) {
+ case STATIC:
+ case SUPER:
+ case INTERFACE:
+ case VIRTUAL:
+ break;
+ default:
+ throw new Unimplemented("Invoke type " + invokeType + " is not yet supported.");
}
}
@@ -65,7 +72,7 @@
}
@Override
- void prepareInstructions() {
+ protected void prepareInstructions() {
// Prepare call arguments.
List<MoveType> argMoveTypes = new ArrayList<>();
List<Integer> argRegisters = new ArrayList<>();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/SingleBlockSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/SingleBlockSourceCode.java
similarity index 89%
rename from src/main/java/com/android/tools/r8/ir/desugar/SingleBlockSourceCode.java
rename to src/main/java/com/android/tools/r8/ir/synthetic/SingleBlockSourceCode.java
index 32631c2..8c97f8f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/SingleBlockSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/SingleBlockSourceCode.java
@@ -2,7 +2,7 @@
// 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;
+package com.android.tools.r8.ir.synthetic;
import static com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo.NO_THROW;
@@ -21,9 +21,10 @@
import java.util.List;
import java.util.function.Consumer;
-abstract class SingleBlockSourceCode implements SourceCode {
- final DexType receiver;
- final DexProto proto;
+public abstract class SingleBlockSourceCode implements SourceCode {
+
+ protected final DexType receiver;
+ protected final DexProto proto;
// The next free register, note that we always
// assign each value a new (next available) register.
@@ -40,7 +41,7 @@
// Instruction constructors
private List<Consumer<IRBuilder>> constructors = new ArrayList<>();
- SingleBlockSourceCode(DexType receiver, DexProto proto) {
+ protected SingleBlockSourceCode(DexType receiver, DexProto proto) {
assert proto != null;
this.receiver = receiver;
this.proto = proto;
@@ -57,45 +58,45 @@
}
}
- final void add(Consumer<IRBuilder> constructor) {
+ protected final void add(Consumer<IRBuilder> constructor) {
constructors.add(constructor);
}
- final int nextRegister(MoveType type) {
+ protected final int nextRegister(MoveType type) {
int value = nextRegister;
nextRegister += type == MoveType.WIDE ? 2 : 1;
return value;
}
- final Value getReceiverValue() {
+ protected final Value getReceiverValue() {
assert receiver != null;
assert receiverValue != null;
return receiverValue;
}
- final int getReceiverRegister() {
+ protected final int getReceiverRegister() {
assert receiver != null;
assert receiverRegister >= 0;
return receiverRegister;
}
- final Value getParamValue(int paramIndex) {
+ protected final Value getParamValue(int paramIndex) {
assert paramIndex >= 0;
assert paramIndex < paramValues.length;
return paramValues[paramIndex];
}
- final int getParamCount() {
+ protected final int getParamCount() {
return paramValues.length;
}
- final int getParamRegister(int paramIndex) {
+ protected final int getParamRegister(int paramIndex) {
assert paramIndex >= 0;
assert paramIndex < paramRegisters.length;
return paramRegisters[paramIndex];
}
- abstract void prepareInstructions();
+ protected abstract void prepareInstructions();
@Override
public final boolean needsPrelude() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java
similarity index 73%
rename from src/main/java/com/android/tools/r8/ir/desugar/SynthesizedCode.java
rename to src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java
index 3a07ef4..7716663 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java
@@ -2,7 +2,7 @@
// 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;
+package com.android.tools.r8.ir.synthetic;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.Code;
@@ -13,12 +13,21 @@
import com.android.tools.r8.ir.conversion.SourceCode;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.utils.InternalOptions;
+import java.util.function.Consumer;
public final class SynthesizedCode extends Code {
+
private final SourceCode sourceCode;
+ private final Consumer<UseRegistry> registryCallback;
public SynthesizedCode(SourceCode sourceCode) {
this.sourceCode = sourceCode;
+ this.registryCallback = SynthesizedCode::registerReachableDefinitionsDefault;
+ }
+
+ public SynthesizedCode(SourceCode sourceCode, Consumer<UseRegistry> callback) {
+ this.sourceCode = sourceCode;
+ this.registryCallback = callback;
}
@Override
@@ -31,12 +40,16 @@
return toString(null);
}
- @Override
- public final void registerReachableDefinitions(UseRegistry registry) {
+ private static void registerReachableDefinitionsDefault(UseRegistry registry) {
throw new Unreachable();
}
@Override
+ public void registerReachableDefinitions(UseRegistry registry) {
+ registryCallback.accept(registry);
+ }
+
+ @Override
protected final int computeHashCode() {
return sourceCode.hashCode();
}
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index 4e68d0f..ee0614f 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -3,21 +3,25 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.optimize;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.Descriptor;
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.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.google.common.collect.Sets;
import java.util.Set;
+import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
public class MemberRebindingAnalysis {
+
private final AppInfoWithLiveness appInfo;
private final GraphLense lense;
private final GraphLense.Builder builder = GraphLense.builder();
@@ -109,21 +113,77 @@
private void computeMethodRebinding(Set<DexMethod> methods,
Function<DexMethod, DexEncodedMethod> lookupTarget,
- BiFunction<DexClass, DexMethod, DexEncodedMethod> lookupTargetOnClass) {
+ BiFunction<DexClass, DexMethod, DexEncodedMethod> lookupTargetOnClass,
+ BiConsumer<DexProgramClass, DexEncodedMethod> addMethod) {
for (DexMethod method : methods) {
method = lense.lookupMethod(method, null);
// We can safely ignore array types, as the corresponding methods are defined in a library.
if (!method.getHolder().isClassType()) {
continue;
}
+ DexClass originalClass = appInfo.definitionFor(method.holder);
+ // We can safely ignore calls to library classes, as those cannot be rebound.
+ if (originalClass == null || originalClass.isLibraryClass()) {
+ continue;
+ }
DexEncodedMethod target = lookupTarget.apply(method);
// Rebind to the lowest library class or program class.
if (target != null && target.method != method) {
+ DexClass targetClass = appInfo.definitionFor(target.method.holder);
+ // If the targetclass is not public but the targeted method is, we might run into
+ // visibility problems when rebinding.
+ if (!targetClass.accessFlags.isPublic() && target.accessFlags.isPublic()) {
+ // If the original class is public and this method is public, it might have been called
+ // from anywhere, so we need a bridge. Likewise, if the original is in a different
+ // package, we might need a bridge, too.
+ String packageDescriptor =
+ originalClass.accessFlags.isPublic() ? null : method.holder.getPackageDescriptor();
+ if (packageDescriptor == null
+ || !packageDescriptor.equals(targetClass.type.getPackageDescriptor())) {
+ DexProgramClass bridgeHolder = findBridgeMethodHolder(originalClass, targetClass,
+ packageDescriptor);
+ assert bridgeHolder != null;
+ DexEncodedMethod bridgeMethod = target
+ .toForwardingMethod(bridgeHolder, appInfo.dexItemFactory);
+ addMethod.accept(bridgeHolder, bridgeMethod);
+ assert lookupTarget.apply(method) == bridgeMethod;
+ target = bridgeMethod;
+ }
+ }
builder.map(method, validTargetFor(target.method, method, lookupTargetOnClass));
}
}
}
+ private DexProgramClass findBridgeMethodHolder(DexClass originalClass, DexClass targetClass,
+ String packageDescriptor) {
+ if (originalClass == targetClass || originalClass.isLibraryClass()) {
+ return null;
+ }
+ DexProgramClass newHolder = null;
+ // Recurse through supertype chain.
+ if (originalClass.superType.isSubtypeOf(targetClass.getType(), appInfo)) {
+ DexClass superClass = appInfo.definitionFor(originalClass.superType);
+ newHolder = findBridgeMethodHolder(superClass, targetClass, packageDescriptor);
+ } else {
+ for (DexType iface : originalClass.interfaces.values) {
+ if (iface.isSubtypeOf(targetClass.getType(), appInfo)) {
+ DexClass interfaceClass = appInfo.definitionFor(iface);
+ newHolder = findBridgeMethodHolder(interfaceClass, targetClass, packageDescriptor);
+ }
+ }
+ }
+ if (newHolder != null) {
+ // A supertype fulfills the visibility requirements.
+ return newHolder;
+ } else if (originalClass.accessFlags.isPublic()
+ || originalClass.type.getPackageDescriptor().equals(packageDescriptor)) {
+ // This class is visible. Return it if it is a program class, otherwise null.
+ return originalClass.asProgramClass();
+ }
+ return null;
+ }
+
private void computeFieldRebinding(Set<DexField> fields,
BiFunction<DexType, DexField, DexEncodedField> lookup,
BiFunction<DexClass, DexField, DexEncodedField> lookupTargetOnClass) {
@@ -137,14 +197,19 @@
}
}
+ private static void privateMethodsCheck(DexProgramClass clazz, DexEncodedMethod method) {
+ throw new Unreachable("Direct invokes should not require forwarding.");
+ }
+
public GraphLense run() {
computeMethodRebinding(appInfo.virtualInvokes, this::virtualLookup,
- DexClass::findVirtualTarget);
- computeMethodRebinding(appInfo.superInvokes, this::superLookup, DexClass::findVirtualTarget);
+ DexClass::findVirtualTarget, DexProgramClass::addVirtualMethod);
+ computeMethodRebinding(appInfo.superInvokes, this::superLookup, DexClass::findVirtualTarget,
+ DexProgramClass::addVirtualMethod);
computeMethodRebinding(appInfo.directInvokes, appInfo::lookupDirectTarget,
- DexClass::findDirectTarget);
+ DexClass::findDirectTarget, MemberRebindingAnalysis::privateMethodsCheck);
computeMethodRebinding(appInfo.staticInvokes, appInfo::lookupStaticTarget,
- DexClass::findDirectTarget);
+ DexClass::findDirectTarget, DexProgramClass::addStaticMethod);
computeFieldRebinding(Sets.union(appInfo.staticFieldsRead, appInfo.staticFieldsWritten),
appInfo::lookupStaticTarget, DexClass::findStaticTarget);
diff --git a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
index e038f6a..ec430fd 100644
--- a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
@@ -221,4 +221,5 @@
return false;
}
}
+
}
diff --git a/src/test/examples/memberrebinding/Test.java b/src/test/examples/memberrebinding/Test.java
index ba5e2f0..62a2440 100644
--- a/src/test/examples/memberrebinding/Test.java
+++ b/src/test/examples/memberrebinding/Test.java
@@ -23,6 +23,7 @@
System.out.println(classExtendsLibraryClass.get(2));
PublicClass instance = new PublicClass();
instance.aMethod();
+ PublicClass.aStaticMethod();
ClassExtendsOtherLibraryClass classExtendsOther = new ClassExtendsOtherLibraryClass();
System.out.println(classExtendsOther.aMethodThatReturnsOne());
System.out.println(classExtendsOther.aMethodThatReturnsTwo());
diff --git a/src/test/examples/memberrebinding/subpackage/PackagePrivateClass.java b/src/test/examples/memberrebinding/subpackage/PackagePrivateClass.java
index d749fb1..89e1cea 100644
--- a/src/test/examples/memberrebinding/subpackage/PackagePrivateClass.java
+++ b/src/test/examples/memberrebinding/subpackage/PackagePrivateClass.java
@@ -8,4 +8,8 @@
public final void aMethod() {
System.out.println("Hello, I am aMethod");
}
+
+ public final static void aStaticMethod() {
+ System.out.println("Hello, I am aStaticMethod");
+ }
}
diff --git a/src/test/examples/memberrebinding/subpackage/PublicClass.java b/src/test/examples/memberrebinding/subpackage/PublicClass.java
index df3a92d..6c0a270 100644
--- a/src/test/examples/memberrebinding/subpackage/PublicClass.java
+++ b/src/test/examples/memberrebinding/subpackage/PublicClass.java
@@ -3,6 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package memberrebinding.subpackage;
-public class PublicClass extends PackagePrivateClass {
+public class PublicClass extends PublicClassInTheMiddle {
// Intentionally left empty.
}
diff --git a/src/test/examples/memberrebinding/subpackage/PublicClassInTheMiddle.java b/src/test/examples/memberrebinding/subpackage/PublicClassInTheMiddle.java
new file mode 100644
index 0000000..6ce8cad
--- /dev/null
+++ b/src/test/examples/memberrebinding/subpackage/PublicClassInTheMiddle.java
@@ -0,0 +1,8 @@
+// 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 memberrebinding.subpackage;
+
+public class PublicClassInTheMiddle extends PackagePrivateClass {
+ // Intentionally left empty.
+}
diff --git a/src/test/examplesAndroidN/memberrebinding4/Test.java b/src/test/examplesAndroidN/memberrebinding4/Test.java
new file mode 100644
index 0000000..6fc2ad5
--- /dev/null
+++ b/src/test/examplesAndroidN/memberrebinding4/Test.java
@@ -0,0 +1,21 @@
+// 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 memberrebinding4;
+
+import memberrebinding4.subpackage.PublicInterface;
+
+public class Test {
+
+ static class Inner implements PublicInterface {
+
+ }
+
+ public static void main(String[] args) {
+ test();
+ }
+
+ public static void test() {
+ new Inner().dump();
+ }
+}
diff --git a/src/test/examplesAndroidN/memberrebinding4/subpackage/PackagePrivateInterface.java b/src/test/examplesAndroidN/memberrebinding4/subpackage/PackagePrivateInterface.java
new file mode 100644
index 0000000..4bbb70d
--- /dev/null
+++ b/src/test/examplesAndroidN/memberrebinding4/subpackage/PackagePrivateInterface.java
@@ -0,0 +1,12 @@
+// 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 memberrebinding4.subpackage;
+
+interface PackagePrivateInterface {
+
+ default void dump() {
+ System.out.println("I3");
+ }
+}
+
diff --git a/src/test/examplesAndroidN/memberrebinding4/subpackage/PublicInterface.java b/src/test/examplesAndroidN/memberrebinding4/subpackage/PublicInterface.java
new file mode 100644
index 0000000..8f2e1bb
--- /dev/null
+++ b/src/test/examplesAndroidN/memberrebinding4/subpackage/PublicInterface.java
@@ -0,0 +1,9 @@
+// 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 memberrebinding4.subpackage;
+
+public interface PublicInterface extends PackagePrivateInterface {
+
+}
+
diff --git a/src/test/java/com/android/tools/r8/dex/ExtraFileTest.java b/src/test/java/com/android/tools/r8/dex/ExtraFileTest.java
index d2aa592..26067c7 100644
--- a/src/test/java/com/android/tools/r8/dex/ExtraFileTest.java
+++ b/src/test/java/com/android/tools/r8/dex/ExtraFileTest.java
@@ -16,7 +16,6 @@
import java.util.List;
import java.util.concurrent.ExecutionException;
import org.junit.Assert;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
@@ -33,7 +32,6 @@
@Rule
public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
- @Ignore("b/38187737")
@Test
public void splitMemberRebindingTwoFiles()
throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index 7c75610..115aabd 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -37,9 +37,9 @@
import com.android.tools.r8.ir.code.Switch.Type;
import com.android.tools.r8.ir.conversion.IRBuilder;
import com.android.tools.r8.ir.conversion.SourceCode;
-import com.android.tools.r8.ir.desugar.SynthesizedCode;
import com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator;
import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import com.android.tools.r8.ir.synthetic.SynthesizedCode;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.shaking.ProguardRuleParserException;
import com.android.tools.r8.utils.AndroidApp;
@@ -82,6 +82,7 @@
private static final int MANY_CLASSES_COUNT = 1000;
private static final List<String> MANY_CLASSES;
private static final String MANY_CLASSES_APP = "many-classes.zip";
+
static {
ImmutableList.Builder<String> builder = ImmutableList.builder();
for (int i = 0; i < MANY_CLASSES_COUNT; ++i) {
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java
index e45b639..55bd373 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java
@@ -7,9 +7,9 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.CompilationException;
-import com.android.tools.r8.R8;
import com.android.tools.r8.R8Command;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.shaking.ProguardRuleParserException;
import com.android.tools.r8.utils.DexInspector;
@@ -22,16 +22,12 @@
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
-import java.util.Map;
-import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -63,23 +59,22 @@
private final Path programFile;
private final Consumer<DexInspector> inspection;
private final Consumer<DexInspector> originalInspection;
+ private final int minApiLevel;
@Rule
public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
- public MemberRebindingTest(
- String test, Frontend kind,
- Consumer<DexInspector> inspection,
- Consumer<DexInspector> originalInspection) {
- this.kind = kind;
- originalDex = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + test + "/classes.dex");
+ public MemberRebindingTest(TestConfiguration configuration) {
+ this.kind = configuration.kind;
+ originalDex = configuration.getDexPath();
if (kind == Frontend.DEX) {
this.programFile = originalDex;
} else {
- this.programFile = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + test + ".jar");
+ this.programFile = configuration.getJarPath();
}
- this.inspection = inspection;
- this.originalInspection = originalInspection;
+ this.inspection = configuration.processedInspection;
+ this.originalInspection = configuration.originalInspection;
+ this.minApiLevel = configuration.getMinApiLevel();
}
@Before
@@ -95,12 +90,14 @@
.setOutputPath(Paths.get(out))
.addProgramFiles(programFile)
.addLibraryFiles(ListUtils.map(libs, Paths::get))
+ .setMinApiLevel(minApiLevel)
.build(),
options -> options.inlineAccessors = false);
}
private static boolean coolInvokes(InstructionSubject instruction) {
- if (!instruction.isInvokeVirtual() && !instruction.isInvokeInterface()) {
+ if (!instruction.isInvokeVirtual() && !instruction.isInvokeInterface() &&
+ !instruction.isInvokeStatic()) {
return false;
}
InvokeInstructionSubject invoke = (InvokeInstructionSubject) instruction;
@@ -121,6 +118,7 @@
assertTrue(iterator.next().holder().is("memberrebinding.ClassExtendsLibraryClass"));
assertTrue(iterator.next().holder().is("memberrebinding.ClassExtendsLibraryClass"));
assertTrue(iterator.next().holder().is("memberrebinding.subpackage.PublicClass"));
+ assertTrue(iterator.next().holder().is("memberrebinding.subpackage.PublicClass"));
assertTrue(iterator.next().holder().is("memberrebinding.ClassExtendsOtherLibraryClass"));
assertTrue(iterator.next().holder().is("memberrebinding.ClassExtendsOtherLibraryClass"));
assertTrue(iterator.next().holder().is("memberrebinding.ClassExtendsOtherLibraryClass"));
@@ -147,7 +145,8 @@
assertTrue(iterator.next().holder().is("java.util.ArrayList"));
assertTrue(iterator.next().holder().is("java.util.ArrayList"));
assertTrue(iterator.next().holder().is("java.util.ArrayList"));
- assertTrue(iterator.next().holder().is("memberrebinding.subpackage.PackagePrivateClass"));
+ assertTrue(iterator.next().holder().is("memberrebinding.subpackage.PublicClassInTheMiddle"));
+ assertTrue(iterator.next().holder().is("memberrebinding.subpackage.PublicClassInTheMiddle"));
// For the next three - test that we re-bind to the lowest library class.
assertTrue(iterator.next().holder().is("memberrebindinglib.SubClass"));
assertTrue(iterator.next().holder().is("memberrebindinglib.SubClass"));
@@ -173,6 +172,8 @@
assertTrue(iterator.next().holder().is("memberrebinding2.ClassAtBottomOfChain"));
assertTrue(iterator.next().holder().is("memberrebinding2.subpackage.PublicClass"));
}
+ assertTrue(iterator.next().holder().is("java.lang.System"));
+ assertFalse(iterator.hasNext());
}
private static void inspectMain2(DexInspector inspector) {
@@ -186,6 +187,8 @@
assertTrue(iterator.next().holder().is("memberrebinding2.SuperClassOfAll"));
assertTrue(iterator.next().holder().is("memberrebinding2.subpackage.PackagePrivateClass"));
}
+ assertTrue(iterator.next().holder().is("java.lang.System"));
+ assertFalse(iterator.hasNext());
}
public static MethodSignature TEST =
@@ -198,6 +201,7 @@
assertTrue(iterator.next().holder().is("memberrebinding3.ClassAtBottomOfChain"));
assertTrue(iterator.next().holder().is("memberrebinding3.ClassAtBottomOfChain"));
assertTrue(iterator.next().holder().is("memberrebinding3.ClassAtBottomOfChain"));
+ assertFalse(iterator.hasNext());
}
private static void inspect3(DexInspector inspector) {
@@ -207,38 +211,114 @@
assertTrue(iterator.next().holder().is("memberrebinding3.ClassAtBottomOfChain"));
assertTrue(iterator.next().holder().is("memberrebinding3.ClassInMiddleOfChain"));
assertTrue(iterator.next().holder().is("memberrebinding3.SuperClassOfAll"));
+ assertFalse(iterator.hasNext());
}
- @Parameters(name = "{0}{1}")
- public static Collection<Object[]> data() {
- List<String> tests =
- ImmutableList.of("memberrebinding", "memberrebinding2", "memberrebinding3");
+ private static void inspectOriginal4(DexInspector inspector) {
+ MethodSubject main = inspector.clazz("memberrebinding4.Test").method(TEST);
+ Iterator<InvokeInstructionSubject> iterator =
+ main.iterateInstructions(InstructionSubject::isInvoke);
+ assertTrue(iterator.next().holder().is("memberrebinding4.Test$Inner"));
+ assertTrue(iterator.next().holder().is("memberrebinding4.subpackage.PublicInterface"));
+ assertFalse(iterator.hasNext());
+ }
- Map<String, List<Consumer<DexInspector>>> inspections = new HashMap<>();
- inspections.put("memberrebinding", ImmutableList.of(
- MemberRebindingTest::inspectMain,
- MemberRebindingTest::inspectOriginalMain));
- inspections.put("memberrebinding2", ImmutableList.of(
- MemberRebindingTest::inspectMain2,
- MemberRebindingTest::inspectOriginalMain2));
- inspections.put("memberrebinding3", ImmutableList.of(
- MemberRebindingTest::inspect3,
- MemberRebindingTest::inspectOriginal3));
+ private static void inspect4(DexInspector inspector) {
+ MethodSubject main = inspector.clazz("memberrebinding4.Test").method(TEST);
+ Iterator<InvokeInstructionSubject> iterator =
+ main.iterateInstructions(InstructionSubject::isInvoke);
+ assertTrue(iterator.next().holder().is("memberrebinding4.Test$Inner"));
+ assertTrue(iterator.next().holder().is("memberrebinding4.subpackage.PublicInterface"));
+ assertFalse(iterator.hasNext());
+ }
- List<Object[]> testCases = new ArrayList<>();
- Set<String> usedInspections = new HashSet<>();
+ private static class TestConfiguration {
- for (String test : tests) {
- List<Consumer<DexInspector>> inspection = inspections.get(test);
- assert inspection != null;
- usedInspections.add(test);
- testCases.add(new Object[]{test, Frontend.JAR, inspection.get(0), inspection.get(1)});
- testCases.add(new Object[]{test, Frontend.DEX, inspection.get(0), inspection.get(1)});
+ private enum AndroidVersion {
+ PRE_N,
+ N
}
- assert usedInspections.size() == inspections.size();
+ final String name;
+ final Frontend kind;
+ final AndroidVersion version;
+ final Consumer<DexInspector> originalInspection;
+ final Consumer<DexInspector> processedInspection;
- return testCases;
+ private TestConfiguration(String name,
+ Frontend kind,
+ AndroidVersion version,
+ Consumer<DexInspector> originalInspection,
+ Consumer<DexInspector> processedInspection) {
+ this.name = name;
+ this.kind = kind;
+ this.version = version;
+ this.originalInspection = originalInspection;
+ this.processedInspection = processedInspection;
+ }
+
+ public static void add(ImmutableList.Builder<TestConfiguration> builder,
+ String name,
+ AndroidVersion version,
+ Consumer<DexInspector> originalInspection,
+ Consumer<DexInspector> processedInspection) {
+ if (version == AndroidVersion.PRE_N) {
+ builder.add(new TestConfiguration(name, Frontend.DEX, version, originalInspection,
+ processedInspection));
+ }
+ builder.add(new TestConfiguration(name, Frontend.JAR, version, originalInspection,
+ processedInspection));
+ }
+
+ public Path getDexPath() {
+ return getBuildPath().resolve(name).resolve("classes.dex");
+ }
+
+ public Path getJarPath() {
+ return getBuildPath().resolve(name + ".jar");
+ }
+
+ public Path getBuildPath() {
+ switch (version) {
+ case PRE_N:
+ return Paths.get(ToolHelper.EXAMPLES_BUILD_DIR);
+ case N:
+ return Paths.get(ToolHelper.EXAMPLES_ANDROID_N_BUILD_DIR);
+ default:
+ Assert.fail();
+ return null;
+ }
+ }
+
+ public int getMinApiLevel() {
+ switch (version) {
+ case PRE_N:
+ return Constants.DEFAULT_ANDROID_API;
+ case N:
+ return Constants.ANDROID_N_API;
+ default:
+ Assert.fail();
+ return -1;
+ }
+ }
+
+ public String toString() {
+ return name + " " + kind;
+ }
+ }
+
+ @Parameters(name = "{0}")
+ public static Collection<TestConfiguration> data() {
+ ImmutableList.Builder<TestConfiguration> builder = ImmutableList.builder();
+ TestConfiguration.add(builder, "memberrebinding", TestConfiguration.AndroidVersion.PRE_N,
+ MemberRebindingTest::inspectOriginalMain, MemberRebindingTest::inspectMain);
+ TestConfiguration.add(builder, "memberrebinding2", TestConfiguration.AndroidVersion.PRE_N,
+ MemberRebindingTest::inspectOriginalMain2, MemberRebindingTest::inspectMain2);
+ TestConfiguration.add(builder, "memberrebinding3", TestConfiguration.AndroidVersion.PRE_N,
+ MemberRebindingTest::inspectOriginal3, MemberRebindingTest::inspect3);
+ TestConfiguration.add(builder, "memberrebinding4", TestConfiguration.AndroidVersion.N,
+ MemberRebindingTest::inspectOriginal4, MemberRebindingTest::inspect4);
+ return builder.build();
}
@Test
diff --git a/src/test/java/com/android/tools/r8/utils/DexInspector.java b/src/test/java/com/android/tools/r8/utils/DexInspector.java
index 694ccc5..39e7dd2 100644
--- a/src/test/java/com/android/tools/r8/utils/DexInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/DexInspector.java
@@ -886,6 +886,12 @@
if (instruction instanceof InvokeDirectRange) {
return ((InvokeDirectRange) instruction).getMethod();
}
+ if (instruction instanceof InvokeStatic) {
+ return ((InvokeStatic) instruction).getMethod();
+ }
+ if (instruction instanceof InvokeStaticRange) {
+ return ((InvokeStaticRange) instruction).getMethod();
+ }
assert false;
return null;
}