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; - } - } -}