package org.jbehave.core.configuration.groovy; import groovy.lang.GroovyClassLoader; import groovyjarjarasm.asm.ClassWriter; import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.control.CompilationUnit; import org.codehaus.groovy.control.SourceUnit; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.HashMap; import java.util.Map; /** * Groovy does not cache the bytecode sequences for generated classes. * BytecodeReadingParanamer needs these to get paramater names from classes The * Groovy compiler does create the debug tables, and they are the same as the * ones made for a native Java class, so this derived GroovyClassLoader fills in * for the missing functionality from the base GroovyClassLoader. * * Groovy allows a mechanism via a system property to force the dump of bytecode * to a (temp) directory, but caching the bytecode avoids having to clean up * temp directories after the run. */ public class BytecodeGroovyClassLoader extends GroovyClassLoader { private Map<String, byte[]> classBytes = new HashMap<String, byte[]>(); @Override public InputStream getResourceAsStream(String name) { if (classBytes.containsKey(name)) { return new ByteArrayInputStream(classBytes.get(name)); } return super.getResourceAsStream(name); } @Override protected ClassCollector createCollector(CompilationUnit unit, SourceUnit su) { // These six lines copied from Groovy itself, with the intention to // return a subclass InnerLoader loader = AccessController.doPrivileged(new PrivilegedAction<InnerLoader>() { public InnerLoader run() { return new InnerLoader(BytecodeGroovyClassLoader.this); } }); return new BytecodeClassCollector(classBytes, loader, unit, su); } public static class BytecodeClassCollector extends ClassCollector { private final Map<String, byte[]> classBytes; public BytecodeClassCollector(Map<String, byte[]> classBytes, InnerLoader loader, CompilationUnit unit, SourceUnit su) { super(loader, unit, su); this.classBytes = classBytes; } @Override protected Class<?> onClassNode(ClassWriter classWriter, ClassNode classNode) { classBytes.put(classNode.getName() + ".class", classWriter.toByteArray()); return super.onClassNode(classWriter, classNode); } } }