Merge pull request #257 from zcbbpo/FIXED-INVOKEINTERFACE_1

Fixed invokeinterface issuse
This commit is contained in:
Fan Lin 2022-01-07 14:55:25 +08:00 committed by GitHub
commit a25f14c0ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 510 additions and 198 deletions

View File

@ -0,0 +1,42 @@
package com.alibaba.demo.lambda;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* @author jim
*/
public interface BaseDemo {
default void blackHole(Object... ignore) {}
default void consumesRun(Runnable r) {
r.run();
}
default void consumesFunction0(Runnable f) {
f.run();
}
default <T> void consumesFunction1(Consumer<T> f) {
f.accept(null);
}
default <T, R> void consumesFunction2(Function<T, R> f) {
f.apply(null);
}
default <T1, T2, R> void consumesFunction3(BiFunction<T1, T2, R> f) {
f.apply(null, null);
}
default <T> void consumesSupplier(Supplier<T> supplier) {
supplier.get();
}
default <T, R> void consumesTwoFunction2(Function<T, R> f1, Function<T, R> f2) {
f1.apply(null);
f2.apply(null);
}
}

View File

@ -0,0 +1,121 @@
package com.alibaba.demo.lambda;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* @author jim
*/
@SuppressWarnings("SimplifyStreamApiCallChains")
public class CollectionListCodeDemo implements BaseDemo {
public void list() {
List<User> userList = getUsers();
double d = userList.stream()
.map(v -> v)
.map(v -> Function.<User>identity().apply(v))
.map(User::getId)
.mapToDouble(Double::valueOf)
.reduce(0, Double::sum);
//.reduce(0, (v1, v2) -> v1 + v2);
blackHole(d);
blackHole(userList.stream()
.map(User::getId)
.mapToLong(Long::valueOf)
.reduce(0, Long::sum));
blackHole(userList.stream()
.map(User::getId)
.mapToInt(Long::intValue)
.reduce(0, Integer::sum));
Map<Long, User> map = userList.stream()
.filter(User::isActive)
.collect(Collectors.toMap(User::getId, Function.identity(), (v1, v2) -> v1));
blackHole(map);
List<User> l1 = userList.stream()
.collect(Collectors.toList())
.stream()
.collect(Collectors.toSet())
.stream()
.collect(Collectors.groupingBy(User::getName))
.entrySet()
.stream()
.filter(v -> v.getValue().stream().anyMatch(User::isActive))
.map(Map.Entry::getValue)
.flatMap(Collection::parallelStream)
.collect(Collectors.groupingBy(User::getId))
.values()
.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
blackHole(l1);
userList.stream()
.map(User::isActive)
.reduce(Boolean::logicalAnd)
.ifPresent(this::blackHole);
}
private List<User> getUsers() {
List<User> userList = new ArrayList<>();
for (int i=0; i<10; i++) {
userList.add(getUser(i));
}
return userList;
}
private User getUser(int index) {
User u1 = new User();
u1.id = (long)index;
u1.name = "u" + index;
u1.age = index;
u1.active = true;
return u1;
}
private static class User {
private Long id;
private String name;
private int age;
private boolean active;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
}
}

View File

@ -0,0 +1,86 @@
package com.alibaba.demo.lambda;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
/**
* @author jim
*/
public class InvokeInterfaceDemo implements BaseDemo {
public void collectionInterfaceDefaultOrStatic() {
blackHole(collectionStreamTest());
blackHole(arraysStreamTest());
}
public void interfaceDefault() {
ILambda l = new LambdaFoo();
consumesRun(l::run);
consumesFunction1(l::function1);
}
public void interfaceStatic() {
consumesRun(ILambda::staticRun);
consumesFunction1(ILambda::staticFunction1);
}
public Object collectionStreamTest() {
List<List<String>> testList = new ArrayList<>();
List<String> fooList = new ArrayList<>();
fooList.add("123");
fooList.add("456");
testList.add(fooList);
return testList.stream()
//.flatMap(v -> v.stream())
.flatMap(Collection::stream)
.map(Double::valueOf)
.map(BigDecimal::new)
.reduce(BigDecimal::add);
}
public Object arraysStreamTest() {
List<String[]> zz = new ArrayList<>();
zz.add(new String[]{"1", "2", "3"});
return zz.stream()
.flatMap(Arrays::stream)
.map(Double::valueOf)
.map(BigDecimal::new)
.reduce(BigDecimal::add);
}
public void objectStaticMethodReference() {
List<Boolean> f = new ArrayList<>();
f.add(false);
f.add(false);
f.add(false);
blackHole(f.stream()
.reduce(Boolean::logicalAnd)
.get()
);
}
public interface ILambda {
default void run() {
}
default void function1(String s) {
}
static void staticRun() {
}
static void staticFunction1(String s) {
}
}
public static class LambdaFoo implements ILambda {
}
}

