Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lambda inlining optimization #353

Open
wants to merge 87 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
274fd19
Added lambda inlining
TimothyGeerkensGuardsquare Aug 3, 2023
043be3a
Fixes from inlining lambdas in the klox compiler
MaartenS11 Aug 4, 2023
c098ad8
Added test for retracer + addressed Oberon's comment from index 1 to 9
TimothyGeerkensGuardsquare Aug 4, 2023
7922f11
Merge branch 'lambda-inlining' of github.com:MaartenS11/proguard into…
MaartenS11 Aug 7, 2023
bb66672
Fixed regression from merge
MaartenS11 Aug 7, 2023
573995e
Removed some unused fields from the RecursiveInliner
MaartenS11 Aug 7, 2023
e4ce1e0
Removed import .* in LambdaLocator
TimothyGeerkensGuardsquare Aug 7, 2023
70e5966
Simplified the RecursiveInliner a bit
MaartenS11 Aug 7, 2023
aabc5bd
Cleaned up RecursiveInliner by moving some code into a new CalledMeth…
MaartenS11 Aug 7, 2023
f4a22fa
Move some code into IndyLambdaImplVisitor class
MaartenS11 Aug 7, 2023
7b2146e
Remove argument from RecursiveInliner because it isn't really needed …
MaartenS11 Aug 7, 2023
3573c19
Reduce line length a bit by putting index calculation in a variable
MaartenS11 Aug 7, 2023
025571c
Remove wildcard imports from RecursiveInliner
MaartenS11 Aug 7, 2023
845d044
Merge branch 'lambda-inlining' of github.com:MaartenS11/proguard into…
MaartenS11 Aug 7, 2023
13b38d5
Removed visitor staircase in LambdaLocator
TimothyGeerkensGuardsquare Aug 7, 2023
fb9fe7d
Removed wildcards in import
TimothyGeerkensGuardsquare Aug 7, 2023
cf74760
Cleaned up the while loop in the LambdaUsageFinder by using the new I…
MaartenS11 Aug 7, 2023
2090514
Merge branch 'lambda-inlining' of github.com:MaartenS11/proguard into…
MaartenS11 Aug 7, 2023
fb5eedb
Merge pull request #1 from MaartenS11/klox-fixes
MaartenS11 Aug 7, 2023
6ae5fc3
Removed unused class and method
TimothyGeerkensGuardsquare Aug 7, 2023
e90c5c9
merge
TimothyGeerkensGuardsquare Aug 7, 2023
33aa83d
Add more comments to the RecursiveInliner
MaartenS11 Aug 7, 2023
1d49b49
removed print and added logging
TimothyGeerkensGuardsquare Aug 7, 2023
cb60c13
refactored test and added performance tests
TimothyGeerkensGuardsquare Aug 7, 2023
3d3bf8f
Only stop inlining if we actually have the current lambda in one of t…
MaartenS11 Aug 7, 2023
8c354ab
refactored util
TimothyGeerkensGuardsquare Aug 8, 2023
813be34
Use argument index from LambdaUsageFinder instead of searching in the…
MaartenS11 Aug 8, 2023
f155107
RecursiveInliner should also use the argument index from the lambda u…
MaartenS11 Aug 8, 2023
7d82d2d
Remove findFirstLambdaParameter method since it is no longer needed
MaartenS11 Aug 8, 2023
047719c
WIP commit (please amend)
MaartenS11 Aug 8, 2023
5cc6748
Merge branch 'lambda-inlining' of github.com:MaartenS11/proguard into…
MaartenS11 Aug 8, 2023
1f5c85b
Merge pull request #2 from MaartenS11/recursive-inliner-adjustments
MaartenS11 Aug 8, 2023
54233a5
Put stop condition that was duplicated into a method
MaartenS11 Aug 8, 2023
ef37241
Don't hardcode the MethodInliner max resulting code size
MaartenS11 Aug 8, 2023
beeadd5
Merge branch 'lambda-inlining' of github.com:MaartenS11/proguard into…
MaartenS11 Aug 8, 2023
343df22
Fixed cast removal so it works in all cases
MaartenS11 Aug 8, 2023
71213fe
Reverted some minor accidental changes
MaartenS11 Aug 8, 2023
42493c6
Cleaned up a bit
MaartenS11 Aug 8, 2023
8cfd505
Merge pull request #3 from MaartenS11/more-fixes
MaartenS11 Aug 8, 2023
cff6565
Conditionally inline lambdas
MaartenS11 Aug 9, 2023
5644a9d
Added logging when a lambda has been inlined and only print method le…
MaartenS11 Aug 9, 2023
48c52e5
Log the consuming method and invoke method so the use knows which met…
MaartenS11 Aug 9, 2023
badf492
Merge pull request #4 from MaartenS11/inline-conditionally
MaartenS11 Aug 9, 2023
61e1e98
Fixed typo in comment
MaartenS11 Aug 9, 2023
00664b2
Removed some unnecessary comments from the tests
MaartenS11 Aug 9, 2023
fca2cde
Fixed typo in comment
MaartenS11 Aug 9, 2023
f8f1155
Rename package to lambdainline instead of lambdaInline
MaartenS11 Aug 9, 2023
bcab91e
Merge branch 'lambda-inlining' of github.com:MaartenS11/proguard into…
MaartenS11 Aug 9, 2023
2710ff3
Rename lambda_locator package to lambdalocator
MaartenS11 Aug 9, 2023
49bd302
Added newline to Lambda class and removed some unneeded methods
MaartenS11 Aug 9, 2023
55b4f0e
Use result of lambdaUsageHandler.handle() directly instead of storing…
MaartenS11 Aug 9, 2023
7613999
This doesn't have to be a nested if
MaartenS11 Aug 9, 2023
ee3e3ab
Added more javadoc comments to the IterativeInstructionVisitor
MaartenS11 Aug 9, 2023
f1ba773
Added more javadoc strings and fixed a typo
MaartenS11 Aug 9, 2023
7d602c9
Renamed isSourceInstruction to isNotSourceInstruction
TimothyGeerkensGuardsquare Aug 9, 2023
4d18370
Merge branch 'lambda-inlining' of github.com:MaartenS11/proguard into…
TimothyGeerkensGuardsquare Aug 9, 2023
2648338
Added more javadoc comments
MaartenS11 Aug 9, 2023
b431cb7
Apply suggestions from code review
MaartenS11 Aug 9, 2023
11f21f1
Use Optional in MethodLengthFinder so the programmer is made aware th…
MaartenS11 Aug 9, 2023
028e8c4
Merge branch 'lambda-inlining' of github.com:MaartenS11/proguard into…
MaartenS11 Aug 9, 2023
0cc53cd
Return ProgramMethod instead of just Method
MaartenS11 Aug 9, 2023
a440b45
Use the name kotlin lambdas instead of static lambdas in the LambdaLo…
MaartenS11 Aug 9, 2023
7e652b3
Renamed constantInstruction and offset to be more precise also added …
MaartenS11 Aug 9, 2023
607bf86
Added more info about what "a lambda" really means
MaartenS11 Aug 10, 2023
f69fd14
Change some things to the FixedPointCodeAttributeVisitor API
MaartenS11 Aug 10, 2023
95b23ab
invokespecial is now considered a potential source for the sourcetracer
TimothyGeerkensGuardsquare Aug 10, 2023
c5bca4a
merge
TimothyGeerkensGuardsquare Aug 10, 2023
23d4efe
Use library and program class pools instead of passing AppView
MaartenS11 Aug 10, 2023
7cdac26
Merge branch 'lambda-inlining' of github.com:MaartenS11/proguard into…
MaartenS11 Aug 10, 2023
03ae5c6
Made one of the debug log messages info again because it was accident…
MaartenS11 Aug 10, 2023
ad8b5ff
Add large javadoc comment explaining all the steps taken when using t…
MaartenS11 Aug 10, 2023
26d9850
Added comment explaining why descriptor is changed to use unboxed types
MaartenS11 Aug 10, 2023
a5ebb5e
Move a lot of code from the BaseLambdaInliner into a private inner cl…
MaartenS11 Aug 10, 2023
71b63a4
Fixed incorrect comment mentioning method that no longer exists
MaartenS11 Aug 10, 2023
7da8e01
Disable replacing usages with aconst_null
MaartenS11 Aug 10, 2023
751cc74
Add -inlinekotlinlambdas command line option to enable and disable la…
MaartenS11 Aug 10, 2023
f4f84e4
Moved lambda inlining optimization from inline to lambdainline packag…
MaartenS11 Aug 10, 2023
9d080b2
Add CannotInlineException message to debug log
MaartenS11 Aug 10, 2023
b8fbcd0
Add ProGuard header to every class
MaartenS11 Aug 11, 2023
3a866fc
Added detection for the second null check method for parameters in Ko…
MaartenS11 Aug 11, 2023
a910309
Added WIP README file to the lambdainline package
MaartenS11 Aug 11, 2023
0cf13a7
Fix example and explain more about the cast removal/boxing/unboxing
MaartenS11 Aug 11, 2023
1ba33ba
Mention a bit more which classes are used to do which operations
MaartenS11 Aug 11, 2023
ceea1b6
Be more specific when using the ClassReferenceInitializer, not all re…
MaartenS11 Aug 12, 2023
93d3bb6
Only re-run the ClassReferenceInitializer on the consuming class
MaartenS11 Aug 12, 2023
ac7f42d
Only run AccessFixer on consumingClass instead of the entire programC…
MaartenS11 Aug 12, 2023
370704b
Adjust conditional inlining attempt condition to use sum of consuming…
MaartenS11 Aug 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions base/src/main/java/proguard/Configuration.java
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,11 @@ public class Configuration
*/
public boolean mergeInterfacesAggressively = false;

