mirror of
https://github.com/alibaba/testable-mock.git
synced 2025-03-13 19:30:35 +08:00
Merge pull request #257 from zcbbpo/FIXED-INVOKEINTERFACE_1
Fixed invokeinterface issuse
This commit is contained in:
commit
a25f14c0ec
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
||||
}
|
||||
}
|
@ -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) {
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user