View File

@ -9,7 +9,7 @@ import java.util.function.Supplier;
* @author jimca
*/
@SuppressWarnings({"WrapperTypeMayBePrimitive", "ResultOfMethodCallIgnored", "MismatchedReadAndWriteOfArray", "unused"})
public class ExternalLambdaDemo {
public class InvokeVirtualDemo {
public void string1() {
String s = "";
@ -197,8 +197,8 @@ public class ExternalLambdaDemo {
}
public void function3() {
ExternalLambdaDemo externalLambdaDemo = new ExternalLambdaDemo();
consumesFunction3(externalLambdaDemo::f3);
InvokeVirtualDemo invokeVirtualDemo = new InvokeVirtualDemo();
consumesFunction3(invokeVirtualDemo::f3);
}
public Boolean f3(String s1, Long l) {

View File

@ -1,7 +1,7 @@
package com.alibaba.demo.lambda;
import java.util.Collections;
import java.util.List;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
@ -10,70 +10,31 @@ import java.util.stream.Collectors;
/**
* @author jim
*/
public class StaticInstanceReference {
public class StaticInstanceReference implements BaseDemo {
private static A a = new A();
private static final StaticClassA STATIC_CLASS_A = new StaticClassA();
public void staticMethodReference() {
//consumesRun(() -> a.doIt());
//A b = new A();
//consumesRun(b::doIt);
consumesRun(a::doIt);
consumesFunction1(a::function1);
consumesFunction2(a::function2);
consumesFunction3(a::function3);
consumesRun(STATIC_CLASS_A::doIt);
consumesFunction1(STATIC_CLASS_A::function1);
consumesFunction2(STATIC_CLASS_A::function2);
consumesFunction3(STATIC_CLASS_A::function3);
}
private void consumesRun(Runnable r) {
r.run();
}
private <T> void consumesFunction1(Consumer<T> r) {
r.accept(null);
}
private <T, R> void consumesFunction2(Function<T, R> r) {
r.apply(null);
}
private <T1, T2, R> void consumesFunction3(BiFunction<T1, T2, R> r) {
r.apply(null, null);
}
public static class A {
public static class StaticClassA {
public void doIt() {
}
public void function1(String s) {
}
public Integer function2(String s) {
public Integer function2(String s) {
return 1;
}
}
public Integer function3(String s, Double d) {
return 1;
}
}
public static class XBean {
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
public void foo() {
List<XBean> testList = Collections.emptyList();
//noinspection RedundantOperationOnEmptyContainer
List<Long> response = testList.stream().map(XBean::getId).distinct().collect(Collectors.toList());
public Integer function3(String s, Double d) {
return 1;
}
}
}

View File

@ -0,0 +1,28 @@
package com.alibaba.demo.lambda;
import com.alibaba.testable.core.annotation.MockDiagnose;
import com.alibaba.testable.core.annotation.MockInvoke;
import com.alibaba.testable.core.model.LogLevel;
import org.junit.jupiter.api.Test;
/**
* @author jim
*/
public class CollectionListCodeDemoTest {
private final CollectionListCodeDemo instance = new CollectionListCodeDemo();
//@MockDiagnose(LogLevel.VERBOSE)
public static class Mock {
@MockInvoke(targetClass = String.class, targetMethod = "contains")
public boolean mockContains(CharSequence s) {
return false;
}
}
@Test
public void listTest() {
instance.list();
}
}

View File

@ -0,0 +1,72 @@
package com.alibaba.demo.lambda;
import com.alibaba.testable.core.annotation.MockDiagnose;
import com.alibaba.testable.core.annotation.MockInvoke;
import com.alibaba.testable.core.model.LogLevel;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Stream;
import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked;
/**
* @author jim
*/
public class InvokeInterfaceDemoTest {
private final InvokeInterfaceDemo instance = new InvokeInterfaceDemo();
//@MockDiagnose(LogLevel.VERBOSE)
public static class Mock {
@MockInvoke(targetClass = InvokeInterfaceDemo.ILambda.class, targetMethod = "run")
private void mockILambdaRun() {
}
@MockInvoke(targetClass = InvokeInterfaceDemo.ILambda.class, targetMethod = "function1")
private void mockILambdaFunction1(String s) {
}
@SuppressWarnings("unchecked")
@MockInvoke(targetClass = Collection.class, targetMethod = "stream")
<E> Stream<E> mockStream() {
List<E> fooList = new ArrayList<>();
fooList.add((E) "123");
fooList.add((E) "456");
return fooList.stream();
}
@MockInvoke(targetClass = Boolean.class, targetMethod = "logicalAnd")
public static boolean mockLogicalAnd(boolean a, boolean b) {
return false;
}
}
@Test
public void shouldMockCollectionStream() {
instance.collectionInterfaceDefaultOrStatic();
verifyInvoked("mockStream").withTimes(1);
}
@Test
public void shouldMockInterfaceDefault() {
instance.interfaceDefault();
verifyInvoked("mockILambdaRun").withTimes(1);
verifyInvoked("mockILambdaFunction1").withTimes(1);
}
@Test
public void shouldMockObjectStaticMethodReference() {
instance.objectStaticMethodReference();
verifyInvoked("mockLogicalAnd").withTimes(2);
}
@Test
public void shouldMockInterfaceStatic() {
instance.interfaceStatic();
}
}

View File

@ -8,8 +8,8 @@ import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked
/**
* @author zcbbpo
*/
public class ExternalLambdaDemoTest {
private final ExternalLambdaDemo lambdaDemo = new ExternalLambdaDemo();
public class InvokeVirtualDemoTest {
private final InvokeVirtualDemo lambdaDemo = new InvokeVirtualDemo();
@SuppressWarnings("unused")
public static class Mock {
@ -34,7 +34,7 @@ public class ExternalLambdaDemoTest {
return "";
}
@MockInvoke(targetClass = ExternalLambdaDemo.class, targetMethod = "f3")
@MockInvoke(targetClass = InvokeVirtualDemo.class, targetMethod = "f3")
public Boolean mockF3(String s1, Long l) {
return true;
}

View File

@ -13,17 +13,17 @@ import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked
*/
public class StaticInstanceReferenceTest {
private StaticInstanceReference instance = new StaticInstanceReference();
private final StaticInstanceReference instance = new StaticInstanceReference();
//@MockDiagnose(LogLevel.VERBOSE)
public static class Mock {
@MockInvoke(targetClass = StaticInstanceReference.A.class, targetMethod = "doIt")
@MockInvoke(targetClass = StaticInstanceReference.StaticClassA.class, targetMethod = "doIt")
private void mockDoIt() {
}
}
@Test
public void shouldMockT1() {
public void shouldMockDoIt() {
instance.staticMethodReference();
verifyInvoked("mockDoIt").withTimes(1);
}

View File

@ -1,20 +1,16 @@
package com.alibaba.testable.agent.handler;
import com.alibaba.testable.agent.model.BasicType;
import com.alibaba.testable.agent.model.BsmArg;
import com.alibaba.testable.agent.model.MethodInfo;
import com.alibaba.testable.agent.model.TravelStatus;
import com.alibaba.testable.agent.util.BytecodeUtil;
import com.alibaba.testable.agent.util.ClassUtil;
import com.alibaba.testable.agent.util.MethodUtil;
import com.alibaba.testable.core.util.LogUtil;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.*;
import org.objectweb.asm.tree.*;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@ -64,12 +60,12 @@ public class SourceClassHandler extends BaseClassHandler {
resolveMethodReference(cn);
for (MethodNode m : cn.methods) {
transformMethod(m, memberInjectMethods, newOperatorInjectMethods, cn);
transformMethod(m, memberInjectMethods, newOperatorInjectMethods);
}
}
private void transformMethod(MethodNode mn, Set<MethodInfo> memberInjectMethods,
Set<MethodInfo> newOperatorInjectMethods, ClassNode cn) {
Set<MethodInfo> newOperatorInjectMethods) {
LogUtil.verbose(" Found method %s", mn.name);
if (mn.name.startsWith("$")) {
// skip methods e.g. "$jacocoInit"
@ -341,85 +337,46 @@ public class SourceClassHandler extends BaseClassHandler {
return Opcodes.INVOKEVIRTUAL == opcode && ClassUtil.isCompanionClassName(ownerClass);
}
private void setFinalValue(Field ownerField, Object obj, Object value) throws Exception {
ownerField.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(ownerField, ownerField.getModifiers() & ~Modifier.FINAL);
ownerField.set(obj, value);
}
private List<Handle> fetchInvokeDynamicHandle(MethodNode mn) {
List<Handle> handleList = new ArrayList<Handle>();
private List<BsmArg> fetchInvokeDynamicHandle(MethodNode mn) {
List<BsmArg> handleList = new ArrayList<BsmArg>();
for (AbstractInsnNode instruction : mn.instructions) {
if (instruction.getOpcode() == Opcodes.INVOKEDYNAMIC) {
InvokeDynamicInsnNode invokeDynamicInsnNode = (InvokeDynamicInsnNode) instruction;
handleList.add((Handle) invokeDynamicInsnNode.bsmArgs[1]);
//handleList.add((Handle) invokeDynamicInsnNode.bsmArgs[1]);
BsmArg bsmArg = new BsmArg(invokeDynamicInsnNode.bsmArgs);
handleList.add(bsmArg);
}
}
return handleList;
}
private void resolveMethodReference(ClassNode cn) {
List<Handle> invokeDynamicList = new ArrayList<Handle>();
List<BsmArg> invokeDynamicList = new ArrayList<BsmArg>();
for (MethodNode method : cn.methods) {
List<Handle> handleList = fetchInvokeDynamicHandle(method);
List<BsmArg> handleList = fetchInvokeDynamicHandle(method);
invokeDynamicList.addAll(handleList);
}
// process for method reference
for (Handle handle : invokeDynamicList) {
for (BsmArg bsmArg : invokeDynamicList) {
// the jdk auto generation method
if (handle.getName().startsWith("lambda$")) {
if (bsmArg.getHandle().getName().startsWith("lambda$")) {
continue;
}
int tag = handle.getTag();
int tag = bsmArg.getHandle().getTag();
if (tag == Opcodes.H_NEWINVOKESPECIAL) {
// lambda new method reference
continue;
}
// external mean:
// public void foo() {
// String s = "";
// consumes(s::contains);
//}
boolean external = tag == Opcodes.H_INVOKEVIRTUAL;
boolean isStatic = bsmArg.isStatic();
Handle handle = bsmArg.getHandle();
Type handleDesc = bsmArg.getHandleDesc();
boolean isStatic = tag == Opcodes.H_INVOKESTATIC || external;
String desc = handle.getDesc();
String parameters = desc.substring(desc.indexOf("(") + 1, desc.lastIndexOf(")"));
String returnType = desc.substring(desc.indexOf(")") + 1);
String[] parameterArray = parameters.split(";");
int len = parameterArray.length;
for (String s : parameterArray) {
if (s.isEmpty()) {
len--;
}
}
len = external ? len + 1 : len;
String[] refineParameterArray = new String[len];
int index = external ? 1 : 0;
if (external) {
// The type should was reference type
refineParameterArray[0] = "L" + handle.getOwner();
}
for (String s : parameterArray) {
if (!s.isEmpty()) {
refineParameterArray[index] = s;
index++;
}
}
String externalDesc = buildDesc(refineParameterArray, returnType);
String lambdaName = String.format("Lambda$_%s_%d", handle.getName(), atomicInteger.incrementAndGet());
MethodVisitor mv = cn.visitMethod(isStatic ? ACC_PUBLIC + ACC_STATIC : ACC_PUBLIC, lambdaName, external ? externalDesc : desc, null, null);
String lambdaName = String.format("_Lambda$_%s_%d", handle.getName(), atomicInteger.incrementAndGet());
MethodVisitor mv = cn.visitMethod(isStatic ? ACC_PUBLIC + ACC_STATIC : ACC_PUBLIC, lambdaName, handleDesc.getDescriptor(), null, null);
mv.visitCode();
Label l0 = new Label();
@ -428,102 +385,52 @@ public class SourceClassHandler extends BaseClassHandler {
// add this
mv.visitVarInsn(ALOAD, 0);
}
for (int i = 0; i < refineParameterArray.length; i++) {
String arg = refineParameterArray[i];
mv.visitVarInsn(getLoadType(arg), isStatic ? i : i + 1);
Type[] argumentTypes = handleDesc.getArgumentTypes();
int nextVar = isStatic ? 0 : 1;
for (Type argumentType : argumentTypes) {
String arg = argumentType.getDescriptor();
mv.visitVarInsn(getLoadType(arg), nextVar);
nextVar = isLongByte(argumentType) ? nextVar + 2 : nextVar + 1;
}
// the method call is static ?
mv.visitMethodInsn(Opcodes.H_INVOKESTATIC == tag ? INVOKESTATIC : INVOKEVIRTUAL, handle.getOwner(), handle.getName(), desc, false);
if (tag == H_INVOKEINTERFACE) {
mv.visitMethodInsn(INVOKEINTERFACE, handle.getOwner(), handle.getName(), bsmArg.getOriginalHandleDesc(), handle.isInterface());
} else {
mv.visitMethodInsn(Opcodes.H_INVOKESTATIC == tag ? INVOKESTATIC : INVOKEVIRTUAL, handle.getOwner(), handle.getName(), bsmArg.getOriginalHandleDesc(), handle.isInterface());
}
mv.visitInsn(getReturnType(returnType));
mv.visitInsn(getReturnType(handleDesc.getReturnType().getDescriptor()));
Label l1 = new Label();
mv.visitLabel(l1);
// static function was not required add this to first parameter
if (isStatic) {
for (int i = 0; i < refineParameterArray.length; i++) {
String localVar = refineParameterArray[i];
if (!isPrimitive(localVar)) {
// primitive type and reference type difference
localVar = localVar.endsWith(";") ? localVar : localVar + ";";
}
if (localVar.isEmpty()) {
continue;
}
// add local var
mv.visitLocalVariable(String.format("o%d", i), localVar, null, l0, l1, i);
}
visitLocalVariableByArguments(mv, 0, argumentTypes, l0, l1);
} else {
mv.visitLocalVariable("this", "L" + handle.getOwner() + ";", null, l0, l1, 0);
for (int i = 0; i < refineParameterArray.length; i++) {
String localVar = refineParameterArray[i];
if (!isPrimitive(localVar) && !isPrimitiveArray(localVar)) {
localVar = localVar.endsWith(";") ? localVar : localVar + ";";
}
if (localVar.isEmpty()) {
continue;
}
mv.visitLocalVariable(String.format("o%d", i), localVar, null, l0, l1, i + 1);
}
visitLocalVariableByArguments(mv, 1, argumentTypes, l0, l1);
}
// auto compute max
mv.visitMaxs(-1, -1);
mv.visitEnd();
try {
// modify handle to the generation method
setFinalValue(handle.getClass().getDeclaredField("name"), handle, lambdaName);
// mark: should merge the below two if.
if (!handle.getOwner().equals(cn.name) && isStatic) {
setFinalValue(handle.getClass().getDeclaredField("owner"), handle, cn.name);
}
if (external) {
setFinalValue(handle.getClass().getDeclaredField("owner"), handle, cn.name);
setFinalValue(handle.getClass().getDeclaredField("descriptor"), handle, externalDesc);
setFinalValue(handle.getClass().getDeclaredField("tag"), handle, H_INVOKESTATIC);
}
} catch (Exception ignore) {
}
bsmArg.complete(cn.name, lambdaName);
}
}
private String buildDesc(String[] refineParameterArray, String returnType) {
StringBuilder sb = new StringBuilder();
sb.append("(");
for (String s : refineParameterArray) {
sb.append(s);
if (!isPrimitive(s)) {
sb.append(";");
}
private void visitLocalVariableByArguments(MethodVisitor mv, final int initVar, Type[] argumentTypes, Label l0, Label l1) {
int nextLocalVar = initVar;
for (int i = 0; i < argumentTypes.length; i++) {
Type argumentType = argumentTypes[i];
String localVar = argumentType.getDescriptor();
mv.visitLocalVariable(String.format("o%d", i), localVar, null, l0, l1, nextLocalVar);
nextLocalVar = isLongByte(argumentType) ? nextLocalVar + 2 : nextLocalVar + 1;
}
sb.append(")");
sb.append(returnType);
return sb.toString();
}
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
private boolean isPrimitive(String type) {
if (type.endsWith(";")) {
type = type.substring(0, type.length() - 1);
}
return BasicType.basicType(type.charAt(0)).isPrimitive();
}
private boolean isPrimitiveArray(String type) {
if (!type.startsWith("[")) {
return false;
}
if (type.endsWith(";")) {
type = type.substring(0, type.length() - 1);
}
type = type.replace("[", "");
return BasicType.basicType(type.charAt(0)).isPrimitive();
private boolean isLongByte(Type argumentType) {
return double.class.getName().equals(argumentType.getClassName()) || long.class.getName().equals(argumentType.getClassName());
}
private int getReturnType(String returnType) {

View File

@ -0,0 +1,84 @@
package com.alibaba.testable.agent.model;
import com.alibaba.testable.agent.util.StringUtil;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
/**
* @author jim
*/
public class BsmArg {
private Type handleDesc;
private Type methodDesc;
private Handle handle;
private final Object[] bsmArgs;
private final String originalHandleDesc;
public BsmArg(Object[] bsmArgs) {
this.bsmArgs = bsmArgs;
handle = (Handle) bsmArgs[1];
methodDesc = (Type) bsmArgs[2];
handleDesc = Type.getType(handle.getDesc());
originalHandleDesc = handle.getDesc();
// H_INVOKEVIRTUAL: String s = "";consumes(s::contains);
// H_INVOKEINTERFACE: list.stream().flatMap(Collection::stream)
if (handle.getTag() == Opcodes.H_INVOKEVIRTUAL || handle.getTag() == Opcodes.H_INVOKEINTERFACE) {
Type[] argumentTypes = handleDesc.getArgumentTypes();
Type thisArgument = Type.getType("L" + handle.getOwner() + ";");
String handleDescString = refineHandle(thisArgument, argumentTypes);
handle = new Handle(handle.getTag(), handle.getOwner(), handle.getName(), handleDescString, handle.isInterface());
handleDesc = Type.getType(handleDescString);
if (/*handle.getTag() == Opcodes.H_INVOKEVIRTUAL || */methodDesc.getArgumentTypes().length == handleDesc.getArgumentTypes().length - 1) {
Type[] methodArguments = methodDesc.getArgumentTypes();
String methodDescString = refineHandle(thisArgument, methodArguments);
methodDesc = Type.getType(methodDescString);
}
}
}
private String refineHandle(Type thisArgument, Type[] argumentTypes) {
Type[] handleArguments = new Type[argumentTypes.length + 1];
String[] handleArgs = new String[argumentTypes.length + 1];
handleArguments[0] = thisArgument;
System.arraycopy(argumentTypes, 0, handleArguments, 1, argumentTypes.length);
for (int i = 0; i < handleArguments.length; i++) {
handleArgs[i] = handleArguments[i].getDescriptor();
}
return "(" + StringUtil.join("", handleArgs) + ")" + handleDesc.getReturnType().getDescriptor();
}
public Type getHandleDesc() {
return handleDesc;
}
public Type getMethodDesc() {
return methodDesc;
}
public Handle getHandle() {
return handle;
}
public String getOriginalHandleDesc() {
return originalHandleDesc;
}
public boolean isStatic() {
int tag = handle.getTag();
return tag == Opcodes.H_INVOKESTATIC || tag == Opcodes.H_INVOKEVIRTUAL || tag == Opcodes.H_INVOKEINTERFACE;
}
public void complete(String owner, String methodName) {
bsmArgs[1] = new Handle(isStatic()? Opcodes.H_INVOKESTATIC : Opcodes.H_INVOKEVIRTUAL, owner, methodName, handleDesc.getDescriptor(), false);
}
}

View File

@ -19,4 +19,15 @@ public class StringUtil {
return sb.toString();
}
public static String join(String delimiter, String[] s) {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < s.length; i++) {
stringBuilder.append(s[i]);
if (i != s.length - 1) {
stringBuilder.append(delimiter);
}
}
return stringBuilder.toString();
}
}