Skip to content

Commit

Permalink
Merge pull request #1528 from newrelic/1219_Scala_Try_Not_Present_Exc…
Browse files Browse the repository at this point in the history
…eption

Resolve missing class exception on Scala instrumentation.
  • Loading branch information
jbedell-newrelic authored Oct 17, 2023
2 parents cad7865 + 2a920a1 commit 94b344a
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import com.newrelic.agent.instrumentation.classmatchers.OptimizedClassMatcher;
import com.newrelic.agent.instrumentation.context.ContextClassTransformer;
import com.newrelic.agent.instrumentation.context.InstrumentationContext;
import com.newrelic.agent.util.asm.CustomClassLoaderClassWriter;
import com.newrelic.agent.util.asm.PatchedClassWriter;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
Expand Down Expand Up @@ -34,7 +36,7 @@ private byte[] doTransform(ClassLoader loader, String className, Class<?> classB

LOG.debug("Instrumenting class " + className);
ClassReader reader = new ClassReader(classfileBuffer);
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
ClassWriter writer = new CustomClassLoaderClassWriter(ClassWriter.COMPUTE_FRAMES, loader);
ClassVisitor cv = writer;
cv = new ScalaTraitFinalFieldVisitor(cv, context.getScalaFinalFields());
reader.accept(cv, ClassReader.SKIP_FRAMES);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.newrelic.agent.util.asm;

import com.newrelic.agent.Agent;
import com.newrelic.agent.bridge.AgentBridge;
import org.objectweb.asm.ClassReader;

import java.io.IOException;
import java.util.logging.Level;

public class CustomClassLoaderClassWriter extends PatchedClassWriter {

private final ClassLoader classLoader;
private final ClassResolver classResolver;

public CustomClassLoaderClassWriter(int flags, ClassLoader classLoader) {
super(flags, classLoader);
this.classLoader = classLoader;
this.classResolver = ClassResolvers.getClassLoaderResolver(classLoader == null ?
AgentBridge.getAgent().getClass().getClassLoader() : classLoader);
}

private Class loadClass(String type) throws ClassNotFoundException {
Class result = null;
try {
// try the custom classloader first
result = classLoader.loadClass(type);
} catch (ClassNotFoundException e) {
Agent.LOG.log(Level.FINEST, "class not found in custom classloader: "+type);
try {
// now try the base classloader
result = this.getClass().getClassLoader().loadClass(type);
} catch (ClassNotFoundException e2) {
Agent.LOG.log(Level.FINEST, "class not found in base classloader: "+type);
try {
// if all else fails, let's try the hard way
// this case exists because of continued TypeNotPresentExceptions when instrumenting Scala
ClassReader classReader = getClassReader(type);
if (classReader != null) {
result = classReader.getClass();
}
} catch (IOException ioe) {
Agent.LOG.log(Level.FINEST, ioe.toString(), ioe);
throw new ClassNotFoundException("Could not find class via ClassReader: "+type);
}
}
}

return result;
}

protected String getCommonSuperClass(String type1, String type2) {
Class class1;
try {
class1 = loadClass(type1);
} catch (ClassNotFoundException e) {
throw new TypeNotPresentException(type1, e);
}

Class class2;
try {
class2 = loadClass(type2);
} catch (ClassNotFoundException e) {
throw new TypeNotPresentException(type2, e);
}

if (class1 == null || class2 == null) {
return JAVA_LANG_OBJECT;
}

if (class1.isAssignableFrom(class2)) {
return type1;
} else if (class2.isAssignableFrom(class1)) {
return type2;
} else if (!class1.isInterface() && !class2.isInterface()) {
do {
class1 = class1.getSuperclass();
} while(!class1.isAssignableFrom(class2));

return class1.getName().replace('.', '/');
} else {
return "java/lang/Object";
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
* structure without actually loading any classes.
*/
public class PatchedClassWriter extends ClassWriter {
private static final String JAVA_LANG_OBJECT = "java/lang/Object";
static final String JAVA_LANG_OBJECT = "java/lang/Object";
protected final ClassResolver classResolver;

public PatchedClassWriter(int flags, ClassLoader classLoader) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.newrelic.agent.util.asm;

import org.junit.Assert;
import org.junit.Test;
import org.objectweb.asm.ClassWriter;

import java.io.Serializable;

public class CustomClassLoaderClassWriterTest {

CustomClassLoaderClassWriter api = new CustomClassLoaderClassWriter(ClassWriter.COMPUTE_FRAMES, this.getClass().getClassLoader());

@Test
public void test_getCommonSuperClass_sameClass() {
String className = CustomClassLoaderClassWriterTest.class.getName();
Assert.assertEquals(className, api.getCommonSuperClass(className, className));
}

@Test
public void test_getCommonSuperClass_commonSuper() {
Assert.assertEquals(Object.class.getName().replace('.','/'),
api.getCommonSuperClass(String.class.getName(), Integer.class.getName()));
}

@Test
public void test_getCommonSuperClass_commonSuper2() {
Assert.assertEquals(Number.class.getName(),
api.getCommonSuperClass(Integer.class.getName(), Number.class.getName()));
}

@Test
public void test_getCommonSuperClass_interfaces() {
Assert.assertEquals(Object.class.getName().replace('.','/'),
api.getCommonSuperClass(Comparable.class.getName(), Serializable.class.getName()));
}

@Test
public void test_getCommonSuperClass_noCommonSuper() {
Assert.assertEquals(Object.class.getName().replace('.','/'),
api.getCommonSuperClass(String.class.getName(), CustomClassLoaderClassWriterTest.class.getName()));
}

@Test
public void test_getCommonSuperClass_invalidClass1() {
Assert.assertEquals(Object.class.getName().replace('.', '/'),
api.getCommonSuperClass("noexist1", CustomClassLoaderClassWriterTest.class.getName()));
}

@Test
public void test_getCommonSuperClass_invalidClass2() {
Assert.assertEquals(Object.class.getName().replace('.', '/'),
api.getCommonSuperClass(CustomClassLoaderClassWriterTest.class.getName(), "noexist2"));
}

}

0 comments on commit 94b344a

Please sign in to comment.