diff options
author | Nada Amin <namin@alum.mit.edu> | 2012-09-28 01:32:44 +0200 |
---|---|---|
committer | Nada Amin <namin@alum.mit.edu> | 2012-09-28 01:45:25 +0200 |
commit | ddcf5cea60245ee8d41e248feabe901df609bcf4 (patch) | |
tree | 786c51850a2953a1694cc90d7aed49f23179ddb1 | |
parent | ae1871bb1c0480e5b055b83f2db08f49ef3c30cf (diff) | |
download | scala-ddcf5cea60245ee8d41e248feabe901df609bcf4.tar.gz scala-ddcf5cea60245ee8d41e248feabe901df609bcf4.tar.bz2 scala-ddcf5cea60245ee8d41e248feabe901df609bcf4.zip |
Fix class loader issues in instrumentation tests.
The ASM ClassWriter uses a wimpy class loader when computing common
superclasses. This could cause a ClassNotFoundException in the
transform method (at reader.accept). This exception gets swallowed,
resulting in a class that should be instrumented to silently not
be. The fix is to override getCommonSuperClass to use the correct
class loader.
Trivia: This bug was discovered while 'stress-testing' this
instrumentation scheme on the Coursera students, to check that they
implement one method in terms of another in the assignment.
-rw-r--r-- | src/partest/scala/tools/partest/javaagent/ASMTransformer.java | 30 |
1 files changed, 28 insertions, 2 deletions
diff --git a/src/partest/scala/tools/partest/javaagent/ASMTransformer.java b/src/partest/scala/tools/partest/javaagent/ASMTransformer.java index 09cd485d6b..a9a56d124d 100644 --- a/src/partest/scala/tools/partest/javaagent/ASMTransformer.java +++ b/src/partest/scala/tools/partest/javaagent/ASMTransformer.java @@ -26,9 +26,35 @@ public class ASMTransformer implements ClassFileTransformer { className.startsWith("instrumented/")); } - public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { + public byte[] transform(final ClassLoader classLoader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { if (shouldTransform(className)) { - ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); + ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) { + // this is copied verbatim from the superclass, + // except that we use the outer class loader + @Override protected String getCommonSuperClass(final String type1, final String type2) { + Class<?> c, d; + try { + c = Class.forName(type1.replace('/', '.'), false, classLoader); + d = Class.forName(type2.replace('/', '.'), false, classLoader); + } catch (Exception e) { + throw new RuntimeException(e.toString()); + } + if (c.isAssignableFrom(d)) { + return type1; + } + if (d.isAssignableFrom(c)) { + return type2; + } + if (c.isInterface() || d.isInterface()) { + return "java/lang/Object"; + } else { + do { + c = c.getSuperclass(); + } while (!c.isAssignableFrom(d)); + return c.getName().replace('.', '/'); + } + } + }; ProfilerVisitor visitor = new ProfilerVisitor(writer); ClassReader reader = new ClassReader(classfileBuffer); reader.accept(visitor, 0); |