From de2e520d95fe1be9be670840d45c50fdd46c55f1 Mon Sep 17 00:00:00 2001 From: jimcao Date: Tue, 4 Jan 2022 18:02:49 +0800 Subject: [PATCH 1/6] Fixed interface default method --- .../demo/lambda/StaticInstanceReference.java | 81 +++++++++++++------ .../lambda/StaticInstanceReferenceTest.java | 21 ++++- .../agent/handler/SourceClassHandler.java | 8 +- 3 files changed, 82 insertions(+), 28 deletions(-) diff --git a/demo/java-demo/src/main/java/com/alibaba/demo/lambda/StaticInstanceReference.java b/demo/java-demo/src/main/java/com/alibaba/demo/lambda/StaticInstanceReference.java index f608868..41687a9 100644 --- a/demo/java-demo/src/main/java/com/alibaba/demo/lambda/StaticInstanceReference.java +++ b/demo/java-demo/src/main/java/com/alibaba/demo/lambda/StaticInstanceReference.java @@ -1,5 +1,8 @@ package com.alibaba.demo.lambda; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.function.BiFunction; @@ -12,16 +15,22 @@ import java.util.stream.Collectors; */ public class StaticInstanceReference { - 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); + StaticClassA a = new StaticClassA(); 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); + blackHole(invokeInterfaceTest()); + } + + public void interfaceDefault() { + ILambda l = new LambdaFoo(); + consumesRun(l::run); + consumesFunction1(l::function1); } private void consumesRun(Runnable r) { @@ -40,35 +49,34 @@ public class StaticInstanceReference { 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 Integer function3(String s, Double d) { + return 1; + } + } - public static class XBean { + public static class XBean { private Long id; - public Long getId() { - return id; - } + public Long getId() { + return id; + } - public void setId(Long id) { - this.id = id; - } - } + public void setId(Long id) { + this.id = id; + } + } public void foo() { List testList = Collections.emptyList(); @@ -76,4 +84,31 @@ public class StaticInstanceReference { List response = testList.stream().map(XBean::getId).distinct().collect(Collectors.toList()); } + public Object invokeInterfaceTest() { + List> zz = new ArrayList<>(); + zz.add(new ArrayList<>()); + return zz.stream() + //.flatMap(Collection::stream) + //.flatMap(v -> v.stream()) + .flatMap(Collection::stream) + .map(Double::valueOf) + .map(BigDecimal::new) + .reduce(BigDecimal::add); + } + + private void blackHole(Object... ignore) {} + + public interface ILambda { + default void run() { + + } + + default void function1(String s) { + + } + } + + public static class LambdaFoo implements ILambda { + + } } diff --git a/demo/java-demo/src/test/java/com/alibaba/demo/lambda/StaticInstanceReferenceTest.java b/demo/java-demo/src/test/java/com/alibaba/demo/lambda/StaticInstanceReferenceTest.java index b03b5e9..c5a6bfe 100644 --- a/demo/java-demo/src/test/java/com/alibaba/demo/lambda/StaticInstanceReferenceTest.java +++ b/demo/java-demo/src/test/java/com/alibaba/demo/lambda/StaticInstanceReferenceTest.java @@ -13,18 +13,33 @@ 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() { } + + @MockInvoke(targetClass = StaticInstanceReference.ILambda.class, targetMethod = "run") + private void mockFooRun() { + } + + @MockInvoke(targetClass = StaticInstanceReference.ILambda.class, targetMethod = "function1") + private void mockIFunction1(String s) { + } } @Test public void shouldMockT1() { instance.staticMethodReference(); - verifyInvoked("mockDoIt").withTimes(1); + verifyInvoked("mockDoIt").withTimes(2); + } + + @Test + public void shouldMockFooRun() { + instance.interfaceDefault(); + verifyInvoked("mockFooRun").withTimes(1); + verifyInvoked("mockIFunction1").withTimes(1); } } diff --git a/testable-agent/src/main/java/com/alibaba/testable/agent/handler/SourceClassHandler.java b/testable-agent/src/main/java/com/alibaba/testable/agent/handler/SourceClassHandler.java index e66e180..4f3be9b 100644 --- a/testable-agent/src/main/java/com/alibaba/testable/agent/handler/SourceClassHandler.java +++ b/testable-agent/src/main/java/com/alibaba/testable/agent/handler/SourceClassHandler.java @@ -386,7 +386,7 @@ public class SourceClassHandler extends BaseClassHandler { // String s = ""; // consumes(s::contains); //} - boolean external = tag == Opcodes.H_INVOKEVIRTUAL; + boolean external = tag == Opcodes.H_INVOKEVIRTUAL || tag == H_INVOKEINTERFACE; boolean isStatic = tag == Opcodes.H_INVOKESTATIC || external; @@ -434,7 +434,11 @@ public class SourceClassHandler extends BaseClassHandler { } // the method call is static ? - mv.visitMethodInsn(Opcodes.H_INVOKESTATIC == tag ? INVOKESTATIC : INVOKEVIRTUAL, handle.getOwner(), handle.getName(), desc, false); + if (handle.isInterface() && tag == H_INVOKEINTERFACE) { + mv.visitMethodInsn(INVOKEINTERFACE, handle.getOwner(), handle.getName(), desc, true); + } else { + mv.visitMethodInsn(Opcodes.H_INVOKESTATIC == tag ? INVOKESTATIC : INVOKEVIRTUAL, handle.getOwner(), handle.getName(), desc, false); + } mv.visitInsn(getReturnType(returnType)); From 3343a26de8c5761664b3174ed6c364a9cf70f86f Mon Sep 17 00:00:00 2001 From: jimcao Date: Wed, 5 Jan 2022 09:12:38 +0800 Subject: [PATCH 2/6] Add interface static test case --- .../demo/lambda/StaticInstanceReference.java | 37 ++++++++++++++++--- .../lambda/StaticInstanceReferenceTest.java | 33 +++++++++++++---- 2 files changed, 57 insertions(+), 13 deletions(-) diff --git a/demo/java-demo/src/main/java/com/alibaba/demo/lambda/StaticInstanceReference.java b/demo/java-demo/src/main/java/com/alibaba/demo/lambda/StaticInstanceReference.java index 41687a9..d6ee1bf 100644 --- a/demo/java-demo/src/main/java/com/alibaba/demo/lambda/StaticInstanceReference.java +++ b/demo/java-demo/src/main/java/com/alibaba/demo/lambda/StaticInstanceReference.java @@ -1,10 +1,7 @@ package com.alibaba.demo.lambda; import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; +import java.util.*; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; @@ -18,13 +15,18 @@ public class StaticInstanceReference { private static final StaticClassA STATIC_CLASS_A = new StaticClassA(); public void staticMethodReference() { - StaticClassA a = new StaticClassA(); - consumesRun(a::doIt); + //StaticClassA a = new StaticClassA(); + //consumesRun(a::doIt); consumesRun(STATIC_CLASS_A::doIt); consumesFunction1(STATIC_CLASS_A::function1); consumesFunction2(STATIC_CLASS_A::function2); consumesFunction3(STATIC_CLASS_A::function3); + + } + + public void collectionInterfaceDefaultOrStatic() { blackHole(invokeInterfaceTest()); + blackHole(interfaceStaticMethodTest()); } public void interfaceDefault() { @@ -33,6 +35,11 @@ public class StaticInstanceReference { consumesFunction1(l::function1); } + public void interfaceStatic() { + consumesRun(ILambda::staticRun); + consumesFunction1(ILambda::staticFunction1); + } + private void consumesRun(Runnable r) { r.run(); } @@ -96,6 +103,16 @@ public class StaticInstanceReference { .reduce(BigDecimal::add); } + public Object interfaceStaticMethodTest() { + List zz = new ArrayList<>(); + zz.add(new String[]{"1"}); + return zz.stream() + .flatMap(Arrays::stream) + .map(Double::valueOf) + .map(BigDecimal::new) + .reduce(BigDecimal::add); + } + private void blackHole(Object... ignore) {} public interface ILambda { @@ -106,6 +123,14 @@ public class StaticInstanceReference { default void function1(String s) { } + + static void staticRun() { + + } + + static void staticFunction1(String s) { + + } } public static class LambdaFoo implements ILambda { diff --git a/demo/java-demo/src/test/java/com/alibaba/demo/lambda/StaticInstanceReferenceTest.java b/demo/java-demo/src/test/java/com/alibaba/demo/lambda/StaticInstanceReferenceTest.java index c5a6bfe..2ca935f 100644 --- a/demo/java-demo/src/test/java/com/alibaba/demo/lambda/StaticInstanceReferenceTest.java +++ b/demo/java-demo/src/test/java/com/alibaba/demo/lambda/StaticInstanceReferenceTest.java @@ -5,6 +5,9 @@ import com.alibaba.testable.core.annotation.MockInvoke; import com.alibaba.testable.core.model.LogLevel; import org.junit.jupiter.api.Test; +import java.util.Collection; +import java.util.stream.Stream; + import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked; @@ -22,24 +25,40 @@ public class StaticInstanceReferenceTest { } @MockInvoke(targetClass = StaticInstanceReference.ILambda.class, targetMethod = "run") - private void mockFooRun() { + private void mockILambdaRun() { } @MockInvoke(targetClass = StaticInstanceReference.ILambda.class, targetMethod = "function1") - private void mockIFunction1(String s) { + private void mockILambdaFunction1(String s) { + } + + @MockInvoke(targetClass = Collection.class, targetMethod = "stream") + Stream mockStream() { + return null; } } @Test - public void shouldMockT1() { + public void shouldMockDoIt() { instance.staticMethodReference(); - verifyInvoked("mockDoIt").withTimes(2); + verifyInvoked("mockDoIt").withTimes(1); } @Test - public void shouldMockFooRun() { + public void shouldMockCollectionStream() { + instance.collectionInterfaceDefaultOrStatic(); + verifyInvoked("mockStream").withTimes(1); + } + + @Test + public void shouldMockInterfaceDefault() { instance.interfaceDefault(); - verifyInvoked("mockFooRun").withTimes(1); - verifyInvoked("mockIFunction1").withTimes(1); + verifyInvoked("mockILambdaRun").withTimes(1); + verifyInvoked("mockILambdaFunction1").withTimes(1); + } + + @Test + public void shouldMockInterfaceStatic() { + instance.interfaceStatic(); } } From 66db3cff7db5189cfba60dc3051a2644b3b2f1de Mon Sep 17 00:00:00 2001 From: jimcao Date: Wed, 5 Jan 2022 09:49:49 +0800 Subject: [PATCH 3/6] temp --- .../java/com/alibaba/demo/lambda/Fzo.java | 30 +++++++++++++++++++ .../demo/lambda/StaticInstanceReference.java | 8 +++++ .../java/com/alibaba/demo/lambda/FzoTest.java | 25 ++++++++++++++++ .../lambda/StaticInstanceReferenceTest.java | 7 ++++- .../agent/handler/SourceClassHandler.java | 8 ++--- 5 files changed, 71 insertions(+), 7 deletions(-) create mode 100644 demo/java-demo/src/main/java/com/alibaba/demo/lambda/Fzo.java create mode 100644 demo/java-demo/src/test/java/com/alibaba/demo/lambda/FzoTest.java diff --git a/demo/java-demo/src/main/java/com/alibaba/demo/lambda/Fzo.java b/demo/java-demo/src/main/java/com/alibaba/demo/lambda/Fzo.java new file mode 100644 index 0000000..edf0b7c --- /dev/null +++ b/demo/java-demo/src/main/java/com/alibaba/demo/lambda/Fzo.java @@ -0,0 +1,30 @@ +package com.alibaba.demo.lambda; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.function.BiFunction; + +/** + * @author jim + */ +public class Fzo { + + public void objectStaticMethodReference() { + consumesFunction3(Boolean::logicalAnd); + //consumesFunction3((v1, v2) -> Boolean.logicalAnd(v1, v2)); + } + + private void consumesFunction3(BiFunction r) { + r.apply(true, true); + } + + public Object zz() { + List> zz = new ArrayList<>(); + zz.add(new ArrayList<>()); + return zz.stream() + .flatMap(Collection::stream) + .reduce(Boolean::logicalAnd); + } + +} diff --git a/demo/java-demo/src/main/java/com/alibaba/demo/lambda/StaticInstanceReference.java b/demo/java-demo/src/main/java/com/alibaba/demo/lambda/StaticInstanceReference.java index d6ee1bf..dd781ed 100644 --- a/demo/java-demo/src/main/java/com/alibaba/demo/lambda/StaticInstanceReference.java +++ b/demo/java-demo/src/main/java/com/alibaba/demo/lambda/StaticInstanceReference.java @@ -113,6 +113,14 @@ public class StaticInstanceReference { .reduce(BigDecimal::add); } + public Object objectStaticMethodReference() { + List> zz = new ArrayList<>(); + zz.add(new ArrayList<>()); + return zz.stream() + .flatMap(Collection::stream) + .reduce(Boolean::logicalAnd); + } + private void blackHole(Object... ignore) {} public interface ILambda { diff --git a/demo/java-demo/src/test/java/com/alibaba/demo/lambda/FzoTest.java b/demo/java-demo/src/test/java/com/alibaba/demo/lambda/FzoTest.java new file mode 100644 index 0000000..4bfe870 --- /dev/null +++ b/demo/java-demo/src/test/java/com/alibaba/demo/lambda/FzoTest.java @@ -0,0 +1,25 @@ +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 FzoTest { + Fzo f = new Fzo(); + @MockDiagnose(LogLevel.VERBOSE) + public static class Mock { + @MockInvoke(targetClass = Double.class, targetMethod = "hashCode") + private int cc() { + return 1; + } + } + + @Test + public void shouldMockInterfaceStatic() { + f.objectStaticMethodReference(); + } +} diff --git a/demo/java-demo/src/test/java/com/alibaba/demo/lambda/StaticInstanceReferenceTest.java b/demo/java-demo/src/test/java/com/alibaba/demo/lambda/StaticInstanceReferenceTest.java index 2ca935f..e4a51d9 100644 --- a/demo/java-demo/src/test/java/com/alibaba/demo/lambda/StaticInstanceReferenceTest.java +++ b/demo/java-demo/src/test/java/com/alibaba/demo/lambda/StaticInstanceReferenceTest.java @@ -18,7 +18,7 @@ public class StaticInstanceReferenceTest { private final StaticInstanceReference instance = new StaticInstanceReference(); - //@MockDiagnose(LogLevel.VERBOSE) + @MockDiagnose(LogLevel.VERBOSE) public static class Mock { @MockInvoke(targetClass = StaticInstanceReference.StaticClassA.class, targetMethod = "doIt") private void mockDoIt() { @@ -57,6 +57,11 @@ public class StaticInstanceReferenceTest { verifyInvoked("mockILambdaFunction1").withTimes(1); } + @Test + public void shouldMockObjectStaticMethodReference() { + instance.objectStaticMethodReference(); + } + @Test public void shouldMockInterfaceStatic() { instance.interfaceStatic(); diff --git a/testable-agent/src/main/java/com/alibaba/testable/agent/handler/SourceClassHandler.java b/testable-agent/src/main/java/com/alibaba/testable/agent/handler/SourceClassHandler.java index 4f3be9b..6f1a980 100644 --- a/testable-agent/src/main/java/com/alibaba/testable/agent/handler/SourceClassHandler.java +++ b/testable-agent/src/main/java/com/alibaba/testable/agent/handler/SourceClassHandler.java @@ -386,7 +386,7 @@ public class SourceClassHandler extends BaseClassHandler { // String s = ""; // consumes(s::contains); //} - boolean external = tag == Opcodes.H_INVOKEVIRTUAL || tag == H_INVOKEINTERFACE; + boolean external = tag == Opcodes.H_INVOKEVIRTUAL || tag == Opcodes.H_INVOKEINTERFACE; boolean isStatic = tag == Opcodes.H_INVOKESTATIC || external; @@ -434,11 +434,7 @@ public class SourceClassHandler extends BaseClassHandler { } // the method call is static ? - if (handle.isInterface() && tag == H_INVOKEINTERFACE) { - mv.visitMethodInsn(INVOKEINTERFACE, handle.getOwner(), handle.getName(), desc, true); - } else { - mv.visitMethodInsn(Opcodes.H_INVOKESTATIC == tag ? INVOKESTATIC : INVOKEVIRTUAL, handle.getOwner(), handle.getName(), desc, false); - } + mv.visitMethodInsn(Opcodes.H_INVOKESTATIC == tag ? INVOKESTATIC : INVOKEVIRTUAL, handle.getOwner(), handle.getName(), desc, false); mv.visitInsn(getReturnType(returnType)); From 0d43d93a292f131bf2a836278a3b50d7d0531b6c Mon Sep 17 00:00:00 2001 From: jimcao Date: Thu, 6 Jan 2022 11:52:18 +0800 Subject: [PATCH 4/6] temp update --- .../java/com/alibaba/demo/lambda/Fzo.java | 20 +-- .../demo/lambda/StaticInstanceReference.java | 17 +-- .../java/com/alibaba/demo/lambda/FzoTest.java | 21 +++ .../lambda/StaticInstanceReferenceTest.java | 7 +- .../agent/handler/SourceClassHandler.java | 128 ++++++++---------- .../alibaba/testable/agent/model/BsmArg.java | 119 ++++++++++++++++ .../testable/agent/util/WrapperUtil.java | 85 ++++++++++++ 7 files changed, 305 insertions(+), 92 deletions(-) create mode 100644 testable-agent/src/main/java/com/alibaba/testable/agent/model/BsmArg.java create mode 100644 testable-agent/src/main/java/com/alibaba/testable/agent/util/WrapperUtil.java diff --git a/demo/java-demo/src/main/java/com/alibaba/demo/lambda/Fzo.java b/demo/java-demo/src/main/java/com/alibaba/demo/lambda/Fzo.java index edf0b7c..399a957 100644 --- a/demo/java-demo/src/main/java/com/alibaba/demo/lambda/Fzo.java +++ b/demo/java-demo/src/main/java/com/alibaba/demo/lambda/Fzo.java @@ -3,7 +3,11 @@ package com.alibaba.demo.lambda; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Optional; import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; /** * @author jim @@ -11,17 +15,17 @@ import java.util.function.BiFunction; public class Fzo { public void objectStaticMethodReference() { - consumesFunction3(Boolean::logicalAnd); - //consumesFunction3((v1, v2) -> Boolean.logicalAnd(v1, v2)); + Boolean aBoolean = zz().get(); + System.out.println(aBoolean); } - private void consumesFunction3(BiFunction r) { - r.apply(true, true); - } - - public Object zz() { + public Optional zz() { List> zz = new ArrayList<>(); - zz.add(new ArrayList<>()); + List f = new ArrayList<>(); + f.add(false); + f.add(false); + f.add(false); + zz.add(f); return zz.stream() .flatMap(Collection::stream) .reduce(Boolean::logicalAnd); diff --git a/demo/java-demo/src/main/java/com/alibaba/demo/lambda/StaticInstanceReference.java b/demo/java-demo/src/main/java/com/alibaba/demo/lambda/StaticInstanceReference.java index dd781ed..5657a2c 100644 --- a/demo/java-demo/src/main/java/com/alibaba/demo/lambda/StaticInstanceReference.java +++ b/demo/java-demo/src/main/java/com/alibaba/demo/lambda/StaticInstanceReference.java @@ -95,14 +95,12 @@ public class StaticInstanceReference { List> zz = new ArrayList<>(); zz.add(new ArrayList<>()); return zz.stream() - //.flatMap(Collection::stream) //.flatMap(v -> v.stream()) .flatMap(Collection::stream) .map(Double::valueOf) .map(BigDecimal::new) .reduce(BigDecimal::add); } - public Object interfaceStaticMethodTest() { List zz = new ArrayList<>(); zz.add(new String[]{"1"}); @@ -113,12 +111,15 @@ public class StaticInstanceReference { .reduce(BigDecimal::add); } - public Object objectStaticMethodReference() { - List> zz = new ArrayList<>(); - zz.add(new ArrayList<>()); - return zz.stream() - .flatMap(Collection::stream) - .reduce(Boolean::logicalAnd); + public void objectStaticMethodReference() { + List f = new ArrayList<>(); + f.add(false); + f.add(false); + f.add(false); + blackHole(f.stream() + .reduce(Boolean::logicalAnd) + .get() + ); } private void blackHole(Object... ignore) {} diff --git a/demo/java-demo/src/test/java/com/alibaba/demo/lambda/FzoTest.java b/demo/java-demo/src/test/java/com/alibaba/demo/lambda/FzoTest.java index 4bfe870..da5bf14 100644 --- a/demo/java-demo/src/test/java/com/alibaba/demo/lambda/FzoTest.java +++ b/demo/java-demo/src/test/java/com/alibaba/demo/lambda/FzoTest.java @@ -5,6 +5,11 @@ 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; + /** * @author jim */ @@ -16,6 +21,22 @@ public class FzoTest { private int cc() { return 1; } + + @MockInvoke(targetClass = Collection.class, targetMethod = "stream") + Stream mockStream() { + List l = new ArrayList<>(); + l.add((E)Boolean.TRUE); + l.add((E)Boolean.TRUE); + l.add((E)Boolean.TRUE); + System.out.println("mockStreammockStreammockStreammockStreammockStreammockStreammockStream"); + return l.stream(); + } + + @MockInvoke(targetClass = Boolean.class, targetMethod = "logicalAnd") + public static boolean mockLogicalAnd(boolean a, boolean b) { + System.out.println("ZPPPPPPPPPPPPPPPPPPPPPP"); + return false; + } } @Test diff --git a/demo/java-demo/src/test/java/com/alibaba/demo/lambda/StaticInstanceReferenceTest.java b/demo/java-demo/src/test/java/com/alibaba/demo/lambda/StaticInstanceReferenceTest.java index e4a51d9..27aaf81 100644 --- a/demo/java-demo/src/test/java/com/alibaba/demo/lambda/StaticInstanceReferenceTest.java +++ b/demo/java-demo/src/test/java/com/alibaba/demo/lambda/StaticInstanceReferenceTest.java @@ -31,11 +31,15 @@ public class StaticInstanceReferenceTest { @MockInvoke(targetClass = StaticInstanceReference.ILambda.class, targetMethod = "function1") private void mockILambdaFunction1(String s) { } - @MockInvoke(targetClass = Collection.class, targetMethod = "stream") Stream mockStream() { return null; } + + @MockInvoke(targetClass = Boolean.class, targetMethod = "logicalAnd") + public static boolean mockLogicalAnd(boolean a, boolean b) { + return false; + } } @Test @@ -60,6 +64,7 @@ public class StaticInstanceReferenceTest { @Test public void shouldMockObjectStaticMethodReference() { instance.objectStaticMethodReference(); + verifyInvoked("mockLogicalAnd").withTimes(2); } @Test diff --git a/testable-agent/src/main/java/com/alibaba/testable/agent/handler/SourceClassHandler.java b/testable-agent/src/main/java/com/alibaba/testable/agent/handler/SourceClassHandler.java index 6f1a980..84e627b 100644 --- a/testable-agent/src/main/java/com/alibaba/testable/agent/handler/SourceClassHandler.java +++ b/testable-agent/src/main/java/com/alibaba/testable/agent/handler/SourceClassHandler.java @@ -1,16 +1,15 @@ 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.agent.util.WrapperUtil; 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; @@ -341,7 +340,7 @@ public class SourceClassHandler extends BaseClassHandler { return Opcodes.INVOKEVIRTUAL == opcode && ClassUtil.isCompanionClassName(ownerClass); } - private void setFinalValue(Field ownerField, Object obj, Object value) throws Exception { + public static void setFinalValue(Field ownerField, Object obj, Object value) throws Exception { ownerField.setAccessible(true); Field modifiersField = Field.class.getDeclaredField("modifiers"); modifiersField.setAccessible(true); @@ -349,77 +348,48 @@ public class SourceClassHandler extends BaseClassHandler { ownerField.set(obj, value); } - private List fetchInvokeDynamicHandle(MethodNode mn) { - List handleList = new ArrayList(); + private List fetchInvokeDynamicHandle(MethodNode mn) { + List handleList = new ArrayList(); 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 invokeDynamicList = new ArrayList(); + List invokeDynamicList = new ArrayList(); for (MethodNode method : cn.methods) { - List handleList = fetchInvokeDynamicHandle(method); + List 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 || tag == Opcodes.H_INVOKEINTERFACE; + boolean isStatic = bsmArg.isStatic(); + Handle handle = bsmArg.getHandle(); + Type handleDesc = bsmArg.getHandleDesc(); + Type methodDesc = bsmArg.getMethodDesc(); - 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); + MethodVisitor mv = cn.visitMethod(isStatic ? ACC_PUBLIC + ACC_STATIC : ACC_PUBLIC, lambdaName, handleDesc.getDescriptor(), null, null); mv.visitCode(); Label l0 = new Label(); @@ -428,46 +398,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(); + Type[] methodArgs = methodDesc.getArgumentTypes(); + for (int i = 0; i < argumentTypes.length; i++) { + String arg = argumentTypes[i].getDescriptor(); + String methodArg = methodArgs[i].getDescriptor(); + + WrapperUtil.Boxed boxed = WrapperUtil.wrapper2Primitive(methodArg, arg); + if (boxed != null) { + mv.visitVarInsn(getLoadType(methodArg), isStatic ? i : i + 1); + mv.visitMethodInsn(INVOKEVIRTUAL, boxed.getOwner(), boxed.getW2pMethod(), boxed.getW2pMethodDesc(), false); + } else { + mv.visitVarInsn(getLoadType(arg), isStatic ? i : i + 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()); + } + + WrapperUtil.Boxed boxed = WrapperUtil.primitive2Wrapper(handleDesc.getReturnType().getDescriptor(), methodDesc.getReturnType().getDescriptor()); + if (boxed != null) { + mv.visitMethodInsn(INVOKESTATIC, boxed.getOwner(), boxed.getP2wMethod(), boxed.getP2wMethodDesc(), false); + mv.visitInsn(getReturnType(methodDesc.getReturnType().getDescriptor())); + } else { + mv.visitInsn(getReturnType(handleDesc.getReturnType().getDescriptor())); + } - mv.visitInsn(getReturnType(returnType)); 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; - } + for (int i = 0; i < argumentTypes.length; i++) { + String localVar = argumentTypes[i].getDescriptor(); // add local var mv.visitLocalVariable(String.format("o%d", i), localVar, null, l0, l1, i); } } 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; - } - + for (int i = 0; i < argumentTypes.length; i++) { + String localVar = argumentTypes[i].getDescriptor(); mv.visitLocalVariable(String.format("o%d", i), localVar, null, l0, l1, i + 1); } } @@ -475,7 +451,9 @@ public class SourceClassHandler extends BaseClassHandler { mv.visitMaxs(-1, -1); mv.visitEnd(); - try { + bsmArg.complete(cn.name, lambdaName); + /*try { + // modify handle to the generation method setFinalValue(handle.getClass().getDeclaredField("name"), handle, lambdaName); // mark: should merge the below two if. @@ -488,7 +466,7 @@ public class SourceClassHandler extends BaseClassHandler { setFinalValue(handle.getClass().getDeclaredField("tag"), handle, H_INVOKESTATIC); } } catch (Exception ignore) { - } + }*/ } } diff --git a/testable-agent/src/main/java/com/alibaba/testable/agent/model/BsmArg.java b/testable-agent/src/main/java/com/alibaba/testable/agent/model/BsmArg.java new file mode 100644 index 0000000..0bdee65 --- /dev/null +++ b/testable-agent/src/main/java/com/alibaba/testable/agent/model/BsmArg.java @@ -0,0 +1,119 @@ +package com.alibaba.testable.agent.model; + +import org.objectweb.asm.Handle; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +/** + * @author jim + */ +public class BsmArg { + private Type handleDesc; + private Type methodDesc; + + private Handle handle; + + private Object[] bsmArgs; + + private 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 "(" + join("", handleArgs) + ")" + handleDesc.getReturnType().getDescriptor(); + } + + private 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(); + } + + + 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, methodDesc.getDescriptor(), false); + + //bsmArgs[1] = new Handle(isStatic()? Opcodes.H_INVOKESTATIC : Opcodes.H_INVOKEVIRTUAL, owner, methodName, handleDesc.getDescriptor(), false); + Handle h = (Handle) bsmArgs[1]; + try { + setFinalValue(Handle.class.getDeclaredField("tag"), h, isStatic()? Opcodes.H_INVOKESTATIC : Opcodes.H_INVOKEVIRTUAL); + setFinalValue(Handle.class.getDeclaredField("owner"), h, owner); + setFinalValue(Handle.class.getDeclaredField("name"), h, methodName); + setFinalValue(Handle.class.getDeclaredField("descriptor"), h, handleDesc.getDescriptor()); + setFinalValue(Handle.class.getDeclaredField("isInterface"), h, false); + + }catch (Exception e) { + e.printStackTrace(); + } + } + + public static 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); + } +} diff --git a/testable-agent/src/main/java/com/alibaba/testable/agent/util/WrapperUtil.java b/testable-agent/src/main/java/com/alibaba/testable/agent/util/WrapperUtil.java new file mode 100644 index 0000000..396c213 --- /dev/null +++ b/testable-agent/src/main/java/com/alibaba/testable/agent/util/WrapperUtil.java @@ -0,0 +1,85 @@ +package com.alibaba.testable.agent.util; + +import com.alibaba.testable.agent.model.BasicType; +import com.alibaba.testable.agent.model.WrapperType; + +/** + * @author jim + */ +public class WrapperUtil { + + public static Boxed wrapper2Primitive(String from, String to) { + /*if ("Z".equals(to) && "Ljava/lang/Boolean;".equals(from)) { + return Boxed.BOOL; + }*/ + return null; + } + + public static Boxed primitive2Wrapper(String from, String to) { + /*if ("Ljava/lang/Boolean;".equals(to) && "Z".equals(from)) { + return Boxed.BOOL; + }*/ + return null; + } + + + public enum Boxed { + + BOOL(boolean.class, Boolean.class, "java/lang/Boolean", "booleanValue", "()Z", "valueOf", "(Z)Ljava/lang/Boolean;"), + BYTE(byte.class, Byte.class, "java/lang/Byte","byteValue", "()B", "valueOf", "(B)Ljava/lang/Byte;"), + SHORT(short.class, Short.class, "java/lang/Short","shortValue", "()S", "valueOf", "(S)Ljava/lang/Short;"), + /*CHAR(boolean.class, Boolean.class, "booleanValue", "()Z", "valueOf", "(Z)Ljava/lang/Boolean;"), + BOOL(boolean.class, Boolean.class, "booleanValue", "()Z", "valueOf", "(Z)Ljava/lang/Boolean;"), + BOOL(boolean.class, Boolean.class, "booleanValue", "()Z", "valueOf", "(Z)Ljava/lang/Boolean;"), + BOOL(boolean.class, Boolean.class, "booleanValue", "()Z", "valueOf", "(Z)Ljava/lang/Boolean;"), + BOOL(boolean.class, Boolean.class, "booleanValue", "()Z", "valueOf", "(Z)Ljava/lang/Boolean;"), + BYTE(byte.class, Byte.class, "byteValue", "()B", "valueOf", "(B)Ljava/lang/Byte;")*/; + + private Class primitive; + private Class wrapper; + private String owner; + private String w2pMethod; + private String w2pMethodDesc; + + private String p2wMethod; + private String p2wMethodDesc; + + Boxed(Class primitive, Class wrapper, String owner, String w2pMethod, String w2pMethodDesc, String p2wMethod, String p2wMethodDesc) { + this.primitive = primitive; + this.wrapper = wrapper; + this.owner = owner; + this.w2pMethod = w2pMethod; + this.w2pMethodDesc = w2pMethodDesc; + this.p2wMethod = p2wMethod; + this.p2wMethodDesc = p2wMethodDesc; + } + + public Class getPrimitive() { + return primitive; + } + + public Class getWrapper() { + return wrapper; + } + + public String getOwner() { + return owner; + } + + public String getW2pMethod() { + return w2pMethod; + } + + public String getW2pMethodDesc() { + return w2pMethodDesc; + } + + public String getP2wMethod() { + return p2wMethod; + } + + public String getP2wMethodDesc() { + return p2wMethodDesc; + } + } +} From 17b31b39dddac1b9593ecb9c26af3772d0ed5ab6 Mon Sep 17 00:00:00 2001 From: jimcao Date: Thu, 6 Jan 2022 13:13:09 +0800 Subject: [PATCH 5/6] refine code --- .../com/alibaba/demo/lambda/BaseDemo.java | 42 +++++++ .../java/com/alibaba/demo/lambda/Fzo.java | 34 ------ .../demo/lambda/InvokeInterfaceDemo.java | 84 +++++++++++++ ...LambdaDemo.java => InvokeVirtualDemo.java} | 6 +- .../demo/lambda/StaticInstanceReference.java | 110 +----------------- .../java/com/alibaba/demo/lambda/FzoTest.java | 46 -------- .../demo/lambda/InvokeInterfaceDemoTest.java | 72 ++++++++++++ ...moTest.java => InvokeVirtualDemoTest.java} | 6 +- .../lambda/StaticInstanceReferenceTest.java | 44 ------- 9 files changed, 205 insertions(+), 239 deletions(-) create mode 100644 demo/java-demo/src/main/java/com/alibaba/demo/lambda/BaseDemo.java delete mode 100644 demo/java-demo/src/main/java/com/alibaba/demo/lambda/Fzo.java create mode 100644 demo/java-demo/src/main/java/com/alibaba/demo/lambda/InvokeInterfaceDemo.java rename demo/java-demo/src/main/java/com/alibaba/demo/lambda/{ExternalLambdaDemo.java => InvokeVirtualDemo.java} (96%) delete mode 100644 demo/java-demo/src/test/java/com/alibaba/demo/lambda/FzoTest.java create mode 100644 demo/java-demo/src/test/java/com/alibaba/demo/lambda/InvokeInterfaceDemoTest.java rename demo/java-demo/src/test/java/com/alibaba/demo/lambda/{ExternalLambdaDemoTest.java => InvokeVirtualDemoTest.java} (90%) diff --git a/demo/java-demo/src/main/java/com/alibaba/demo/lambda/BaseDemo.java b/demo/java-demo/src/main/java/com/alibaba/demo/lambda/BaseDemo.java new file mode 100644 index 0000000..e92d917 --- /dev/null +++ b/demo/java-demo/src/main/java/com/alibaba/demo/lambda/BaseDemo.java @@ -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 void consumesFunction1(Consumer f) { + f.accept(null); + } + + default void consumesFunction2(Function f) { + f.apply(null); + } + + default void consumesFunction3(BiFunction f) { + f.apply(null, null); + } + + default void consumesSupplier(Supplier supplier) { + supplier.get(); + } + + default void consumesTwoFunction2(Function f1, Function f2) { + f1.apply(null); + f2.apply(null); + } +} diff --git a/demo/java-demo/src/main/java/com/alibaba/demo/lambda/Fzo.java b/demo/java-demo/src/main/java/com/alibaba/demo/lambda/Fzo.java deleted file mode 100644 index 399a957..0000000 --- a/demo/java-demo/src/main/java/com/alibaba/demo/lambda/Fzo.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.alibaba.demo.lambda; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Optional; -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Stream; - -/** - * @author jim - */ -public class Fzo { - - public void objectStaticMethodReference() { - Boolean aBoolean = zz().get(); - System.out.println(aBoolean); - } - - public Optional zz() { - List> zz = new ArrayList<>(); - List f = new ArrayList<>(); - f.add(false); - f.add(false); - f.add(false); - zz.add(f); - return zz.stream() - .flatMap(Collection::stream) - .reduce(Boolean::logicalAnd); - } - -} diff --git a/demo/java-demo/src/main/java/com/alibaba/demo/lambda/InvokeInterfaceDemo.java b/demo/java-demo/src/main/java/com/alibaba/demo/lambda/InvokeInterfaceDemo.java new file mode 100644 index 0000000..0ea3e09 --- /dev/null +++ b/demo/java-demo/src/main/java/com/alibaba/demo/lambda/InvokeInterfaceDemo.java @@ -0,0 +1,84 @@ +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> testList = new ArrayList<>(); + List fooList = new ArrayList<>(); + 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 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 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 { + + } +} diff --git a/demo/java-demo/src/main/java/com/alibaba/demo/lambda/ExternalLambdaDemo.java b/demo/java-demo/src/main/java/com/alibaba/demo/lambda/InvokeVirtualDemo.java similarity index 96% rename from demo/java-demo/src/main/java/com/alibaba/demo/lambda/ExternalLambdaDemo.java rename to demo/java-demo/src/main/java/com/alibaba/demo/lambda/InvokeVirtualDemo.java index b9fa289..5883af5 100644 --- a/demo/java-demo/src/main/java/com/alibaba/demo/lambda/ExternalLambdaDemo.java +++ b/demo/java-demo/src/main/java/com/alibaba/demo/lambda/InvokeVirtualDemo.java @@ -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) { diff --git a/demo/java-demo/src/main/java/com/alibaba/demo/lambda/StaticInstanceReference.java b/demo/java-demo/src/main/java/com/alibaba/demo/lambda/StaticInstanceReference.java index 5657a2c..b85f056 100644 --- a/demo/java-demo/src/main/java/com/alibaba/demo/lambda/StaticInstanceReference.java +++ b/demo/java-demo/src/main/java/com/alibaba/demo/lambda/StaticInstanceReference.java @@ -10,50 +10,15 @@ import java.util.stream.Collectors; /** * @author jim */ -public class StaticInstanceReference { +public class StaticInstanceReference implements BaseDemo { private static final StaticClassA STATIC_CLASS_A = new StaticClassA(); public void staticMethodReference() { - //StaticClassA a = new StaticClassA(); - //consumesRun(a::doIt); consumesRun(STATIC_CLASS_A::doIt); consumesFunction1(STATIC_CLASS_A::function1); consumesFunction2(STATIC_CLASS_A::function2); consumesFunction3(STATIC_CLASS_A::function3); - - } - - public void collectionInterfaceDefaultOrStatic() { - blackHole(invokeInterfaceTest()); - blackHole(interfaceStaticMethodTest()); - } - - public void interfaceDefault() { - ILambda l = new LambdaFoo(); - consumesRun(l::run); - consumesFunction1(l::function1); - } - - public void interfaceStatic() { - consumesRun(ILambda::staticRun); - consumesFunction1(ILambda::staticFunction1); - } - - private void consumesRun(Runnable r) { - r.run(); - } - - private void consumesFunction1(Consumer r) { - r.accept(null); - } - - private void consumesFunction2(Function r) { - r.apply(null); - } - - private void consumesFunction3(BiFunction r) { - r.apply(null, null); } public static class StaticClassA { @@ -72,77 +37,4 @@ public class StaticInstanceReference { 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 testList = Collections.emptyList(); - //noinspection RedundantOperationOnEmptyContainer - List response = testList.stream().map(XBean::getId).distinct().collect(Collectors.toList()); - } - - public Object invokeInterfaceTest() { - List> zz = new ArrayList<>(); - zz.add(new ArrayList<>()); - return zz.stream() - //.flatMap(v -> v.stream()) - .flatMap(Collection::stream) - .map(Double::valueOf) - .map(BigDecimal::new) - .reduce(BigDecimal::add); - } - public Object interfaceStaticMethodTest() { - List zz = new ArrayList<>(); - zz.add(new String[]{"1"}); - return zz.stream() - .flatMap(Arrays::stream) - .map(Double::valueOf) - .map(BigDecimal::new) - .reduce(BigDecimal::add); - } - - public void objectStaticMethodReference() { - List f = new ArrayList<>(); - f.add(false); - f.add(false); - f.add(false); - blackHole(f.stream() - .reduce(Boolean::logicalAnd) - .get() - ); - } - - private void blackHole(Object... ignore) {} - - 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 { - - } } diff --git a/demo/java-demo/src/test/java/com/alibaba/demo/lambda/FzoTest.java b/demo/java-demo/src/test/java/com/alibaba/demo/lambda/FzoTest.java deleted file mode 100644 index da5bf14..0000000 --- a/demo/java-demo/src/test/java/com/alibaba/demo/lambda/FzoTest.java +++ /dev/null @@ -1,46 +0,0 @@ -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; - -/** - * @author jim - */ -public class FzoTest { - Fzo f = new Fzo(); - @MockDiagnose(LogLevel.VERBOSE) - public static class Mock { - @MockInvoke(targetClass = Double.class, targetMethod = "hashCode") - private int cc() { - return 1; - } - - @MockInvoke(targetClass = Collection.class, targetMethod = "stream") - Stream mockStream() { - List l = new ArrayList<>(); - l.add((E)Boolean.TRUE); - l.add((E)Boolean.TRUE); - l.add((E)Boolean.TRUE); - System.out.println("mockStreammockStreammockStreammockStreammockStreammockStreammockStream"); - return l.stream(); - } - - @MockInvoke(targetClass = Boolean.class, targetMethod = "logicalAnd") - public static boolean mockLogicalAnd(boolean a, boolean b) { - System.out.println("ZPPPPPPPPPPPPPPPPPPPPPP"); - return false; - } - } - - @Test - public void shouldMockInterfaceStatic() { - f.objectStaticMethodReference(); - } -} diff --git a/demo/java-demo/src/test/java/com/alibaba/demo/lambda/InvokeInterfaceDemoTest.java b/demo/java-demo/src/test/java/com/alibaba/demo/lambda/InvokeInterfaceDemoTest.java new file mode 100644 index 0000000..c80ca76 --- /dev/null +++ b/demo/java-demo/src/test/java/com/alibaba/demo/lambda/InvokeInterfaceDemoTest.java @@ -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") + Stream mockStream() { + List 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(); + } +} diff --git a/demo/java-demo/src/test/java/com/alibaba/demo/lambda/ExternalLambdaDemoTest.java b/demo/java-demo/src/test/java/com/alibaba/demo/lambda/InvokeVirtualDemoTest.java similarity index 90% rename from demo/java-demo/src/test/java/com/alibaba/demo/lambda/ExternalLambdaDemoTest.java rename to demo/java-demo/src/test/java/com/alibaba/demo/lambda/InvokeVirtualDemoTest.java index 9c84911..32a03a2 100644 --- a/demo/java-demo/src/test/java/com/alibaba/demo/lambda/ExternalLambdaDemoTest.java +++ b/demo/java-demo/src/test/java/com/alibaba/demo/lambda/InvokeVirtualDemoTest.java @@ -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; } diff --git a/demo/java-demo/src/test/java/com/alibaba/demo/lambda/StaticInstanceReferenceTest.java b/demo/java-demo/src/test/java/com/alibaba/demo/lambda/StaticInstanceReferenceTest.java index 27aaf81..9e760e5 100644 --- a/demo/java-demo/src/test/java/com/alibaba/demo/lambda/StaticInstanceReferenceTest.java +++ b/demo/java-demo/src/test/java/com/alibaba/demo/lambda/StaticInstanceReferenceTest.java @@ -5,9 +5,6 @@ import com.alibaba.testable.core.annotation.MockInvoke; import com.alibaba.testable.core.model.LogLevel; import org.junit.jupiter.api.Test; -import java.util.Collection; -import java.util.stream.Stream; - import static com.alibaba.testable.core.matcher.InvocationVerifier.verifyInvoked; @@ -23,23 +20,6 @@ public class StaticInstanceReferenceTest { @MockInvoke(targetClass = StaticInstanceReference.StaticClassA.class, targetMethod = "doIt") private void mockDoIt() { } - - @MockInvoke(targetClass = StaticInstanceReference.ILambda.class, targetMethod = "run") - private void mockILambdaRun() { - } - - @MockInvoke(targetClass = StaticInstanceReference.ILambda.class, targetMethod = "function1") - private void mockILambdaFunction1(String s) { - } - @MockInvoke(targetClass = Collection.class, targetMethod = "stream") - Stream mockStream() { - return null; - } - - @MockInvoke(targetClass = Boolean.class, targetMethod = "logicalAnd") - public static boolean mockLogicalAnd(boolean a, boolean b) { - return false; - } } @Test @@ -47,28 +27,4 @@ public class StaticInstanceReferenceTest { instance.staticMethodReference(); verifyInvoked("mockDoIt").withTimes(1); } - - @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(); - } } From 635cbf62fb3a7cfeaa68dc008dac92faa719564f Mon Sep 17 00:00:00 2001 From: jimcao Date: Thu, 6 Jan 2022 15:13:32 +0800 Subject: [PATCH 6/6] completed --- .../demo/lambda/CollectionListCodeDemo.java | 121 ++++++++++++++++++ .../demo/lambda/InvokeInterfaceDemo.java | 2 + .../lambda/CollectionListCodeDemoTest.java | 28 ++++ .../demo/lambda/InvokeInterfaceDemoTest.java | 2 +- .../lambda/StaticInstanceReferenceTest.java | 2 +- .../agent/handler/SourceClassHandler.java | 111 +++------------- .../alibaba/testable/agent/model/BsmArg.java | 45 +------ .../testable/agent/util/StringUtil.java | 11 ++ .../testable/agent/util/WrapperUtil.java | 85 ------------ 9 files changed, 189 insertions(+), 218 deletions(-) create mode 100644 demo/java-demo/src/main/java/com/alibaba/demo/lambda/CollectionListCodeDemo.java create mode 100644 demo/java-demo/src/test/java/com/alibaba/demo/lambda/CollectionListCodeDemoTest.java delete mode 100644 testable-agent/src/main/java/com/alibaba/testable/agent/util/WrapperUtil.java diff --git a/demo/java-demo/src/main/java/com/alibaba/demo/lambda/CollectionListCodeDemo.java b/demo/java-demo/src/main/java/com/alibaba/demo/lambda/CollectionListCodeDemo.java new file mode 100644 index 0000000..530bfbd --- /dev/null +++ b/demo/java-demo/src/main/java/com/alibaba/demo/lambda/CollectionListCodeDemo.java @@ -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 userList = getUsers(); + double d = userList.stream() + .map(v -> v) + .map(v -> Function.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 map = userList.stream() + .filter(User::isActive) + .collect(Collectors.toMap(User::getId, Function.identity(), (v1, v2) -> v1)); + blackHole(map); + + List 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 getUsers() { + List 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; + } + } +} diff --git a/demo/java-demo/src/main/java/com/alibaba/demo/lambda/InvokeInterfaceDemo.java b/demo/java-demo/src/main/java/com/alibaba/demo/lambda/InvokeInterfaceDemo.java index 0ea3e09..82726d7 100644 --- a/demo/java-demo/src/main/java/com/alibaba/demo/lambda/InvokeInterfaceDemo.java +++ b/demo/java-demo/src/main/java/com/alibaba/demo/lambda/InvokeInterfaceDemo.java @@ -30,6 +30,8 @@ public class InvokeInterfaceDemo implements BaseDemo { public Object collectionStreamTest() { List> testList = new ArrayList<>(); List fooList = new ArrayList<>(); + fooList.add("123"); + fooList.add("456"); testList.add(fooList); return testList.stream() //.flatMap(v -> v.stream()) diff --git a/demo/java-demo/src/test/java/com/alibaba/demo/lambda/CollectionListCodeDemoTest.java b/demo/java-demo/src/test/java/com/alibaba/demo/lambda/CollectionListCodeDemoTest.java new file mode 100644 index 0000000..754cf3a --- /dev/null +++ b/demo/java-demo/src/test/java/com/alibaba/demo/lambda/CollectionListCodeDemoTest.java @@ -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(); + } +} diff --git a/demo/java-demo/src/test/java/com/alibaba/demo/lambda/InvokeInterfaceDemoTest.java b/demo/java-demo/src/test/java/com/alibaba/demo/lambda/InvokeInterfaceDemoTest.java index c80ca76..a185be1 100644 --- a/demo/java-demo/src/test/java/com/alibaba/demo/lambda/InvokeInterfaceDemoTest.java +++ b/demo/java-demo/src/test/java/com/alibaba/demo/lambda/InvokeInterfaceDemoTest.java @@ -20,7 +20,7 @@ public class InvokeInterfaceDemoTest { private final InvokeInterfaceDemo instance = new InvokeInterfaceDemo(); - @MockDiagnose(LogLevel.VERBOSE) + //@MockDiagnose(LogLevel.VERBOSE) public static class Mock { @MockInvoke(targetClass = InvokeInterfaceDemo.ILambda.class, targetMethod = "run") diff --git a/demo/java-demo/src/test/java/com/alibaba/demo/lambda/StaticInstanceReferenceTest.java b/demo/java-demo/src/test/java/com/alibaba/demo/lambda/StaticInstanceReferenceTest.java index 9e760e5..c539c35 100644 --- a/demo/java-demo/src/test/java/com/alibaba/demo/lambda/StaticInstanceReferenceTest.java +++ b/demo/java-demo/src/test/java/com/alibaba/demo/lambda/StaticInstanceReferenceTest.java @@ -15,7 +15,7 @@ public class StaticInstanceReferenceTest { private final StaticInstanceReference instance = new StaticInstanceReference(); - @MockDiagnose(LogLevel.VERBOSE) + //@MockDiagnose(LogLevel.VERBOSE) public static class Mock { @MockInvoke(targetClass = StaticInstanceReference.StaticClassA.class, targetMethod = "doIt") private void mockDoIt() { diff --git a/testable-agent/src/main/java/com/alibaba/testable/agent/handler/SourceClassHandler.java b/testable-agent/src/main/java/com/alibaba/testable/agent/handler/SourceClassHandler.java index 84e627b..0b4f1d5 100644 --- a/testable-agent/src/main/java/com/alibaba/testable/agent/handler/SourceClassHandler.java +++ b/testable-agent/src/main/java/com/alibaba/testable/agent/handler/SourceClassHandler.java @@ -7,13 +7,10 @@ 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.agent.util.WrapperUtil; import com.alibaba.testable.core.util.LogUtil; 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; @@ -63,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 memberInjectMethods, - Set newOperatorInjectMethods, ClassNode cn) { + Set newOperatorInjectMethods) { LogUtil.verbose(" Found method %s", mn.name); if (mn.name.startsWith("$")) { // skip methods e.g. "$jacocoInit" @@ -340,14 +337,6 @@ public class SourceClassHandler extends BaseClassHandler { return Opcodes.INVOKEVIRTUAL == opcode && ClassUtil.isCompanionClassName(ownerClass); } - public static 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 fetchInvokeDynamicHandle(MethodNode mn) { List handleList = new ArrayList(); for (AbstractInsnNode instruction : mn.instructions) { @@ -385,10 +374,8 @@ public class SourceClassHandler extends BaseClassHandler { boolean isStatic = bsmArg.isStatic(); Handle handle = bsmArg.getHandle(); Type handleDesc = bsmArg.getHandleDesc(); - Type methodDesc = bsmArg.getMethodDesc(); - - String lambdaName = String.format("Lambda$_%s_%d", handle.getName(), atomicInteger.incrementAndGet()); + 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(); @@ -399,109 +386,51 @@ public class SourceClassHandler extends BaseClassHandler { mv.visitVarInsn(ALOAD, 0); } Type[] argumentTypes = handleDesc.getArgumentTypes(); - Type[] methodArgs = methodDesc.getArgumentTypes(); - for (int i = 0; i < argumentTypes.length; i++) { - String arg = argumentTypes[i].getDescriptor(); - String methodArg = methodArgs[i].getDescriptor(); - - WrapperUtil.Boxed boxed = WrapperUtil.wrapper2Primitive(methodArg, arg); - if (boxed != null) { - mv.visitVarInsn(getLoadType(methodArg), isStatic ? i : i + 1); - mv.visitMethodInsn(INVOKEVIRTUAL, boxed.getOwner(), boxed.getW2pMethod(), boxed.getW2pMethodDesc(), false); - } else { - mv.visitVarInsn(getLoadType(arg), isStatic ? i : i + 1); - } + 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 ? 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()); } - WrapperUtil.Boxed boxed = WrapperUtil.primitive2Wrapper(handleDesc.getReturnType().getDescriptor(), methodDesc.getReturnType().getDescriptor()); - if (boxed != null) { - mv.visitMethodInsn(INVOKESTATIC, boxed.getOwner(), boxed.getP2wMethod(), boxed.getP2wMethodDesc(), false); - mv.visitInsn(getReturnType(methodDesc.getReturnType().getDescriptor())); - } else { - mv.visitInsn(getReturnType(handleDesc.getReturnType().getDescriptor())); - } - + 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 < argumentTypes.length; i++) { - String localVar = argumentTypes[i].getDescriptor(); - - // 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 < argumentTypes.length; i++) { - String localVar = argumentTypes[i].getDescriptor(); - 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(); bsmArg.complete(cn.name, lambdaName); - /*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) { - }*/ } } - 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) { diff --git a/testable-agent/src/main/java/com/alibaba/testable/agent/model/BsmArg.java b/testable-agent/src/main/java/com/alibaba/testable/agent/model/BsmArg.java index 0bdee65..f5dce2d 100644 --- a/testable-agent/src/main/java/com/alibaba/testable/agent/model/BsmArg.java +++ b/testable-agent/src/main/java/com/alibaba/testable/agent/model/BsmArg.java @@ -1,12 +1,10 @@ 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; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; - /** * @author jim */ @@ -16,9 +14,9 @@ public class BsmArg { private Handle handle; - private Object[] bsmArgs; + private final Object[] bsmArgs; - private String originalHandleDesc; + private final String originalHandleDesc; public BsmArg(Object[] bsmArgs) { this.bsmArgs = bsmArgs; @@ -56,21 +54,9 @@ public class BsmArg { handleArgs[i] = handleArguments[i].getDescriptor(); } - return "(" + join("", handleArgs) + ")" + handleDesc.getReturnType().getDescriptor(); + return "(" + StringUtil.join("", handleArgs) + ")" + handleDesc.getReturnType().getDescriptor(); } - private 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(); - } - - public Type getHandleDesc() { return handleDesc; } @@ -93,27 +79,6 @@ public class BsmArg { } public void complete(String owner, String methodName) { - //bsmArgs[1] = new Handle(isStatic()? Opcodes.H_INVOKESTATIC : Opcodes.H_INVOKEVIRTUAL, owner, methodName, methodDesc.getDescriptor(), false); - - //bsmArgs[1] = new Handle(isStatic()? Opcodes.H_INVOKESTATIC : Opcodes.H_INVOKEVIRTUAL, owner, methodName, handleDesc.getDescriptor(), false); - Handle h = (Handle) bsmArgs[1]; - try { - setFinalValue(Handle.class.getDeclaredField("tag"), h, isStatic()? Opcodes.H_INVOKESTATIC : Opcodes.H_INVOKEVIRTUAL); - setFinalValue(Handle.class.getDeclaredField("owner"), h, owner); - setFinalValue(Handle.class.getDeclaredField("name"), h, methodName); - setFinalValue(Handle.class.getDeclaredField("descriptor"), h, handleDesc.getDescriptor()); - setFinalValue(Handle.class.getDeclaredField("isInterface"), h, false); - - }catch (Exception e) { - e.printStackTrace(); - } - } - - public static 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); + bsmArgs[1] = new Handle(isStatic()? Opcodes.H_INVOKESTATIC : Opcodes.H_INVOKEVIRTUAL, owner, methodName, handleDesc.getDescriptor(), false); } } diff --git a/testable-agent/src/main/java/com/alibaba/testable/agent/util/StringUtil.java b/testable-agent/src/main/java/com/alibaba/testable/agent/util/StringUtil.java index 4e2619b..d2e1615 100644 --- a/testable-agent/src/main/java/com/alibaba/testable/agent/util/StringUtil.java +++ b/testable-agent/src/main/java/com/alibaba/testable/agent/util/StringUtil.java @@ -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(); + } + } diff --git a/testable-agent/src/main/java/com/alibaba/testable/agent/util/WrapperUtil.java b/testable-agent/src/main/java/com/alibaba/testable/agent/util/WrapperUtil.java deleted file mode 100644 index 396c213..0000000 --- a/testable-agent/src/main/java/com/alibaba/testable/agent/util/WrapperUtil.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.alibaba.testable.agent.util; - -import com.alibaba.testable.agent.model.BasicType; -import com.alibaba.testable.agent.model.WrapperType; - -/** - * @author jim - */ -public class WrapperUtil { - - public static Boxed wrapper2Primitive(String from, String to) { - /*if ("Z".equals(to) && "Ljava/lang/Boolean;".equals(from)) { - return Boxed.BOOL; - }*/ - return null; - } - - public static Boxed primitive2Wrapper(String from, String to) { - /*if ("Ljava/lang/Boolean;".equals(to) && "Z".equals(from)) { - return Boxed.BOOL; - }*/ - return null; - } - - - public enum Boxed { - - BOOL(boolean.class, Boolean.class, "java/lang/Boolean", "booleanValue", "()Z", "valueOf", "(Z)Ljava/lang/Boolean;"), - BYTE(byte.class, Byte.class, "java/lang/Byte","byteValue", "()B", "valueOf", "(B)Ljava/lang/Byte;"), - SHORT(short.class, Short.class, "java/lang/Short","shortValue", "()S", "valueOf", "(S)Ljava/lang/Short;"), - /*CHAR(boolean.class, Boolean.class, "booleanValue", "()Z", "valueOf", "(Z)Ljava/lang/Boolean;"), - BOOL(boolean.class, Boolean.class, "booleanValue", "()Z", "valueOf", "(Z)Ljava/lang/Boolean;"), - BOOL(boolean.class, Boolean.class, "booleanValue", "()Z", "valueOf", "(Z)Ljava/lang/Boolean;"), - BOOL(boolean.class, Boolean.class, "booleanValue", "()Z", "valueOf", "(Z)Ljava/lang/Boolean;"), - BOOL(boolean.class, Boolean.class, "booleanValue", "()Z", "valueOf", "(Z)Ljava/lang/Boolean;"), - BYTE(byte.class, Byte.class, "byteValue", "()B", "valueOf", "(B)Ljava/lang/Byte;")*/; - - private Class primitive; - private Class wrapper; - private String owner; - private String w2pMethod; - private String w2pMethodDesc; - - private String p2wMethod; - private String p2wMethodDesc; - - Boxed(Class primitive, Class wrapper, String owner, String w2pMethod, String w2pMethodDesc, String p2wMethod, String p2wMethodDesc) { - this.primitive = primitive; - this.wrapper = wrapper; - this.owner = owner; - this.w2pMethod = w2pMethod; - this.w2pMethodDesc = w2pMethodDesc; - this.p2wMethod = p2wMethod; - this.p2wMethodDesc = p2wMethodDesc; - } - - public Class getPrimitive() { - return primitive; - } - - public Class getWrapper() { - return wrapper; - } - - public String getOwner() { - return owner; - } - - public String getW2pMethod() { - return w2pMethod; - } - - public String getW2pMethodDesc() { - return w2pMethodDesc; - } - - public String getP2wMethod() { - return p2wMethod; - } - - public String getP2wMethodDesc() { - return p2wMethodDesc; - } - } -}