feat: support debug mode

This commit is contained in:
ReaJason
2024-12-09 20:24:58 +08:00
parent d7a40f74b8
commit c72ae586a2
13 changed files with 281 additions and 68 deletions
@@ -0,0 +1,65 @@
package com.reajason.javaweb.buddy;
import lombok.SneakyThrows;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledOnJre;
import java.lang.reflect.InaccessibleObjectException;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.condition.JRE.JAVA_17;
/**
* @author ReaJason
* @since 2024/12/7
*/
class ByPassJavaModuleInterceptorTest {
static class TestClass {
static {
System.out.println("TestClass");
}
public TestClass() {
}
public String hello() {
return "hello";
}
}
@Test
@SneakyThrows
@EnabledOnJre(JAVA_17)
void testByPassModule() {
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
assertThrows(InaccessibleObjectException.class, () -> {
defineClass.setAccessible(true);
});
ByPassJavaModuleInterceptor.enter(this.getClass());
assertDoesNotThrow(() -> {
defineClass.setAccessible(true);
});
}
@Test
@SneakyThrows
void test() {
DynamicType.Builder<?> builder = new ByteBuddy()
.redefine(TestClass.class)
.name("com.reajason.javaweb.buddy.ByPassJdkModuleInterceptorTest$TestClass1");
builder = ByPassJavaModuleInterceptor.extend(builder);
try (DynamicType.Unloaded<?> make = builder.make()) {
byte[] bytes = make.getBytes();
Files.write(Paths.get("build", "classes", "TestClass1.class"), bytes);
Class<?> loaded = make.load(Thread.currentThread().getContextClassLoader()).getLoaded();
assertNotNull(loaded.getDeclaredMethod("byPassJdkModule"));
}
}
}
@@ -1,44 +0,0 @@
package com.reajason.javaweb.buddy;
import lombok.SneakyThrows;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import org.junit.jupiter.api.Test;
import java.nio.file.Files;
import java.nio.file.Paths;
/**
* @author ReaJason
* @since 2024/12/7
*/
class ByPassJdkModuleInterceptorTest {
static class TestClass {
static {
System.out.println("TestClass");
}
public TestClass() {
}
public String hello() {
return "hello";
}
}
@Test
@SneakyThrows
void test() {
DynamicType.Builder<?> builder = new ByteBuddy()
.redefine(TestClass.class)
.name("com.reajason.javaweb.buddy.ByPassJdkModuleInterceptorTest$TestClass1");
builder = ByPassJdkModuleInterceptor.extend(builder);
try (DynamicType.Unloaded<?> make = builder.make()) {
byte[] bytes = make.getBytes();
Files.write(Paths.get("build", "classes", "TestClass1.class"), bytes);
}
}
}
@@ -0,0 +1,112 @@
package com.reajason.javaweb.buddy;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.asm.AsmVisitorWrapper;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.jar.asm.MethodVisitor;
import net.bytebuddy.jar.asm.Opcodes;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.pool.TypePool;
import org.junit.jupiter.api.Test;
import java.nio.file.Files;
import java.nio.file.Paths;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.Mockito.*;
/**
* @author ReaJason
* @since 2024/12/4
*/
@Slf4j
class LogRemoveVisitorWrapperTest {
@Test
void testExtend() {
DynamicType.Builder<?> builder = new ByteBuddy().subclass(Object.class);
DynamicType.Builder<?> extendedBuilder = LogRemoveMethodVisitor.extend(builder);
assertNotNull(extendedBuilder);
assertNotEquals(builder, extendedBuilder);
}
@Test
void testWrap() {
LogRemoveMethodVisitor visitor = LogRemoveMethodVisitor.INSTANCE;
TypeDescription instrumentedType = mock(TypeDescription.class);
MethodDescription instrumentedMethod = mock(MethodDescription.class);
MethodVisitor methodVisitor = mock(MethodVisitor.class);
Implementation.Context implementationContext = mock(Implementation.Context.class);
TypePool typePool = mock(TypePool.class);
MethodVisitor wrappedVisitor = visitor.wrap(instrumentedType, instrumentedMethod, methodVisitor,
implementationContext, typePool, 0, 0);
assertNotNull(wrappedVisitor);
assertNotEquals(methodVisitor, wrappedVisitor);
}
@Test
void testVisitMethodInsn_RemoveSystemOutPrintln() {
MethodVisitor methodVisitor = mock(MethodVisitor.class);
LogRemoveMethodVisitor.INSTANCE.wrap(null, null, methodVisitor, null, null, 0, 0)
.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
verify(methodVisitor, never()).visitMethodInsn(anyInt(), anyString(), anyString(), anyString(), anyBoolean());
}
@Test
void testVisitMethodInsn_RemovePrintStackTrace() {
MethodVisitor methodVisitor = mock(MethodVisitor.class);
LogRemoveMethodVisitor.INSTANCE.wrap(null, null, methodVisitor, null, null, 0, 0)
.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Exception", "printStackTrace", "()V", false);
verify(methodVisitor, never()).visitMethodInsn(anyInt(), anyString(), anyString(), anyString(), anyBoolean());
}
@Test
void testVisitMethodInsn_KeepOtherMethodCalls() {
MethodVisitor methodVisitor = mock(MethodVisitor.class);
MethodVisitor wrappedVisitor = LogRemoveMethodVisitor.INSTANCE.wrap(null, null, methodVisitor, null, null, 0, 0);
wrappedVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "length", "()I", false);
verify(methodVisitor).visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "length", "()I", false);
}
public static class TestClass {
public TestClass() {
}
public void methodWithLogs() {
System.out.println("This should be removed");
String test = "test";
int length = test.length();
try {
System.out.println("hello");
throw new RuntimeException("hello");
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Test
void testIntegration() throws Exception {
// Use ByteBuddy to create a new class with log statements removed
DynamicType.Unloaded<TestClass> make = new ByteBuddy()
.redefine(TestClass.class)
.name("com.reajason.javaweb.buddy.TestClass1")
.visit(new AsmVisitorWrapper.ForDeclaredMethods()
.method(ElementMatchers.any(), LogRemoveMethodVisitor.INSTANCE))
.make();
byte[] bytes = make.getBytes();
Files.write(Paths.get("xx.class"), bytes);
Class<?> modifiedClass = make.load(getClass().getClassLoader()).getLoaded();
Object instance = modifiedClass.getDeclaredConstructor().newInstance();
modifiedClass.getMethod("methodWithLogs").invoke(instance);
}
}