Package net.sourceforge.javautil.bytecode

Source Code of net.sourceforge.javautil.bytecode.BytecodeCompiler$Loader

package net.sourceforge.javautil.bytecode;

import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.util.ASMifierClassVisitor;

import net.sourceforge.javautil.bytecode.api.BytecodeResolutionPool;
import net.sourceforge.javautil.bytecode.api.IBytecodeResolvable;
import net.sourceforge.javautil.bytecode.api.BytecodeResolutionPool.Linkable;
import net.sourceforge.javautil.bytecode.api.TypeMemberAccess.Scope;
import net.sourceforge.javautil.bytecode.api.type.AbstractType;
import net.sourceforge.javautil.bytecode.api.type.BytecodeContextType;
import net.sourceforge.javautil.bytecode.api.type.IBytecodeWriterType;
import net.sourceforge.javautil.bytecode.api.type.JavaClassAbstract;
import net.sourceforge.javautil.bytecode.api.type.JavaClassConcrete;
import net.sourceforge.javautil.bytecode.api.type.JavaInterface;
import net.sourceforge.javautil.common.logging.ILogger;
import net.sourceforge.javautil.common.logging.LoggerLevelStandard;
import net.sourceforge.javautil.common.logging.LoggingContext;
import net.sourceforge.javautil.common.version.IVersion;
import net.sourceforge.javautil.common.version.Version;

/**
* This will allow creation of {@link Class}'s from dynamically generated bytecode.
*
* @author elponderador
* @author $Author$
* @version $Id$
*/
public class BytecodeCompiler {
 
  private static final ILogger log = LoggingContext.getLogger(BytecodeCompiler.class);
 
  /**
   * The different versions of the bytecode/class file specification.
   *
   * @author elponderador
   * @author $Author$
   * @version $Id$
   */
  public enum Version implements IVersion<Version> {
   
    Version1_1("1.1"),
   
    Version1_2("1.2"),
   
    Version1_3("1.3"),
   
    Version1_4("1.4"),
   
    Version1_5("1.5"),
   
    Version6("6.0"),
   
    Version7("7.0");
   
    public static Version getJVMVersion () {
      String version = System.getProperty("java.class.version");
      if ("196653".equals(version)) return Version.Version1_1;
      if ("46.0".equals(version)) return Version.Version1_2;
      if ("47.0".equals(version)) return Version.Version1_3;
      if ("48.0".equals(version)) return Version.Version1_4;
      if ("49.0".equals(version)) return Version.Version1_5;
      if ("50.0".equals(version)) return Version.Version6;
      if ("51.0".equals(version)) return Version.Version7;
     
      throw new RuntimeException("Could not determine current JVM class version");
    }
   
    private final IVersion version;
   
    /**
     * @param version The string form of the version
     */
    private Version(String version) {
      this.version = net.sourceforge.javautil.common.version.Version.decode(version);
    }

    public int getMajorVersion() {
      return version.getMajorVersion();
    }

    public int getMicroVersion() {
      return version.getMicroVersion();
    }

    public int getMinorVersion() {
      return version.getMinorVersion();
    }

    public String getSuffix() {
      return version.getSuffix();
    }
   
  }
 
  protected final IBytecodeFactory factory;
  protected final BytecodeResolutionPool pool;
  protected final Loader loader;
 
  protected final Version defaultVersion;
 
  public BytecodeCompiler(IBytecodeFactory factory) {
    this(Thread.currentThread().getContextClassLoader(), factory);
  }
 
  public BytecodeCompiler(ClassLoader parent, IBytecodeFactory factory) {
    this(parent, factory, Version.getJVMVersion());
  }
 
  public BytecodeCompiler(ClassLoader parent, IBytecodeFactory factory, Version defaultVersion) {
    this.factory = factory;
    this.defaultVersion = defaultVersion;
    this.loader = new Loader(parent);
    this.pool = new BytecodeResolutionPool(loader);
  }
 
  public JavaClassAbstract createAbstractClass (String name, Scope scope, boolean isStatic, boolean isFinal) {
    return new JavaClassAbstract(this, name, scope, isStatic, isFinal);
  }
 
  public JavaClassConcrete createConcreteClass (String name, Scope scope, boolean isStatic, boolean isFinal) {
    return new JavaClassConcrete(this, name, scope, isStatic, isFinal);
  }
 
  public JavaInterface createJavaInterface (String name, Scope scope, boolean isStatic, boolean isFinal) {
    return new JavaInterface(this, name, scope, isStatic, isFinal);
  }

  /**
   * @return The factory being used by this compiler
   */
  public IBytecodeFactory getFactory() { return factory; }

  /**
   * @return The pool being used by this compiler
   */
  public BytecodeResolutionPool getPool() { return pool; }

  /**
   * @return The default version that will be used at compile time
   */
  public Version getDefaultVersion() { return defaultVersion; }

  /**
   * This assumes the use of the {@link #getDefaultVersion()}.
   *
   * @see #compile(AbstractType, Version)
   */
  public Class compile (AbstractType type) { return compile(type, defaultVersion); }
 
  /**
   * @param type The type to compile
   * @param version The version of bytecode/class file format to generate
   * @return The compiled class
   */
  public Class compile (AbstractType type, Version version) {
    BytecodeContextType ctx = this.factory.createTypeContext(type, pool, version);
    type.write(ctx);

    byte[] bytecode = ctx.getGeneratedBytecode();
   
    if (log.isLogging(LoggerLevelStandard.DEBUG)) {
      ClassReader cr = new ClassReader(bytecode);
      ASMifierClassVisitor asm = new ASMifierClassVisitor(
        new PrintWriter(log.getLoggingWriter(LoggerLevelStandard.DEBUG), true) // log.getLoggingWriter(LoggerLevelStandard.INFO), true)
      );
      cr.accept(asm, 0);
    }
   
    return loader.define(type.getName(), bytecode);
  }

  /**
   * @param handler The handler for the proxy
   * @param interfaces The interfaces (generated/visible by this compiler)
   * @return The proxy
   */
  public Object createProxy (InvocationHandler handler, Class... interfaces) {
    return Proxy.newProxyInstance(loader, interfaces, handler);
  }
 
  /**
   * @return The class loader used by this compiler
   */
  protected Loader getLoader () {
    return this.loader;
  }
 
  /**
   * Internal loader for class creation.
   *
   * @author elponderador
   * @author $Author$
   * @version $Id$
   */
  private class Loader extends ClassLoader {
   
    public Loader(ClassLoader parent) {
      super(parent);
    }

    /**
     * @param name The name of the class
     * @param bytecode The bytecode for the class
     * @return The created class
     */
    public Class define (String name, byte[] bytecode) {
      Class defined = this.defineClass(name, bytecode, 0, bytecode.length);
      pool.clear(name);
      return defined;
    }

    @Override protected Class<?> findClass(String name) throws ClassNotFoundException {
      try {
        return super.findClass(name);
      } catch (ClassNotFoundException e) {
        IBytecodeResolvable resolved = pool.resolve(name);
        if (resolved != null) {
          if (resolved instanceof AbstractType) {
            return compile( (AbstractType) resolved );
          } else {
            return ((Linkable)resolved).getWrapped();
          }
        } else
          throw e;
      }
    }
   
  }

}
TOP

Related Classes of net.sourceforge.javautil.bytecode.BytecodeCompiler$Loader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.