com.xruby.runtime.lang.util.MethodFactory.java Source code

Java tutorial

Introduction

Here is the source code for com.xruby.runtime.lang.util.MethodFactory.java

Source

/**
 * Copyright 2007 Ye Zheng
 * Distributed under the BSD License
 */

package com.xruby.runtime.lang.util;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.util.CheckClassAdapter;

import com.xruby.compiler.XRubyClassLoader;
import com.xruby.compiler.codegen.CgConfig;
import com.xruby.compiler.codegen.CgUtil;
import com.xruby.compiler.codegen.ClassDumper;
import com.xruby.runtime.lang.RubyMethod;

public class MethodFactory {
    private static final XRubyClassLoader cl = new XRubyClassLoader();
    private static final ClassDumper dumper = new ClassDumper();

    private Class hostClass;
    private Class castClass;
    private boolean module;

    public static MethodFactory createMethodFactory(Class klass) {
        return new MethodFactory(klass, false);
    }

    public static MethodFactory createMethodFactory(Class klass, boolean module) {
        return new MethodFactory(klass, module);
    }

    public static MethodFactory createModuleMethodFactory(Class klass) {
        return new MethodFactory(klass, true);
    }

    private MethodFactory(Class klass, boolean module) {
        this(klass, klass, module);
    }

    private MethodFactory(Class hostClass, Class castClass, boolean module) {
        this.hostClass = hostClass;
        this.castClass = castClass;
        this.module = module;
    }

    public RubyMethod getMethod(String name, MethodType type, boolean singleton, boolean block) {
        return loadMethod(name, type, singleton, block);
    }

    public RubyMethod getMethod(String name, MethodType type) {
        return loadMethod(name, type, false, false);
    }

    public RubyMethod getMethodWithBlock(String name, MethodType type) {
        return loadMethod(name, type, false, true);
    }

    public RubyMethod getSingletonMethod(String name, MethodType type) {
        return loadMethod(name, type, true, false);
    }

    public RubyMethod getSingletonMethodWithBlock(String name, MethodType type) {
        return loadMethod(name, type, true, true);
    }

    private RubyMethod loadMethod(String name, MethodType type, boolean singleton, boolean block) {
        String invokerName = getInvokerName(name, block);
        Class klass = tryClass(invokerName);
        if (klass == null) {
            klass = createMethodClass(invokerName, name, type, singleton, block);
        }

        try {
            return (RubyMethod) klass.newInstance();
        } catch (InstantiationException ie) {
            throw new RuntimeException("fail to create Ruby method", ie);
        } catch (IllegalAccessException iae) {
            throw new RuntimeException("fail to create Ruby method", iae);
        }
    }

    private Class tryClass(String name) {
        try {
            return cl.load(name);
        } catch (ClassNotFoundException e) {
            return null;
        }
    }

    private Class createMethodClass(String invokerName, String name, MethodType type, boolean singleton,
            boolean block) {
        MethodFactoryHelper helper = MethodFactoryHelper.getHelper(type);
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        ClassVisitor cv = new CheckClassAdapter(cw);

        startInvoker(cv, helper, invokerName);
        helper.createRunMethod(cv, this.hostClass, this.castClass, name, (singleton | this.module), block);
        endInvoker(cv);

        return loadClass(invokerName, cw);
    }

    private String getInvokerName(String name, boolean block) {
        StringBuilder sb = new StringBuilder();
        sb.append(hostClass.getName());
        sb.append("$");
        sb.append(name);
        if (block) {
            sb.append("WithBlock");
        }
        sb.append("$Invoker");
        return CgUtil.getInternalName(sb.toString());
    }

    private void startInvoker(ClassVisitor cv, MethodFactoryHelper helper, String invokerName) {
        Type superType = helper.getSuperType();
        cv.visit(CgConfig.TARGET_VERSION, Opcodes.ACC_PUBLIC, invokerName, null, superType.getInternalName(), null);
        CgUtil.createImplicitConstructor(cv, superType);
    }

    private void endInvoker(ClassVisitor cv) {
        cv.visitEnd();
    }

    private Class loadClass(String invokerName, ClassWriter cw) {
        dumper.dump(invokerName, cw.toByteArray());
        return cl.load(invokerName, cw.toByteArray());
    }
}