/**
* Specifies whether lambdas should be inlined.
*/
public boolean lambdaInlining = true;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure whether we would like to have this enabled by default but rather have this as an opt-in functionality. For opt-in I believe it should be false here.


///////////////////////////////////////////////////////////////////////////
// Obfuscation options.
///////////////////////////////////////////////////////////////////////////
Expand Down
11 changes: 11 additions & 0 deletions base/src/main/java/proguard/ProGuard.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import proguard.optimize.LineNumberTrimmer;
import proguard.optimize.Optimizer;
import proguard.optimize.gson.GsonOptimizer;
import proguard.optimize.inline.LambdaInliner;
import proguard.optimize.peephole.LineNumberLinearizer;
import proguard.pass.PassRunner;
import proguard.preverify.PreverificationClearer;
Expand Down Expand Up @@ -195,6 +196,11 @@ public void execute() throws Exception
new ListParser(new NameParser()).parse(configuration.optimizations) :
new ConstantMatcher(true);

if (configuration.lambdaInlining)
{
inlineLambdas();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should probably happen inside the optimize method instead, and run once per optimization pass

}

if (configuration.optimize &&
filter.matches(Optimizer.LIBRARY_GSON))
{
Expand Down Expand Up @@ -462,6 +468,11 @@ private void optimizeGson() throws Exception
passRunner.run(new GsonOptimizer(configuration), appView);
}

