BytecodeGroovyClassLoader.java

  1. package org.jbehave.core.configuration.groovy;

  2. import java.io.ByteArrayInputStream;
  3. import java.io.InputStream;
  4. import java.security.AccessController;
  5. import java.security.PrivilegedAction;
  6. import java.util.HashMap;
  7. import java.util.Map;

  8. import org.codehaus.groovy.ast.ClassNode;
  9. import org.codehaus.groovy.control.CompilationUnit;
  10. import org.codehaus.groovy.control.SourceUnit;

  11. import groovy.lang.GroovyClassLoader;
  12. import groovyjarjarasm.asm.ClassWriter;

  13. /**
  14.  * Groovy does not cache the bytecode sequences for generated classes. BytecodeReadingParanamer needs these to get
  15.  * paramater names from classes The Groovy compiler does create the debug tables, and they are the same as the ones made
  16.  * for a native Java class, so this derived GroovyClassLoader fills in for the missing functionality from the base
  17.  * GroovyClassLoader.
  18.  *
  19.  * <p>Groovy allows a mechanism via a system property to force the dump of bytecode to a (temp) directory, but caching
  20.  * the bytecode avoids having to clean up temp directories after the run.</p>
  21.  */
  22. public class BytecodeGroovyClassLoader extends GroovyClassLoader {

  23.     private Map<String, byte[]> classBytes = new HashMap<>();

  24.     @Override
  25.     public InputStream getResourceAsStream(String name) {
  26.         if (classBytes.containsKey(name)) {
  27.             return new ByteArrayInputStream(classBytes.get(name));
  28.         }
  29.         return super.getResourceAsStream(name);
  30.     }

  31.     @Override
  32.     protected ClassCollector createCollector(CompilationUnit unit, SourceUnit su) {
  33.         // These six lines copied from Groovy itself, with the intention to
  34.         // return a subclass
  35.         InnerLoader loader = AccessController.doPrivileged(new PrivilegedAction<InnerLoader>() {
  36.             @Override
  37.             public InnerLoader run() {
  38.                 return new InnerLoader(BytecodeGroovyClassLoader.this);
  39.             }
  40.         });
  41.         return new BytecodeClassCollector(classBytes, loader, unit, su);
  42.     }

  43.     public static class BytecodeClassCollector extends ClassCollector {
  44.         private final Map<String, byte[]> classBytes;

  45.         public BytecodeClassCollector(Map<String, byte[]> classBytes, InnerLoader loader, CompilationUnit unit,
  46.                 SourceUnit su) {
  47.             super(loader, unit, su);
  48.             this.classBytes = classBytes;
  49.         }

  50.         @Override
  51.         protected Class<?> onClassNode(ClassWriter classWriter, ClassNode classNode) {
  52.             classBytes.put(classNode.getName() + ".class", classWriter.toByteArray());
  53.             return super.onClassNode(classWriter, classNode);
  54.         }
  55.     }

  56. }