/*
* Copyright 2016 NAVER Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.navercorp.pinpoint.profiler.instrument;
import com.navercorp.pinpoint.bootstrap.instrument.InstrumentContext;
import com.navercorp.pinpoint.bootstrap.instrument.aspect.Aspect;
import com.navercorp.pinpoint.profiler.util.JavaAssistUtils;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class ASMClassNodeAdapterTest {
private final InstrumentContext pluginContext = mock(InstrumentContext.class);
@Before
public void setUp() {
when(pluginContext.injectClass(any(ClassLoader.class), any(String.class))).thenAnswer(new Answer<Class<?>>() {
@Override
public Class<?> answer(InvocationOnMock invocation) throws Throwable {
ClassLoader loader = (ClassLoader) invocation.getArguments()[0];
String name = (String) invocation.getArguments()[1];
return loader.loadClass(name);
}
});
when(pluginContext.getResourceAsStream(any(ClassLoader.class), any(String.class))).thenAnswer(new Answer<InputStream>() {
@Override
public InputStream answer(InvocationOnMock invocation) throws Throwable {
ClassLoader loader = (ClassLoader) invocation.getArguments()[0];
String name = (String) invocation.getArguments()[1];
if(loader == null) {
loader = ClassLoader.getSystemClassLoader();
}
return loader.getResourceAsStream(name);
}
});
}
@Test
public void get() {
final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
ASMClassNodeAdapter adapter = ASMClassNodeAdapter.get(pluginContext, classLoader, "com/navercorp/pinpoint/profiler/instrument/mock/BaseClass");
assertNotNull(adapter);
adapter = ASMClassNodeAdapter.get(pluginContext, classLoader, "com/navercorp/pinpoint/profiler/instrument/mock/NotExistClass");
assertNull(adapter);
// skip code
adapter = ASMClassNodeAdapter.get(pluginContext, classLoader, "com/navercorp/pinpoint/profiler/instrument/mock/BaseClass", true);
try {
adapter.getDeclaredMethods();
fail("can't throw IllegalStateException");
} catch(Exception ignored) {
}
try {
adapter.getDeclaredMethod("base", "()");
fail("can't throw IllegalStateException");
} catch(Exception ignored) {
}
}
@Test
public void getter() {
final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
ASMClassNodeAdapter adapter = ASMClassNodeAdapter.get(pluginContext, classLoader, "com/navercorp/pinpoint/profiler/instrument/mock/ExtendedClass");
// name
assertEquals("com/navercorp/pinpoint/profiler/instrument/mock/ExtendedClass", adapter.getInternalName());
assertEquals("com.navercorp.pinpoint.profiler.instrument.mock.ExtendedClass", adapter.getName());
assertEquals("com/navercorp/pinpoint/profiler/instrument/mock/BaseClass", adapter.getSuperClassInternalName());
assertEquals("com.navercorp.pinpoint.profiler.instrument.mock.BaseClass", adapter.getSuperClassName());
assertEquals(false, adapter.isInterface());
assertEquals(false, adapter.isAnnotation());
assertEquals(0, adapter.getInterfaceNames().length);
// method
List<ASMMethodNodeAdapter> methods = adapter.getDeclaredMethods();
assertEquals(1, methods.size());
ASMMethodNodeAdapter method = adapter.getDeclaredMethod("extended", "()");
assertEquals("extended", method.getName());
method = adapter.getDeclaredMethod("notExist", "()");
assertNull(method);
// field
ASMFieldNodeAdapter field = adapter.getField("e", null);
assertEquals("e", field.getName());
field = adapter.getField("e", "Ljava/lang/String;");
assertEquals("e", field.getName());
assertEquals("Ljava/lang/String;", field.getDesc());
field = adapter.getField("notExist", null);
assertNull(field);
// interface
adapter = ASMClassNodeAdapter.get(pluginContext, classLoader, "com/navercorp/pinpoint/profiler/instrument/mock/BaseInterface");
assertEquals(true, adapter.isInterface());
// implement
adapter = ASMClassNodeAdapter.get(pluginContext, classLoader, "com/navercorp/pinpoint/profiler/instrument/mock/BaseImplementClass");
String[] interfaceNames = adapter.getInterfaceNames();
assertEquals(1, interfaceNames.length);
assertEquals("com.navercorp.pinpoint.profiler.instrument.mock.BaseInterface", interfaceNames[0]);
// annotation
adapter = ASMClassNodeAdapter.get(pluginContext, classLoader, "com/navercorp/pinpoint/bootstrap/instrument/aspect/Aspect");
assertEquals(true, adapter.isAnnotation());
}
@Test
public void addGetter() throws Exception {
final String targetClassName = "com.navercorp.pinpoint.profiler.instrument.mock.BaseClass";
final String getterMethodName = "_$PINPOINT$_getValue";
ASMClassNodeLoader.TestClassLoader classLoader = ASMClassNodeLoader.getClassLoader();
classLoader.setTargetClassName(targetClassName);
classLoader.setCallbackHandler(new ASMClassNodeLoader.CallbackHandler() {
@Override
public void handle(ClassNode classNode) {
ASMClassNodeAdapter adapter = new ASMClassNodeAdapter(pluginContext, null, classNode);
ASMFieldNodeAdapter field = adapter.getField("i", null);
adapter.addGetterMethod(getterMethodName, field);
}
});
Class<?> clazz = classLoader.loadClass(targetClassName);
Method method = clazz.getDeclaredMethod(getterMethodName);
assertEquals(0, method.invoke(clazz.newInstance()));
}
@Test
public void addSetter() throws Exception {
final String targetClassName = "com.navercorp.pinpoint.profiler.instrument.mock.BaseClass";
final String setterMethodName = "_$PINPOINT$_setValue";
ASMClassNodeLoader.TestClassLoader classLoader = ASMClassNodeLoader.getClassLoader();
classLoader.setTargetClassName(targetClassName);
classLoader.setCallbackHandler(new ASMClassNodeLoader.CallbackHandler() {
@Override
public void handle(ClassNode classNode) {
ASMClassNodeAdapter adapter = new ASMClassNodeAdapter(pluginContext, null, classNode);
ASMFieldNodeAdapter field = adapter.getField("i", null);
adapter.addSetterMethod(setterMethodName, field);
}
});
Class<?> clazz = classLoader.loadClass(targetClassName);
Method method = clazz.getDeclaredMethod(setterMethodName, int.class);
method.invoke(clazz.newInstance(), 10);
}
@Test
public void addField() throws Exception {
final String targetClassName = "com.navercorp.pinpoint.profiler.instrument.mock.BaseClass";
final String accessorClassName = "com.navercorp.pinpoint.profiler.instrument.mock.BaseAccessor";
final ASMClassNodeLoader.TestClassLoader classLoader = ASMClassNodeLoader.getClassLoader();
classLoader.setTargetClassName(targetClassName);
classLoader.setCallbackHandler(new ASMClassNodeLoader.CallbackHandler() {
@Override
public void handle(ClassNode classNode) {
ASMClassNodeAdapter classNodeAdapter = new ASMClassNodeAdapter(pluginContext, null, classNode);
classNodeAdapter.addField("_$PINPOINT$_" + JavaAssistUtils.javaClassNameToVariableName(accessorClassName), int.class);
classNodeAdapter.addInterface(accessorClassName);
ASMFieldNodeAdapter fieldNode = classNodeAdapter.getField("_$PINPOINT$_" + JavaAssistUtils.javaClassNameToVariableName(accessorClassName), null);
classNodeAdapter.addGetterMethod("_$PINPOINT$_getTraceInt", fieldNode);
classNodeAdapter.addSetterMethod("_$PINPOINT$_setTraceInt", fieldNode);
}
});
Class<?> clazz = classLoader.loadClass(targetClassName);
Object instance = clazz.newInstance();
Method setMethod = clazz.getDeclaredMethod("_$PINPOINT$_setTraceInt", int.class);
setMethod.invoke(instance, 10);
Method getMethod = clazz.getDeclaredMethod("_$PINPOINT$_getTraceInt");
int result = (Integer) getMethod.invoke(instance);
System.out.println(result);
}
@Test
public void hasAnnotation() throws Exception {
ASMClassNodeAdapter classNodeAdapter = ASMClassNodeAdapter.get(pluginContext, null, "com/navercorp/pinpoint/profiler/instrument/mock/AnnotationClass");
Assert.assertTrue(classNodeAdapter.hasAnnotation(Aspect.class));
Assert.assertFalse(classNodeAdapter.hasAnnotation(Override.class));
}
@Test
public void addMethod() throws Exception {
final MethodNode methodNode = ASMClassNodeLoader.get("com.navercorp.pinpoint.profiler.instrument.mock.ArgsClass", "arg");
final ASMMethodNodeAdapter adapter = new ASMMethodNodeAdapter("com/navercorp/pinpoint/profiler/instrument/mock/ArgsClass", methodNode);
final ASMClassNodeLoader.TestClassLoader testClassLoader = ASMClassNodeLoader.getClassLoader();
final String targetClassName = "com.navercorp.pinpoint.profiler.instrument.mock.BaseClass";
testClassLoader.setTargetClassName(targetClassName);
testClassLoader.setCallbackHandler(new ASMClassNodeLoader.CallbackHandler() {
@Override
public void handle(ClassNode classNode) {
ASMClassNodeAdapter classNodeAdapter = new ASMClassNodeAdapter(pluginContext, null, classNode);
classNodeAdapter.copyMethod(adapter);
}
});
Class<?> clazz = testClassLoader.loadClass(targetClassName);
Method method = clazz.getDeclaredMethod("arg");
method.invoke(clazz.newInstance());
}
@Test
public void subclassOf() {
ASMClassNodeAdapter adapter = ASMClassNodeAdapter.get(pluginContext, null, "com/navercorp/pinpoint/profiler/instrument/mock/ExtendedClass");
// self
assertEquals(true, adapter.subclassOf("com/navercorp/pinpoint/profiler/instrument/mock/ExtendedClass"));
// super
assertEquals(true, adapter.subclassOf("com/navercorp/pinpoint/profiler/instrument/mock/BaseClass"));
assertEquals(true, adapter.subclassOf("java/lang/Object"));
// not
assertEquals(false, adapter.subclassOf("com/navercorp/pinpoint/profiler/instrument/mock/NormalClass"));
}
}