private void inlineLambdas() throws Exception
TimothyGeerkensGuardsquare marked this conversation as resolved.
Show resolved Hide resolved
{
LambdaInliner lambdaInliner = new LambdaInliner();
passRunner.run(lambdaInliner, appView);
}

/**
* Performs the optimization step.
Expand Down
301 changes: 301 additions & 0 deletions base/src/main/java/proguard/optimize/inline/BaseLambdaInliner.java

Large diffs are not rendered by default.

48 changes: 48 additions & 0 deletions base/src/main/java/proguard/optimize/inline/CallReplacer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package proguard.optimize.inline;

import proguard.classfile.Clazz;
import proguard.classfile.Method;
import proguard.classfile.ProgramClass;
import proguard.classfile.attribute.CodeAttribute;
import proguard.classfile.constant.AnyMethodrefConstant;
import proguard.classfile.constant.visitor.ConstantVisitor;
import proguard.classfile.editor.CodeAttributeEditor;
import proguard.classfile.editor.ConstantPoolEditor;
import proguard.classfile.instruction.ConstantInstruction;
import proguard.classfile.instruction.Instruction;
import proguard.classfile.instruction.visitor.InstructionVisitor;

public class CallReplacer implements InstructionVisitor {
TimothyGeerkensGuardsquare marked this conversation as resolved.
Show resolved Hide resolved
private final CodeAttributeEditor codeAttributeEditor;
private final Clazz methodOwnerClazz;
private final Method oldMethod;
private final Method newMethod;

public CallReplacer(CodeAttributeEditor codeAttributeEditor, Clazz methodOwnerClazz, Method oldMethod, Method newMethod) {
this.codeAttributeEditor = codeAttributeEditor;
this.methodOwnerClazz = methodOwnerClazz;
this.oldMethod = oldMethod;
this.newMethod = newMethod;
}

@Override
public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {}

@Override
public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) {
if (constantInstruction.opcode == Instruction.OP_INVOKESTATIC) {
ConstantPoolEditor constantPoolEditor = new ConstantPoolEditor((ProgramClass) clazz);
clazz.constantPoolEntryAccept(constantInstruction.constantIndex, new ConstantVisitor() {
@Override
public void visitAnyMethodrefConstant(Clazz clazz, AnyMethodrefConstant anyMethodrefConstant) {
if (anyMethodrefConstant.referencedMethod != null && anyMethodrefConstant.referencedMethod.equals(oldMethod)) {
int newMethodIndex = constantPoolEditor.addMethodrefConstant(methodOwnerClazz, newMethod);
codeAttributeEditor.reset(codeAttribute.u4codeLength);
codeAttributeEditor.replaceInstruction(offset, new ConstantInstruction(Instruction.OP_INVOKESTATIC, newMethodIndex));
codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute);
}
}
});
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package proguard.optimize.inline;

public class CannotInlineException extends RuntimeException {
public CannotInlineException(String reason) {
super(reason);
}
}
75 changes: 75 additions & 0 deletions base/src/main/java/proguard/optimize/inline/CastRemover.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package proguard.optimize.inline;

import proguard.classfile.Clazz;
import proguard.classfile.Method;
import proguard.classfile.attribute.CodeAttribute;
import proguard.classfile.constant.AnyMethodrefConstant;
import proguard.classfile.constant.NameAndTypeConstant;
import proguard.classfile.constant.visitor.ConstantVisitor;
import proguard.classfile.editor.CodeAttributeEditor;
import proguard.classfile.instruction.ConstantInstruction;
import proguard.classfile.instruction.Instruction;
import proguard.classfile.instruction.InstructionFactory;
import proguard.classfile.instruction.VariableInstruction;
import proguard.classfile.instruction.visitor.InstructionVisitor;
import proguard.classfile.visitor.ClassPrinter;

import java.util.*;

public class CastRemover implements InstructionVisitor {
TimothyGeerkensGuardsquare marked this conversation as resolved.
Show resolved Hide resolved
private final CodeAttributeEditor codeAttributeEditor;
private List<Integer> keepList;
private int currentIndex;

public CastRemover(CodeAttributeEditor codeAttributeEditor, List<Integer> keepList) {
this.codeAttributeEditor = codeAttributeEditor;
this.currentIndex = 0;
this.keepList = keepList;
}

public CastRemover(CodeAttributeEditor codeAttributeEditor) {
this(codeAttributeEditor, new ArrayList<>());
}

@Override
public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) {
TimothyGeerkensGuardsquare marked this conversation as resolved.
Show resolved Hide resolved
Set<String> castingMethodNames = new HashSet<>(Arrays.asList("intValue", "booleanValue", "byteValue", "shortValue", "longValue", "floatValue", "doubleValue", "charValue"));
if (constantInstruction.opcode == Instruction.OP_CHECKCAST) {
System.out.println("Removing " + InstructionFactory.create(codeAttribute.code, offset).toString(offset));
codeAttributeEditor.deleteInstruction(offset);
} else if (constantInstruction.opcode == Instruction.OP_INVOKESTATIC) {
if (getInvokedMethodName(clazz, constantInstruction).equals("valueOf")) {
if (!keepList.contains(currentIndex)) {
System.out.print("Removing "); InstructionFactory.create(codeAttribute.code, offset).accept(clazz, method, codeAttribute, offset, new ClassPrinter());
codeAttributeEditor.deleteInstruction(offset);
}

currentIndex++;
}
} else if (constantInstruction.opcode == Instruction.OP_INVOKEVIRTUAL) {
System.out.println("Removing " + InstructionFactory.create(codeAttribute.code, offset).toString(offset));
if (castingMethodNames.contains(getInvokedMethodName(clazz, constantInstruction))) {
codeAttributeEditor.deleteInstruction(offset);
}
}
}

@Override
public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {
}

private String getInvokedMethodName(Clazz clazz, ConstantInstruction constantInstruction) {
final String[] invokedMethodName = new String[1];
clazz.constantPoolEntryAccept(constantInstruction.constantIndex, new ConstantVisitor() {
@Override public void visitAnyMethodrefConstant(Clazz clazz, AnyMethodrefConstant anyMethodrefConstant) {
clazz.constantPoolEntryAccept(anyMethodrefConstant.u2nameAndTypeIndex, new ConstantVisitor() {
@Override
public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant) {
invokedMethodName[0] = nameAndTypeConstant.getName(clazz);
}
});
}
});
return invokedMethodName[0];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package proguard.optimize.inline;

import proguard.classfile.*;
import proguard.classfile.editor.AttributeAdder;
import proguard.classfile.editor.ClassBuilder;
import proguard.classfile.editor.ClassEditor;
import proguard.obfuscate.NameFactory;
import proguard.obfuscate.SimpleNameFactory;
import proguard.obfuscate.UniqueMemberNameFactory;
import proguard.optimize.info.ProgramMemberOptimizationInfoSetter;

import java.util.function.Function;

public class DescriptorModifier {
private final ClassBuilder classBuilder;
private final NameFactory nameFactory;
private final Clazz clazz;

DescriptorModifier(Clazz clazz) {
classBuilder = new ClassBuilder((ProgramClass) clazz);
nameFactory = new UniqueMemberNameFactory(new SimpleNameFactory(), clazz);
this.clazz = clazz;
}

public ProgramMethod modify(ProgramMethod sourceMethod, Function<String, String> modifier) {
return modify(sourceMethod, modifier, false);
}

public ProgramMethod modify(ProgramMethod sourceMethod, Function<String, String> modifier, boolean removeOriginal) {
ProgramMethod newMethod = classBuilder.addAndReturnMethod(
sourceMethod.u2accessFlags,
nameFactory.nextName(),
modifier.apply(sourceMethod.getDescriptor(clazz))
);

sourceMethod.attributesAccept((ProgramClass) clazz,
new AttributeAdder((ProgramClass) clazz, newMethod, true));

if (removeOriginal)
removeOriginal(sourceMethod);

newMethod.accept(clazz, new ProgramMemberOptimizationInfoSetter());
return newMethod;
}

private void removeOriginal(ProgramMethod method) {
ClassEditor classEditor = new ClassEditor((ProgramClass) clazz);
classEditor.removeMethod(method);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package proguard.optimize.inline;

import proguard.classfile.instruction.Instruction;

import java.util.Objects;

public final class InstructionAtOffset {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it not more safe to use the CodeAttribute and Instruction for this? Using existing data types, classes,... helps to make use of the existing infrastructure which makes a lot of stuff easier. The infrastructure helps by performing updates on the data structures and performing validity checks.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is that if we only store a CodeAttribute and an Instruction we can't get the correct offset of the instruction reliably.

private final Instruction instruction;
private final int offset;

InstructionAtOffset(Instruction instruction, int offset) {
this.instruction = instruction;
this.offset = offset;
}

public Instruction instruction() {
return instruction;
}

public int offset() {
return offset;
}

@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null || obj.getClass() != this.getClass()) return false;
InstructionAtOffset that = (InstructionAtOffset) obj;
return Objects.equals(this.instruction, that.instruction) &&
this.offset == that.offset;
}

@Override
public int hashCode() {
return Objects.hash(instruction, offset);
}

@Override
public String toString() {
return "InstructionAtOffset[" +
"instruction=" + instruction + ", " +
"offset=" + offset + ']';
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package proguard.optimize.inline;

import proguard.classfile.*;
import proguard.classfile.constant.ClassConstant;
import proguard.classfile.constant.FieldrefConstant;
import proguard.classfile.constant.visitor.ConstantVisitor;
import proguard.classfile.visitor.AllMethodVisitor;
import proguard.classfile.visitor.MemberVisitor;

public class LambdaImplementationVisitor implements ConstantVisitor, MemberVisitor {
private final InvokeMethodVisitor invokeMethodVisitor;
private Clazz lambdaImplementationClass;
public LambdaImplementationVisitor(InvokeMethodVisitor invokeMethodHandler) {
this.invokeMethodVisitor = invokeMethodHandler;
}

@Override
public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) {
lambdaImplementationClass = fieldrefConstant.referencedClass;
fieldrefConstant.referencedClass.interfaceConstantsAccept(this);
}

@Override
public void visitClassConstant(Clazz clazz, ClassConstant referencedClassConstant) {
referencedClassConstant.referencedClass.methodsAccept(this);
}

@Override
public void visitProgramMethod(ProgramClass interfaceClass, ProgramMethod programMethod)
{
getLambdaImplementation(interfaceClass, programMethod);
}

@Override
public void visitLibraryMethod(LibraryClass interfaceClass, LibraryMethod libraryMethod) {
getLambdaImplementation(interfaceClass, libraryMethod);
}

private void getLambdaImplementation(Clazz interfaceClazz, Method method) {
String descriptor = method.getDescriptor(interfaceClazz);
lambdaImplementationClass.methodAccept("invoke", descriptor, new MemberVisitor() {
@Override
public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) {
TimothyGeerkensGuardsquare marked this conversation as resolved.
Show resolved Hide resolved
System.out.println("Descriptor " + programMethod.getDescriptor(programClass));
String descriptor = programMethod.getDescriptor(programClass);
lambdaImplementationClass.accept(new AllMethodVisitor(new MemberVisitor() {
@Override
public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) {
if (programMethod.getName(programClass).equals("invoke") && programMethod.u2accessFlags == (AccessConstants.PUBLIC | AccessConstants.FINAL)) {
lambdaImplementationClass.methodAccept("invoke", programMethod.getDescriptor(programClass), new MemberVisitor() {
@Override
public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) {
invokeMethodVisitor.visitInvokeMethod(programClass, programMethod, interfaceClazz, descriptor);
}
});
}
}
}));
}
});
}

public interface InvokeMethodVisitor {
void visitInvokeMethod(ProgramClass programClass, ProgramMethod programMethod, Clazz interfaceClass, String bridgeDescriptor);
}
}
Loading