Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   ***** BEGIN LICENSE BLOCK *****
   * Version: CPL 1.0/GPL 2.0/LGPL 2.1
   *
   * The contents of this file are subject to the Common Public
   * License Version 1.0 (the "License"); you may not use this file
   * except in compliance with the License. You may obtain a copy of
   * the License at http://www.eclipse.org/legal/cpl-v10.html
   *
  * Software distributed under the License is distributed on an "AS
  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  * implied. See the License for the specific language governing
  * rights and limitations under the License.
  *
  * Copyright (C) 2006 Charles O Nutter <headius@headius.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either of the GNU General Public License Version 2 or later (the "GPL"),
  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the CPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the CPL, the GPL or the LGPL.
  ***** END LICENSE BLOCK *****/
 
 package org.jruby.compiler.impl;
 
 import java.io.File;
 import java.net.URL;
 import java.util.List;
 
 import org.jruby.Ruby;
 import static org.jruby.util.CodegenUtils.*;
 import  org.objectweb.asm.ClassReader;
 import  org.objectweb.asm.ClassVisitor;
 import  org.objectweb.asm.ClassWriter;
 import  org.objectweb.asm.Label;
 import  org.objectweb.asm.Opcodes;
 import  org.objectweb.asm.Type;
 import  org.objectweb.asm.util.CheckClassAdapter;
 import  org.objectweb.asm.util.TraceClassVisitor;

Author(s):
headius
 
 public class StandardASMCompiler implements ScriptCompiler, Opcodes {
     public static final String THREADCONTEXT = p(ThreadContext.class);
     public static final String RUBY = p(Ruby.class);
     public static final String IRUBYOBJECT = p(IRubyObject.class);
     public static final boolean VERIFY_CLASSFILES = false;
 
     public static Class[] getStaticMethodArgs(Class targetint args) {
         switch (args) {
         case 0:
             return new Class[] {targetThreadContext.classIRubyObject.classBlock.class};
         case 1:
             return new Class[] {targetThreadContext.classIRubyObject.classIRubyObject.classBlock.class};
         case 2:
             return new Class[] {targetThreadContext.classIRubyObject.classIRubyObject.classIRubyObject.classBlock.class};
         case 3:
             return new Class[] {targetThreadContext.classIRubyObject.classIRubyObject.classIRubyObject.classIRubyObject.classBlock.class};
         case 4:
             return new Class[] {targetThreadContext.classIRubyObject.classIRubyObject[].classBlock.class};
         default:
             throw new RuntimeException("unsupported arity: " + args);
         }
     }
 
     public static String getStaticMethodSignature(String classnameint args) {
         switch (args) {
        case 0:
            return sig(IRubyObject.class"L" + classname + ";"ThreadContext.classIRubyObject.classBlock.class);
        case 1:
            return sig(IRubyObject.class"L" + classname + ";"ThreadContext.classIRubyObject.classIRubyObject.classBlock.class);
        case 2:
            return sig(IRubyObject.class"L" + classname + ";"ThreadContext.classIRubyObject.classIRubyObject.classIRubyObject.classBlock.class);
        case 3:
            return sig(IRubyObject.class"L" + classname + ";"ThreadContext.classIRubyObject.classIRubyObject.classIRubyObject.classIRubyObject.classBlock.class);
        case 4:
            return sig(IRubyObject.class"L" + classname + ";"ThreadContext.classIRubyObject.classIRubyObject[].classBlock.class);
        default:
            throw new RuntimeException("unsupported arity: " + args);
        }
    }
    public static Class[] getStaticMethodParams(Class targetint args) {
        switch (args) {
        case 0:
            return new Class[] {targetThreadContext.classIRubyObject.classBlock.class};
        case 1:
            return new Class[] {targetThreadContext.classIRubyObject.classIRubyObject.classBlock.class};
        case 2:
            return new Class[] {targetThreadContext.classIRubyObject.classIRubyObject.classIRubyObject.classBlock.class};
        case 3:
            return new Class[] {targetThreadContext.classIRubyObject.classIRubyObject.classIRubyObject.classIRubyObject.classBlock.class};
        case 4:
            return new Class[] {targetThreadContext.classIRubyObject.classIRubyObject[].classBlock.class};
        default:
            throw new RuntimeException("unsupported arity: " + args);
        }
    }
    public static String getMethodSignature(int args) {
        switch (args) {
        case 0:
            return sig(IRubyObject.classThreadContext.classIRubyObject.classBlock.class);
        case 1:
            return sig(IRubyObject.classThreadContext.classIRubyObject.classIRubyObject.classBlock.class);
        case 2:
            return sig(IRubyObject.classThreadContext.classIRubyObject.classIRubyObject.classIRubyObject.classBlock.class);
        case 3:
            return sig(IRubyObject.classThreadContext.classIRubyObject.classIRubyObject.classIRubyObject.classIRubyObject.classBlock.class);
        case 4:
            return sig(IRubyObject.classThreadContext.classIRubyObject.classIRubyObject[].classBlock.class);
        default:
            throw new RuntimeException("unsupported arity: " + args);
        }
    }
    public static String getStaticClosureSignature(String classdesc) {
        return sig(IRubyObject.class"L" + classdesc + ";"ThreadContext.classIRubyObject.classIRubyObject.classBlock.class);
    }
    public static String getStaticClosure19Signature(String classdesc) {
        return sig(IRubyObject.class"L" + classdesc + ";"ThreadContext.classIRubyObject.classIRubyObject[].classBlock.class);
    }
    public static String getClosureSignature() {
        return sig(IRubyObject.classThreadContext.classIRubyObject.classIRubyObject.classBlock.class);
    }
    public static String getClosure19Signature() {
        return sig(IRubyObject.classThreadContext.classIRubyObject.classIRubyObject[].classBlock.class);
    }
    public static final int THIS = 0;
    public static final int THREADCONTEXT_INDEX = 1;
    public static final int SELF_INDEX = 2;
    public static final int ARGS_INDEX = 3;
    
    public static final int CLOSURE_OFFSET = 0;
    public static final int DYNAMIC_SCOPE_OFFSET = 1;
    public static final int VARS_ARRAY_OFFSET = 2;
    public static final int EXCEPTION_OFFSET = 3;
    public static final int PREVIOUS_EXCEPTION_OFFSET = 4;
    public static final int FIRST_TEMP_OFFSET = 5;
    public static final int STARTING_DSTR_FACTOR = 10;
    
    private String classname;
    private String sourcename;
    private Integer javaVersion;
    private ClassWriter classWriter;
    private int methodIndex = 0;
    private int innerIndex = 0;
    private int rescueNumber = 1;
    private int ensureNumber = 1;
    
    private CacheCompiler cacheCompiler;
Creates a new instance of StandardCompilerContext
    public StandardASMCompiler(String classnameString sourcename) {
        this. = classname;
        this. = sourcename;
    }
    public void setJavaVersion(Integer javaVersion) {
        this. = javaVersion;
    }
    public byte[] getClassByteArray() {
        return .toByteArray();
    }
    public Class<?> loadClass(JRubyClassLoader classLoaderthrows ClassNotFoundException {
        classLoader.defineClass(c(getClassname()), .toByteArray());
        return classLoader.loadClass(c(getClassname()));
    }
    
    public void dumpClass(PrintStream out) {
        PrintWriter pw = new PrintWriter(out);
        try {
            TraceClassVisitor tcv = new TraceClassVisitor(pw);
            new ClassReader(.toByteArray()).accept(tcv, 0);
        } finally {
            pw.close();
        }
    }
    public void writeClass(File destinationthrows IOException {
        writeClass(getClassname(), destination);
    }
    public void writeInvokers(String destinationthrows IOException {
        writeInvokers(new File(destination));
    }
    public void writeInvokers(File destinationthrows IOException {
        for (InvokerDescriptor descriptor : ) {
            byte[] invokerBytes = RuntimeHelpers.defOffline(
                    descriptor.getName(),
                    descriptor.getClassname(),
                    descriptor.getInvokerName(),
                    descriptor.getArity(),
                    descriptor.getScope(),
                    descriptor.getCallConfig(),
                    descriptor.getFile(),
                    descriptor.getLine());
            if () CheckClassAdapter.verify(new ClassReader(invokerBytes), falsenew PrintWriter(.));
            writeClassFile(destinationinvokerBytesdescriptor.getInvokerName());
        }
        for (BlockCallbackDescriptor descriptor : ) {
            byte[] callbackBytes = RuntimeHelpers.createBlockCallbackOffline(
                    descriptor.getClassname(),
                    descriptor.getMethod(),
                    descriptor.getFile(),
                    descriptor.getLine());
            if () CheckClassAdapter.verify(new ClassReader(callbackBytes), falsenew PrintWriter(.));
            writeClassFile(destinationcallbackBytesdescriptor.getCallbackName());
        }
        for (BlockCallbackDescriptor descriptor : ) {
            byte[] callbackBytes = RuntimeHelpers.createBlockCallback19Offline(
                    descriptor.getClassname(),
                    descriptor.getMethod(),
                    descriptor.getFile(),
                    descriptor.getLine());
            if () CheckClassAdapter.verify(new ClassReader(callbackBytes), falsenew PrintWriter(.));
            writeClassFile(destinationcallbackBytesdescriptor.getCallbackName());
        }
    }
    private void writeClass(String classnameFile destination, ClassWriter writerthrows IOException {
        // verify the class
        byte[] bytecode = writer.toByteArray();
        if () CheckClassAdapter.verify(new ClassReader(bytecode), falsenew PrintWriter(.));
        writeClassFile(destinationbytecodeclassname);
    }
    private void writeClassFile(File destinationbyte[] bytecodeString classnamethrows IOException {
        String fullname = classname + ".class";
        String filename = null;
        String path = null;
        if (fullname.lastIndexOf("/") == -1) {
            filename = fullname;
            path = "";
        } else {
            filename = fullname.substring(fullname.lastIndexOf("/") + 1);
            path = fullname.substring(0, fullname.lastIndexOf("/"));
        }
        // create dir if necessary
        File pathfile = new File(destinationpath);
        pathfile.mkdirs();
        FileOutputStream out = new FileOutputStream(new File(pathfilefilename));
        try {
            out.write(bytecode);
        } finally {
            out.close();
        }
    }
    public static class InvokerDescriptor {
        private final String name;
        private final String classname;
        private final String invokerName;
        private final Arity arity;
        private final StaticScope scope;
        private final CallConfiguration callConfig;
        private final String file;
        private final int line;
        
        public InvokerDescriptor(String nameString classnameArity arityStaticScope scopeCallConfiguration callConfigString fileint line) {
            this. = name;
            this. = classname;
            this. = InvocationMethodFactory.getCompiledCallbackName(classnamename);
            this. = arity;
            this. = scope;
            this. = callConfig;
            this. = file;
            this. = line;
        }
        public Arity getArity() {
            return ;
        }
        public CallConfiguration getCallConfig() {
            return ;
        }
        public String getClassname() {
            return ;
        }
        public String getFile() {
            return ;
        }
        public String getInvokerName() {
            return ;
        }
        public int getLine() {
            return ;
        }
        public String getName() {
            return ;
        }
        public StaticScope getScope() {
            return ;
        }
    }
    private static class BlockCallbackDescriptor {
        private final String method;
        private final String classname;
        private final String callbackName;
        private final String file;
        private final int line;
        public BlockCallbackDescriptor(String methodString classnameString fileint line) {
            this. = method;
            this. = classname;
            this. = InvocationMethodFactory.getBlockCallbackName(classnamemethod);
            this. = file;
            this. = line;
        }
        public String getClassname() {
            return ;
        }
        public String getMethod() {
            return ;
        }
        public String getCallbackName() {
            return ;
        }
        public String getFile() {
            return ;
        }
        public int getLine() {
            return ;
        }
    }
    public void addInvokerDescriptor(String newMethodNameint methodArityStaticScope scopeCallConfiguration callConfigString filenameint line) {
        Arity arity = Arity.createArity(methodArity);
        InvokerDescriptor descriptor = new InvokerDescriptor(newMethodNamearityscopecallConfigfilenameline);
        .add(descriptor);
    }
    public void addBlockCallbackDescriptor(String methodString fileint line) {
        .add(new BlockCallbackDescriptor(methodfileline));
    }
    public void addBlockCallback19Descriptor(String methodString fileint line) {
        .add(new BlockCallbackDescriptor(methodfileline));
    }
    public String getClassname() {
        return ;
    }
    public String getSourcename() {
        return ;
    }
    public ClassVisitor getClassVisitor() {
        return ;
    }
    
    public void startScript(StaticScope scope) {
         = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
        // Create the class with the appropriate class name and source file
        .visit( == null ? . : ,
                ACC_PUBLIC + ACC_SUPER,getClassname(), nullp(AbstractScript.class), null);
        // add setPosition impl, which stores filename as constant to speed updates
        SkinnyMethodAdapter method = new SkinnyMethodAdapter(getClassVisitor(), ACC_PRIVATE | ACC_STATIC | ACC_SYNTHETIC, "setPosition"sig(.params(ThreadContext.classint.class)), nullnull);
        method.start();
        method.aload(0); // thread context
        method.ldc();
        method.iload(1); // line number
        method.invokevirtual(p(ThreadContext.class), "setFileAndLine"sig(void.classString.classint.class));
        method.voidreturn();
        method.end();
        
         = scope;
        beginInit();
        
         = OptoFactory.newCacheCompiler(this);
        // This code was originally used to provide debugging info using JSR-45
        // "SMAP" format. However, it breaks using normal Java traces to
        // generate Ruby traces, since the original path is lost. Reverting
        // to full path for now.
//        String sourceNoPath;
//        if (sourcename.indexOf("/") >= 0) {
//            String[] pathElements = sourcename.split("/");
//            sourceNoPath = pathElements[pathElements.length - 1];
//        } else if (sourcename.indexOf("\\") >= 0) {
//            String[] pathElements = sourcename.split("\\\\");
//            sourceNoPath = pathElements[pathElements.length - 1];
//        } else {
//            sourceNoPath = sourcename;
//        }
        final File sourceFile = new File(getSourcename());
        // Revert to using original sourcename here, so that jitted traces match
        // interpreted traces.
        .visitSource(sourceFile.getAbsolutePath());
    }
    public void endScript(boolean generateLoadboolean generateMain) {
        // add Script#run impl, used for running this script with a specified threadcontext and self
        // root method of a script is always in __file__ method
        String methodName = "__file__";
        
        String loadSig = sig(IRubyObject.classThreadContext.classIRubyObject.classboolean.class);
        
        if (generateLoad || generateMain) {
            // the load method is used for loading as a top-level script, and prepares appropriate scoping around the code
            SkinnyMethodAdapter method = new SkinnyMethodAdapter(
                    getClassVisitor(),
                    ACC_PUBLIC,
                    "load",
                    loadSig,
                    null,
                    null);
            method.start();
            // invoke __file__ with threadcontext, self, args (null), and block (null)
            Label tryBegin = new Label();
            Label tryFinally = new Label();
            method.label(tryBegin);
            method.aload();
            String scopeNames = RuntimeHelpers.encodeScope();
            method.ldc(scopeNames);
            method.iload( + 1);
            method.invokestatic(p(RuntimeHelpers.class), "preLoad"sig(void.classThreadContext.classString.classboolean.class));
            method.aload();
            method.aload();
            method.aload();
            method.getstatic(p(IRubyObject.class), "NULL_ARRAY"ci(IRubyObject[].class));
            method.getstatic(p(Block.class), "NULL_BLOCK"ci(Block.class));
            method.invokestatic(getClassname(),methodNamegetStaticMethodSignature(getClassname(), 4));
            method.aload();
            method.invokestatic(p(RuntimeHelpers.class), "postLoad"sig(void.classThreadContext.class));
            method.areturn();
            method.label(tryFinally);
            method.aload();
            method.invokestatic(p(RuntimeHelpers.class), "postLoad"sig(void.classThreadContext.class));
            method.athrow();
            method.trycatch(tryBegintryFinallytryFinallynull);
            method.end();
        }
        
        if (generateMain) {
            // add main impl, used for detached or command-line execution of this script with a new runtime
            // root method of a script is always in stub0, method0
            SkinnyMethodAdapter method = new SkinnyMethodAdapter(getClassVisitor(), ACC_PUBLIC | ACC_STATIC, "main"sig(.params(String[].class)), nullnull);
            method.start();
            // new instance to invoke run against
            method.newobj(getClassname());
            method.dup();
            method.invokespecial(getClassname(), "<init>"sig(.));
            // set filename for the loaded script class (JRUBY-4825)
            method.dup();
            method.ldc(Type.getType("L" + getClassname() + ";"));
            method.invokevirtual(p(Class.class), "getClassLoader"sig(ClassLoader.class));
            method.ldc(getClassname() + ".class");
            method.invokevirtual(p(ClassLoader.class), "getResource"sig(URL.classString.class));
            method.invokevirtual(p(Object.class), "toString"sig(String.class));
            method.astore(1);
            method.aload(1);
            method.invokevirtual(p(AbstractScript.class), "setFilename"sig(void.classString.class));
            // instance config for the script run
            method.newobj(p(RubyInstanceConfig.class));
            method.dup();
            method.invokespecial(p(RubyInstanceConfig.class), "<init>""()V");
            // set argv from main's args
            method.dup();
            method.aload(0);
            method.invokevirtual(p(RubyInstanceConfig.class), "setArgv"sig(void.classString[].class));
            // set script filename ($0)
            method.dup();
            method.aload(1);
            method.invokevirtual(p(RubyInstanceConfig.class), "setScriptFileName"sig(void.classString.class));
            // invoke run with threadcontext and topself
            method.invokestatic(p(Ruby.class), "newInstance"sig(Ruby.classRubyInstanceConfig.class));
            method.dup();
            method.invokevirtual("getCurrentContext"sig(ThreadContext.class));
            method.swap();
            method.invokevirtual("getTopSelf"sig(IRubyObject.class));
            method.ldc(false);
            method.invokevirtual(getClassname(), "load"loadSig);
            method.voidreturn();
            method.end();
        }
        getCacheCompiler().finish();
        
        endInit();
        endClassInit();
    }
    public static String buildStaticScopeNames(StaticScope scope) {
        return RuntimeHelpers.encodeScope(scope);
    }
    private void beginInit() {
        ClassVisitor cv = getClassVisitor();
         = new SkinnyMethodAdapter(cv, ACC_PUBLIC, "<init>"sig(.), nullnull);
        .start();
        .aload();
        .invokespecial(p(AbstractScript.class), "<init>"sig(.));
        
        // JRUBY-3014: make __FILE__ dynamically determined at load time, but
        // we provide a reasonable default here
        .aload();
        .ldc(getSourcename());
        .putfield(getClassname(), "filename"ci(String.class));
    }
    private void endInit() {
        .voidreturn();
        .end();
    }
    private void beginClassInit() {
        ClassVisitor cv = getClassVisitor();
         = new SkinnyMethodAdapter(cv, ACC_PUBLIC | ACC_STATIC, "<clinit>"sig(.), nullnull);
        .start();
    }
    private void endClassInit() {
        if ( != null) {
            .voidreturn();
            .end();
        }
    }
    
        return ;
    }
    
        // lazily create class init only if necessary
        if ( == null) {
            beginClassInit();
        }
        return ;
    }
    
    public CacheCompiler getCacheCompiler() {
        return ;
    }
    
    public BodyCompiler startMethod(String rubyNameString javaNameCompilerCallback argsStaticScope scopeASTInspector inspector) {
        RootScopedBodyCompiler methodCompiler = new MethodBodyCompiler(thisrubyNamejavaNameinspectorscope);
        
        methodCompiler.beginMethod(argsscope);
        
        return methodCompiler;
    }
    public BodyCompiler startFileMethod(CompilerCallback argsStaticScope scopeASTInspector inspector) {
        MethodBodyCompiler methodCompiler = new MethodBodyCompiler(this"__file__""__file__"inspectorscope);
        
        methodCompiler.beginMethod(argsscope);
        
        // boxed arg list __file__
        SkinnyMethodAdapter method = new SkinnyMethodAdapter(getClassVisitor(), ACC_PUBLIC, "__file__"getMethodSignature(4), nullnull);
        method.start();
        // invoke static __file__
        method.aload();
        method.aload();
        method.aload();
        method.aload();
        method.aload( + 1); // block
        method.invokestatic(getClassname(), "__file__"getStaticMethodSignature(getClassname(), 4));
        method.areturn();
        method.end();
        
        if (methodCompiler.isSpecificArity()) {
            // exact arg list __file__
            method = new SkinnyMethodAdapter(getClassVisitor(), ACC_PUBLIC, "__file__"getMethodSignature(scope.getRequiredArgs()), nullnull);
            method.start();
            // invoke static __file__
            method.aload();
            method.aload();
            method.aload();
            for (int i = 0; i < scope.getRequiredArgs(); i++) {
                method.aload( + i);
            }
            method.aload( + scope.getRequiredArgs()); // block
            method.invokestatic(getClassname(), "__file__"getStaticMethodSignature(getClassname(), scope.getRequiredArgs()));
            method.areturn();
            method.end();
        }
        return methodCompiler;
    }
    public BodyCompiler startRoot(String rubyNameString javaNameStaticScope scopeASTInspector inspector) {
        RootScopedBodyCompiler methodCompiler = new MethodBodyCompiler(thisrubyNamejavaNameinspectorscope);
        methodCompiler.beginMethod(nullscope);
        return methodCompiler;
    }
    public int getMethodIndex() {
        return ;
    }
    
    public int getAndIncrementMethodIndex() {
        return ++;
    }
    public int getInnerIndex() {
        return ;
    }
    public int getAndIncrementInnerIndex() {
        return ++;
    }
    public int getRescueNumber() {
        return ;
    }
    public int getAndIncrementRescueNumber() {
        return ++;
    }
    public int getEnsureNumber() {
        return ;
    }
    public int getAndIncrementEnsureNumber() {
        return ++;
    }
    private int constants = 0;
    public String getNewConstant(String typeString name_prefix) {
        return getNewConstant(typename_prefixnull);
    }
    public synchronized String getNewConstantName() {
        return "_" + ++;
    }
    public String getNewConstant(String typeString name_prefixObject init) {
        ClassVisitor cv = getClassVisitor();
        String realName = getNewConstantName();
        // declare the field
        cv.visitField(ACC_PRIVATE, realNametypenullnull).visitEnd();
        if(init != null) {
            .aload();
            .ldc(init);
            .putfield(getClassname(),realNametype);
        }
        return realName;
    }
    public String getNewField(String typeString nameObject init) {
        ClassVisitor cv = getClassVisitor();
        // declare the field
        cv.visitField(ACC_PRIVATE, nametypenullnull).visitEnd();
        if(init != null) {
            .aload();
            .ldc(init);
            .putfield(getClassname(),nametype);
        }
        return name;
    }
    public String getNewStaticConstant(String typeString name_prefix) {
        ClassVisitor cv = getClassVisitor();
        String realName;
        synchronized (this) {
            realName = "__" + ++;
        }
        // declare the field
        cv.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, realNametypenullnull).visitEnd();
        return realName;
    }
New to GrepCode? Check out our FAQ X