com.xruby.compiler.codegen.RubyCompilerImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.xruby.compiler.codegen.RubyCompilerImpl.java

Source

/**
 * Copyright 2005-2007 Xue Yong Zhi, Ye Zheng, Yu Su
 * Distributed under the BSD License
 */

package com.xruby.compiler.codegen;

import com.xruby.compiler.codedom.CodeVisitor;
import com.xruby.compiler.codedom.Program;
import com.xruby.runtime.lang.RubyBinding;

import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.GeneratorAdapter;

import java.math.BigInteger;
import java.util.Stack;

public class RubyCompilerImpl implements CodeVisitor {

    private ClassGenerator cg_;
    private final Stack<ClassGenerator> suspended_cgs_ = new Stack<ClassGenerator>();
    private final CompilationResults compilation_results_;
    private final String extra_;
    private final String script_name_;
    private RubyBinding binding_;

    private Label currentLineLabel;
    private boolean enableDebug = false;

    public RubyCompilerImpl(String extra, String script_name) {
        extra_ = extra;
        script_name_ = script_name;
        compilation_results_ = new CompilationResults();
    }

    private boolean isInGlobalScope() {
        return suspended_cgs_.empty() && !isInClassBuilder();
    }

    private boolean isInBlock() {
        return (cg_ instanceof ClassGeneratorForRubyBlock);
    }

    private boolean isInSingletonMethod() {
        if (cg_ instanceof ClassGeneratorForRubyMethod) {
            return ((ClassGeneratorForRubyMethod) cg_).isSingletonMethod();
        }

        return false;
    }

    private boolean isInClassBuilder() {
        return (cg_ instanceof ClassGeneratorForClassModuleBuilder);
    }

    public void enableDebug() {
        enableDebug = true;
    }

    private void switchToNewClassGenerator(ClassGenerator cg) {
        suspended_cgs_.push(cg_);
        cg_ = cg;
    }

    private void switchToPreviousClassGenerator(boolean last_statement_has_return_value) {
        MethodGenerator mg = cg_.getMethodGenerator();
        if (!last_statement_has_return_value) {
            mg.loadNil();
        }

        mg.returnValue();
        mg.endMethod();
        cg_.visitEnd();

        compilation_results_.add(cg_.getCompilationResult());
        cg_ = suspended_cgs_.pop();
    }

    public CompilationResults compile(Program program, RubyBinding binding) {
        binding_ = binding;
        RubyIDClassGenerator.initScript(extra_, script_name_);
        String className = NameFactory.createClassName(extra_, script_name_, null);
        cg_ = new ClassGeneratorForRubyProgram(className, script_name_, binding, false, false);

        // Start compiling
        program.accept(this);

        MethodGenerator mg = cg_.getMethodGenerator();
        // Record the local variables' range, if user enables debug
        if (enableDebug) {
            mg.writeLocalVariableInfo();
        }
        mg.endMethod();
        cg_.visitEnd();
        compilation_results_.add(RubyIDClassGenerator.getCompilationResult());
        //      RubyIDClassGenerator.clear();
        compilation_results_.add(cg_.getCompilationResult());
        return compilation_results_;
    }

    public void visitClassDefinition1(String className, boolean has_scope) {
        MethodGenerator mg = cg_.getMethodGenerator();
        if (!isInGlobalScope() && !has_scope) {
            mg.loadCurrentClass(isInBlock());
        } else if (has_scope) {
            mg.checkCast(Types.RUBY_MODULE_TYPE);
        }

        mg.push(className);
        //super class will be pushed next, then visitSuperClass() will be called
    }

    public void visitClassDefinition2(String className, boolean has_scope) {
        //TODO optimizing aceess to builtin class (use them directly)
        MethodGenerator mg = cg_.getMethodGenerator();
        if (has_scope) {
            mg.checkCast(Types.RUBY_MODULE_TYPE);
            mg.RubyModule_defineClass();
        } else if (isInGlobalScope()) {
            mg.RubyAPI_defineClass();
        } else {
            mg.RubyModule_defineClass();
        }

        //The class body may refer the constant, so save it before class builder starts.
        callClassModuleBuilder(className, false);
    }

    private void callClassModuleBuilder(String name, boolean is_singleton) {
        MethodGenerator mg = cg_.getMethodGenerator();
        int i = mg.newLocal(Types.RUBY_VALUE_TYPE);
        mg.storeLocal(i);

        String uniqueName = NameFactory.createClassnameForClassModuleBuilder(extra_, script_name_, name);
        Type builder = Type.getType("L" + uniqueName + ";");
        mg.newInstance(builder);
        mg.dup();
        mg.invokeConstructor(builder, CgUtil.CONSTRUCTOR);
        mg.loadLocal(i);
        mg.pushNull();
        mg.pushNull();
        mg.loadLocal(i);
        mg.invokeVirtual(builder, CgUtil.getMethod("invoke", Types.RUBY_VALUE_TYPE, Types.RUBY_VALUE_TYPE,
                Types.RUBY_ARRAY_TYPE, Types.RUBY_BLOCK_TYPE, Types.RUBY_MODULE_TYPE));

        switchToNewClassGenerator(
                new ClassGeneratorForClassModuleBuilder(uniqueName, script_name_, null, is_singleton));
    }

    public void visitSingletonClassDefinition() {
        MethodGenerator mg = cg_.getMethodGenerator();
        mg.loadCurrentScope(isInClassBuilder(), isInSingletonMethod(), isInGlobalScope(), isInBlock());
        mg.RubyValue_getSingletonClass();
        callClassModuleBuilder("SINGLETON", true);
    }

    public void visitClassDefinitionEnd(boolean last_statement_has_return_value) {
        switchToPreviousClassGenerator(last_statement_has_return_value);
    }

    public void visitModuleDefinition(String moduleName, boolean has_scope) {
        MethodGenerator mg = cg_.getMethodGenerator();
        if (!mg.RubyRuntime_getBuiltinModule(moduleName)) {
            if (has_scope) {
                mg.checkCast(Types.RUBY_MODULE_TYPE);
                mg.RubyModule_defineModule(moduleName);
            } else if (isInGlobalScope()) {
                mg.RubyAPI_defineModule(moduleName);
            } else {
                mg.loadArg(3);
                mg.RubyModule_defineModule(moduleName);
            }
        }

        callClassModuleBuilder(moduleName, false);
    }

    public void visitModuleDefinitionEnd(boolean last_statement_has_return_value) {
        switchToPreviousClassGenerator(last_statement_has_return_value);
    }

    public int visitBlockBegin(StringBuilder name, boolean pulled) {
        String method_name = null;
        if (cg_ instanceof ClassGeneratorForRubyMethod) {
            method_name = ((ClassGeneratorForRubyMethod) cg_).getOrginalMethodName();
        } else if (cg_ instanceof ClassGeneratorForRubyBlock) {
            method_name = ((ClassGeneratorForRubyBlock) cg_).getOrginalMethodName();
        }
        String uniqueBlockName = NameFactory.createClassNameForBlock(extra_, script_name_, method_name);

        MethodGenerator mg = cg_.getMethodGenerator();
        mg.new_BlockClass(cg_, uniqueBlockName, isInClassBuilder(), isInSingletonMethod(), isInGlobalScope(),
                isInBlock());

        assert (name.length() == 0);
        name.insert(0, uniqueBlockName);

        if (pulled) {
            //saved the result to a local variable to retrieve later
            int i = mg.newLocal(Type.getType("L" + uniqueBlockName + ";"));
            mg.storeLocal(i);
            return i;
        }

        return -1;
    }

    public void visitBlockBodyBegin(String uniqueBlockName, int argc, boolean has_asterisk_parameter,
            int num_of_default_args, boolean is_for_in_expression, boolean has_extra_comma, boolean has_body) {

        //Save the current state and sart a new class file to write.
        switchToNewClassGenerator(new ClassGeneratorForRubyBlock(uniqueBlockName, script_name_, argc,
                has_asterisk_parameter, num_of_default_args, cg_, is_for_in_expression, has_extra_comma, binding_));
        if (has_body) {
            if (argc >= 1 || has_asterisk_parameter || is_for_in_expression) {
                // has_extra_comma == (argc >= 1)
                cg_.getMethodGenerator().loadArg(1);
            } else {
                cg_.getMethodGenerator().loadNil();
            }
        }

        cg_.getMethodGenerator().getLabelManager().openNewScope();
    }

    public void visitBlockBodyEnd(String uniqueBlockName, boolean last_statement_has_return_value,
            int saved_as_pulled) {

        ClassGeneratorForRubyBlock block_cg = (ClassGeneratorForRubyBlock) cg_;
        String[] commons = block_cg.getFields();
        String[] assigned_commons = block_cg.getAssignedFields();
        String[] blocks = block_cg.getPreviousBlocks();
        block_cg.createFieldsAndConstructorOfRubyBlock(commons, blocks);

        cg_.getMethodGenerator().getLabelManager().closeCurrentScope();

        switchToPreviousClassGenerator(last_statement_has_return_value);

        if (saved_as_pulled >= 0) {
            cg_.getMethodGenerator().loadLocal(saved_as_pulled);
        }

        for (int i = 0; i < commons.length; ++i) {
            cg_.getMethodGenerator().dup();
            cg_.loadVariable(commons[i]);
            cg_.getMethodGenerator().putField(Type.getType("L" + uniqueBlockName + ";"),
                    ClassGenerator.decorateName(commons[i]), Types.RUBY_VALUE_TYPE);
        }

        for (int i = 0; i < blocks.length; ++i) {
            cg_.getMethodGenerator().dup();
            cg_.getSharedBlock(blocks[i]);
            ;
            cg_.getMethodGenerator().putField(Type.getType("L" + uniqueBlockName + ";"),
                    ClassGeneratorForRubyBlock.getNameFromFullpath(blocks[i]), Type.getType("L" + blocks[i] + ";"));
        }

        cg_.getMethodGenerator().storeVariablesAssignedInBlock(uniqueBlockName, assigned_commons, saved_as_pulled);
    }

    public void visitBlockBody() {
        MethodGenerator mg = cg_.getMethodGenerator();
        mg.mark(mg.getLabelManager().getCurrentRedo());
    }

    public String visitMethodDefinition(String methodName, int num_of_args, boolean has_asterisk_parameter,
            int num_of_default_args, boolean is_singleton_method) {

        String uniqueMethodName = NameFactory.createClassName(extra_, script_name_, methodName);

        MethodGenerator mg = cg_.getMethodGenerator();
        if (!is_singleton_method) {
            mg.loadCurrentScope(isInClassBuilder(), isInSingletonMethod(), isInGlobalScope(), isInBlock());
        } else {
            mg.pushNull();
            mg.RubyValue_getSingletonClass();
        }

        mg.RubyModule_defineMethod(methodName, uniqueMethodName);

        //Save the current state and sart a new class file to write.
        switchToNewClassGenerator(ClassGeneratorForRubyMethodFactory.createClassGeneratorForRubyMethod(methodName,
                script_name_, uniqueMethodName, num_of_args, has_asterisk_parameter, num_of_default_args,
                is_singleton_method || mg.isSingleton()));

        return uniqueMethodName;
    }

    public void visitMethodDefinitionParameter(String name) {
        cg_.addParameter(name);
    }

    public void visitMethodDefinitionAsteriskParameter(String name, int argc) {
        cg_.setAsteriskParameter(name, argc);
    }

    public void visitMethodDefinitionBlockParameter(String name) {
        cg_.setBlockParameter(name);
    }

    public void visitMethodDefinitionEnd(boolean last_statement_has_return_value) {
        switchToPreviousClassGenerator(last_statement_has_return_value);
    }

    public void visitMethodDefinitionDefaultParameters(int size) {
        assert (size > 0);
        //create a empty array if arg is null (avoid null reference)
        MethodGenerator mg = cg_.getMethodGenerator();
        mg.loadArg(1);
        Label label = new Label();
        mg.ifNonNull(label);
        mg.ObjectFactory_createArray(size, 0, false);
        mg.storeArg(1);
        mg.mark(label);
    }

    public Object visitMethodDefinitionDefaultParameterBegin(int index) {
        Label next_label = new Label();

        MethodGenerator mg = cg_.getMethodGenerator();
        mg.loadMethodPrameterLength();
        mg.push(index);
        mg.ifICmp(GeneratorAdapter.GT, next_label);

        mg.loadArg(1);

        return next_label;
    }

    public void visitMethodDefinitionDefaultParameterEnd(Object next_label) {
        MethodGenerator mg = cg_.getMethodGenerator();
        mg.RubyArray_add(false);
        mg.pop();
        mg.mark((Label) next_label);
    }

    public void visitNoParameter() {
        cg_.getMethodGenerator().pushNull();
    }

    public void visitNoParameterForSuper() {
        if (cg_ instanceof ClassGeneratorForRubyBlock) {
            ((ClassGeneratorForRubyBlock) cg_).loadNoParameterForSuper();
        } else {
            cg_.getMethodGenerator().loadMethodArg();
        }
    }

    public void visitNoBlock(boolean is_super_or_block_given_call) {
        if (is_super_or_block_given_call) {
            if (isInGlobalScope()) {
                cg_.getMethodGenerator().pushNull();
            } else {
                cg_.getMethodGenerator().loadBlock(isInBlock());
            }
        } else {
            cg_.getMethodGenerator().pushNull();
        }
    }

    public void visitNoSuperClass() {
        cg_.getMethodGenerator().pushNull();
    }

    public void visitBlockArgument() {
        cg_.getMethodGenerator().RubyAPI_convertRubyValue2RubyBlock();
    }

    public void visitMethodCallBegin() {
        cg_.getMethodGenerator().addCurrentVariablesOnStack(Types.RUBY_VALUE_CLASS);
    }

    public void visitMethodCallEnd(String methodName, boolean hasReceiver, String blockName, int argc) {
        MethodGenerator mg = cg_.getMethodGenerator();
        mg.removeCurrentVariablesOnStack();

        if (hasReceiver) {
            switch (argc) {
            case 0:
                mg.RubyAPI_callPublicNoArgMethod(methodName);
                break;
            case 1:
                mg.RubyAPI_callPublicOneArgMethod(methodName);
                break;
            case 2:
                mg.RubyAPI_callPublicTwoArgMethod(methodName);
                break;
            default:
                mg.RubyAPI_callPublicMethod(methodName);
                break;
            }
        } else {
            switch (argc) {
            case 0:
                mg.RubyAPI_callNoArgMethod(methodName);
                break;
            case 1:
                mg.RubyAPI_callOneArgMethod(methodName);
                break;
            case 2:
                mg.RubyAPI_callTwoArgMethod(methodName);
                break;
            default:
                mg.RubyAPI_callMethod(methodName);
                break;
            }
        }

        mg.returnIfBlockReturned();
    }

    public void visitBinaryOperator(String operator) {
        MethodGenerator mg = cg_.getMethodGenerator();
        mg.pushNull();
        if (operator.equals("!=")) {
            mg.RubyAPI_callPublicOneArgMethod("==");
            mg.RubyAPI_operatorNot();
        } else if (operator.equals("!~")) {
            mg.RubyAPI_callPublicOneArgMethod("=~");
            mg.RubyAPI_operatorNot();
        } else {
            //operator as method call
            mg.RubyAPI_callPublicOneArgMethod(operator);
        }
    }

    public Object visitAndBinaryOperatorLeft() {
        //The and and && operators evaluate their first operand. If false,
        //the expression returns false; otherwise, the expression returns
        //the value of the second operand.
        MethodGenerator mg = cg_.getMethodGenerator();
        mg.dup();
        Label label = (Label) visitAfterIfCondition();
        mg.pop();//discard the current value;
        return label;
    }

    public void visitAndBinaryOperatorRight(Object label) {
        cg_.getMethodGenerator().mark((Label) label);
    }

    public Object visitOrBinaryOperatorLeft() {
        //The or and || operators evaluate their first operand. If true,
        //the expression returns true; otherwise, the expression returns
        //the value of the second operand.
        cg_.getMethodGenerator().dup();
        Label label = (Label) visitAfterUnlessCondition();
        cg_.getMethodGenerator().pop();//discard the current value;
        return label;
    }

    public void visitOrBinaryOperatorRight(Object label) {
        visitAndBinaryOperatorRight(label);
    }

    public void visitUnaryOperator(String operator) {
        MethodGenerator mg = cg_.getMethodGenerator();
        if (operator.equals("!")) {
            mg.RubyAPI_operatorNot();
        } else {
            mg.pushNull();
            mg.pushNull();
            mg.RubyAPI_callPublicMethod(operator);
        }
    }

    public void visitGlobalVariableAssignmentOperator(String var, boolean rhs_is_method_call,
            boolean is_multiple_assign) {
        MethodGenerator mg = cg_.getMethodGenerator();
        if (rhs_is_method_call) {
            mg.RubyAPI_expandArrayIfThereIsZeroOrOneValue();
        }
        mg.GlobalVatiables_set(var);
        if (is_multiple_assign) {
            mg.pop();
        }
    }

    public void visitLocalVariableAssignmentOperator(String var, boolean rhs_is_method_call,
            boolean is_multiple_assign) {
        MethodGenerator mg = cg_.getMethodGenerator();
        if (rhs_is_method_call) {
            mg.RubyAPI_expandArrayIfThereIsZeroOrOneValue();
        }
        if (!is_multiple_assign) {
            mg.dup();//do not pop off empty stack
        }

        cg_.storeVariable(var);
        SymbolTable table = mg.getSymbolTable();

        // Record the start label of this variable
        if (enableDebug && table.isNewLocalVar(var)) {
            table.setVarLineNumberInfo(var, currentLineLabel);
        }
    }

    public void visitFloatExpression(double value) {
        cg_.getMethodGenerator().ObjectFactory_createFloat(value);
    }

    public void visitFixnumExpression(int value) {
        cg_.getMethodGenerator().ObjectFactory_createFixnum(value);
    }

    public void visitBignumExpression(BigInteger value) {
        cg_.getMethodGenerator().ObjectFactory_createBignum(value);
    }

    public void visitStringExpression(String value) {
        cg_.getMethodGenerator().ObjectFactory_createString(value);
    }

    public void visitStringExpressionWithExpressionSubstitutionBegin() {
        cg_.getMethodGenerator().ObjectFactory_createString();
    }

    public void visitStringExpressionWithExpressionSubstitution(String value) {
        cg_.getMethodGenerator().RubyString_append(value);
    }

    public void visitStringExpressionWithExpressionSubstitution() {
        cg_.getMethodGenerator().RubyString_append();
    }

    public void visitStringExpressionWithExpressionSubstitutionEnd() {
    }

    public void visitRegexpExpressionWithExpressionSubstitutionEnd(String option) {
        cg_.getMethodGenerator().ObjectFactory_createRegexp(option);
    }

    public void visitCommandOutputExpressionWithExpressionSubstitutionEnd() {
        cg_.getMethodGenerator().RubyAPI_runCommandAndCaptureOutput();
    }

    public void visitRegexpExpression(String value, String option) {
        cg_.getMethodGenerator().ObjectFactory_createRegexp(value, option);
    }

    public void visitSymbolExpression(String value) {
        cg_.getMethodGenerator().ObjectFactory_createSymbol(value);
    }

    public void visitTerminal() {
        cg_.getMethodGenerator().pop();
    }

    public void visitEof(boolean last_statement_has_return_value) {
        MethodGenerator mg = cg_.getMethodGenerator();
        if (!last_statement_has_return_value) {
            mg.loadNil();
        }
        mg.returnValue();
    }

    public void visitLocalVariableExpression(String value) {
        cg_.loadVariable(value);
    }

    public void visitNilExpression() {
        cg_.getMethodGenerator().loadNil();
    }

    public Object visitAfterIfCondition() {
        MethodGenerator mg = cg_.getMethodGenerator();
        mg.RubyValue_isTrue();
        Label label = new Label();
        mg.ifZCmp(GeneratorAdapter.EQ, label);
        return label;
    }

    public void visitWhileConditionBegin(boolean do_first) {
        MethodGenerator mg = cg_.getMethodGenerator();
        LabelManager lm = mg.getLabelManager();
        lm.openNewScope();
        if (do_first) {
            mg.goTo(lm.getCurrentRedo());
        }
        mg.mark(lm.getCurrentNext());
    }

    public void visitWhileConditionEnd(boolean always_true, boolean is_until) {
        MethodGenerator mg = cg_.getMethodGenerator();
        if (always_true) {
            mg.push(true);
        } else {
            mg.RubyValue_isTrue();
        }

        if (is_until) {
            mg.ifZCmp(GeneratorAdapter.NE, mg.getLabelManager().getCurrentX());
        } else {
            mg.ifZCmp(GeneratorAdapter.EQ, mg.getLabelManager().getCurrentX());
        }

        mg.mark(mg.getLabelManager().getCurrentRedo());
    }

    public void visitWhileBodyEnd(boolean has_body) {
        MethodGenerator mg = cg_.getMethodGenerator();
        if (has_body) {
            mg.pop();
        }

        LabelManager lm = mg.getLabelManager();
        mg.goTo(lm.getCurrentNext());
        mg.mark(lm.getCurrentX());
        visitNilExpression();//this is the value of while expression if no break is called.

        mg.mark(lm.getCurrentBreak());
        lm.closeCurrentScope();
    }

    public Object visitAfterIfBody(Object next_label, Object end_label) {
        if (null == end_label) {
            end_label = new Label();
        }

        MethodGenerator mg = cg_.getMethodGenerator();
        if (null != next_label) {
            mg.goTo((Label) end_label);
            mg.mark((Label) next_label);
        } else {
            mg.mark((Label) end_label);
        }

        return end_label;
    }

    public Object visitAfterCaseCondition() {
        int i = cg_.getAnonymousLocalVariable();
        cg_.getMethodGenerator().storeLocal(i);
        return i;
    }

    public Object visitAfterWhenCondition(Object case_value, boolean mrhs) {
        int i = (Integer) case_value;
        MethodGenerator mg = cg_.getMethodGenerator();
        mg.loadLocal(i);
        if (!mrhs) {
            mg.RubyAPI_testCaseEqual();
        } else {
            mg.RubyAPI_testCaseEqual2();
        }
        Label label = new Label();
        mg.ifZCmp(GeneratorAdapter.EQ, label);
        return label;
    }

    public Object visitAfterWhenConditionNotNil(Object case_value) {
        int i = (Integer) case_value;
        MethodGenerator mg = cg_.getMethodGenerator();
        mg.loadLocal(i);
        mg.RubyAPI_testCaseEqualNotNil();

        Label label = new Label();
        mg.ifZCmp(GeneratorAdapter.EQ, label);
        return label;
    }

    public Object visitAfterWhenBody(Object next_label, Object end_label) {
        return visitAfterIfBody(next_label, end_label);
    }

    public void visitTrueExpression() {
        cg_.getMethodGenerator().loadTrue();
    }

    public void visitFalseExpression() {
        cg_.getMethodGenerator().loadFalse();
    }

    public Object visitAfterUnlessCondition() {
        MethodGenerator mg = cg_.getMethodGenerator();
        mg.RubyValue_isTrue();
        Label label = new Label();
        mg.ifZCmp(GeneratorAdapter.NE, label);
        return label;
    }

    public Object visitAfterUnlessBody(Object next_label, Object end_label) {
        return visitAfterIfBody(next_label, end_label);
    }

    public Object visitBodyBegin(boolean has_ensure) {
        //once exceptio is thrown, everything already on the stack will be destoried. so if we have begin..end
        //in the method parameter. e.g. f(..., begin ..end, ...), the method receiver and parameter is already on the list.
        MethodGenerator mg = cg_.getMethodGenerator();
        mg.saveCurrentVariablesOnStack();

        mg.getEnsureLabelManager().openNewScope();
        if (has_ensure) {
            mg.getEnsureLabelManager().setCurrentFinally(new Label());
        }
        mg.mark(mg.getEnsureLabelManager().getCurrentRetry());
        return mg.getEnsureLabelManager().getCurrentRetry();
    }

    public Object visitBodyAfter() {
        return cg_.getMethodGenerator().mark();
    }

    public void visitBodyEnd(Object label) {
        MethodGenerator mg = cg_.getMethodGenerator();
        mg.mark((Label) label);
        mg.getEnsureLabelManager().closeCurrentScope();

        mg.restoreCurrentVariablesOnStack();
    }

    public int visitEnsureBodyBegin() {
        MethodGenerator mg = cg_.getMethodGenerator();
        mg.mark(mg.getEnsureLabelManager().getCurrentFinally());
        mg.getEnsureLabelManager().setCurrentFinally(null);//finished using it

        int var = mg.newLocal(Type.getType(Object.class));
        mg.storeLocal(var);
        return var;
    }

    public void visitEnsureBodyEnd(int var) {
        MethodGenerator mg = cg_.getMethodGenerator();
        mg.pop();
        mg.ret(var);
    }

    public Object visitPrepareEnsure1() {
        Label label = new Label();
        MethodGenerator mg = cg_.getMethodGenerator();
        mg.visitJumpInsn(Opcodes.JSR, mg.getEnsureLabelManager().getCurrentFinally());
        return label;
    }

    public void visitEnsure(int exception_var) {
        if (exception_var >= 0) {
            MethodGenerator mg = cg_.getMethodGenerator();
            Label l = mg.getEnsureLabelManager().getCurrentFinally();
            if (null != l) {
                mg.visitJumpInsn(Opcodes.JSR, l);
            }
            mg.loadLocal(exception_var);
            mg.throwException();
        } else {
            invokeFinallyIfExist();
        }
    }

    public Object visitPrepareEnsure() {
        Label after_exception = new Label();
        cg_.getMethodGenerator().goTo(after_exception);
        return after_exception;
    }

    public int visitRescueBegin(Object begin, Object end) {
        MethodGenerator mg = cg_.getMethodGenerator();
        mg.catchRubyException((Label) begin, (Label) end);

        int exception_variable = cg_.getAnonymousLocalVariable();
        mg.storeLocal(exception_variable);

        return exception_variable;
    }

    public void visitRescueEnd(int exception_variable, boolean has_ensure) {
        if (!has_ensure) {
            MethodGenerator mg = cg_.getMethodGenerator();
            mg.loadLocal(exception_variable);
            mg.throwException();
        }
    }

    public Object visitRescueVariable(String name, int exception_variable) {
        MethodGenerator mg = cg_.getMethodGenerator();
        mg.loadLocal(exception_variable);
        mg.RubyAPI_testExceptionType();
        Label label = new Label();
        mg.ifZCmp(GeneratorAdapter.EQ, label);

        if (null != name) {
            mg.loadLocal(exception_variable);
            mg.RubyAPI_convertRubyException2RubyValue();
            cg_.storeVariable(name);
        }

        return label;
    }

    public void visitAfterRescueBody(Object next_label, Object end_label) {
        MethodGenerator mg = cg_.getMethodGenerator();
        mg.goTo((Label) end_label);
        mg.mark((Label) next_label);
    }

    public void visitArrayBegin(int size, int rhs_size, boolean has_single_asterisk) {
        MethodGenerator mg = cg_.getMethodGenerator();
        mg.ObjectFactory_createArray(size, rhs_size, has_single_asterisk);
        mg.addCurrentVariablesOnStack(Types.RUBY_ARRAY_CLASS);
    }

    public void visitHashBegin() {
        //TODO use addCurrentVariablesOnStack/removeCurrentVariablesOnStack
        cg_.getMethodGenerator().ObjectFactory_createHash();
    }

    public void visitArrayElement(boolean asterisk, boolean is_method_call) {
        if (asterisk) {
            cg_.getMethodGenerator().RubyArray_expand(is_method_call);
        } else {
            cg_.getMethodGenerator().RubyArray_add(is_method_call);
        }
    }

    public void visitBinding(boolean single_arg) {
        cg_.createBinding(isInClassBuilder(), isInSingletonMethod(), isInGlobalScope(), isInBlock());
        if (!single_arg) {
            cg_.getMethodGenerator().RubyArray_add(false);
        }
    }

    public void visitHashElement() {
        cg_.getMethodGenerator().RubyHash_addValue();
    }

    public void visitArrayEnd() {
        cg_.getMethodGenerator().removeCurrentVariablesOnStack();
    }

    public void visitHashEnd() {
    }

    public void visitYieldBegin() {
        MethodGenerator mg = cg_.getMethodGenerator();
        mg.loadBlock(isInBlock());
        mg.dup();//will be used to call breakOrReturned().
        visitSelfExpression();
    }

    public void visitYieldEnd(int argc) {
        MethodGenerator mg = cg_.getMethodGenerator();
        switch (argc) {
        case 0:
            mg.RubyBlock_invokeNoArg(isInBlock());
            break;
        case 1:
            mg.RubyBlock_invokeOneArg(isInBlock());
            break;
        default:
            mg.RubyBlock_invoke(isInBlock());
            break;
        }

        mg.checkBreakedOrReturned(isInBlock());
    }

    public void visitSuperBegin() {
        cg_.getMethodGenerator().loadArg(0);//TODO error checking: super called outside of method (NoMethodError)
    }

    public void visitImplicitSuperEnd() {
        if (cg_ instanceof ClassGeneratorForRubyMethod) {
            ((ClassGeneratorForRubyMethod) cg_).callSuperMethod(true, false);
        } else {
            ((ClassGeneratorForRubyBlock) cg_).callSuperMethod(true, false);
        }
    }

    public void visitExplicitSuperEnd(int argc) {
        if (cg_ instanceof ClassGeneratorForRubyMethod) {
            MethodGenerator mg = cg_.getMethodGenerator();
            switch (argc) {
            case 0:
                mg.RubyAPI_callSuperNoArgMethod();
                break;
            case 1:
                mg.RubyAPI_callSuperOneArgMethod();
                break;
            case 2:
                mg.RubyAPI_callSuperTwoArgMethod();
                break;
            default:
                mg.RubyAPI_callSuperMethod();
                break;
            }
        } else {
            ((ClassGeneratorForRubyBlock) cg_).callSuperMethod(false, 1 == argc);
        }
    }

    public void visitGlobalVariableExpression(String value) {
        cg_.getMethodGenerator().GlobalVatiables_get(value);
    }

    public void visitCommandOutputExpression(String value) {
        cg_.getMethodGenerator().RubyAPI_runCommandAndCaptureOutput(value);
    }

    private void invokeFinallyIfExist() {
        MethodGenerator mg = cg_.getMethodGenerator();
        Label l = mg.getEnsureLabelManager().getCurrentFinally();
        if (null != l) {
            int tmp = cg_.getAnonymousLocalVariable();
            mg.storeLocal(tmp);//store then load to make stack size always equals 1
            mg.visitJumpInsn(Opcodes.JSR, l);
            mg.loadLocal(tmp);
        }
    }

    public void visitReturn() {
        MethodGenerator mg = cg_.getMethodGenerator();
        if (isInBlock()) {
            invokeFinallyIfExist();
            mg.RubyBlock__return__();
            mg.returnValue();
        } else {
            invokeFinallyIfExist();
            mg.returnValue();
        }
    }

    public void visitAliasGlobalVariable(String newName, String oldName) {
        cg_.getMethodGenerator().GlobalVariables_alias(newName, oldName);
    }

    public void visitAliasMethod(String newName, String oldName) {
        MethodGenerator mg = cg_.getMethodGenerator();
        mg.loadCurrentClass(isInBlock());
        mg.RubyModule_aliasMethod(newName, oldName);
    }

    public void visitUndef(String name) {
        MethodGenerator mg = cg_.getMethodGenerator();
        mg.loadCurrentClass(isInBlock());
        mg.RubyModule_undefMethod(name);
    }

    public void visitSelfExpression() {
        cg_.getMethodGenerator().loadSelf(isInBlock());
    }

    public void visitClassVariableExpression(String name) {
        MethodGenerator mg = cg_.getMethodGenerator();
        mg.loadCurrentScope(isInClassBuilder(), isInSingletonMethod(), isInGlobalScope(), isInBlock());
        mg.RubyModule_getClassVariable(name);
    }

    public void visitClassVariableAssignmentOperator(String name, boolean rhs_is_method_call,
            boolean is_multiple_assign) {
        MethodGenerator mg = cg_.getMethodGenerator();
        if (rhs_is_method_call) {
            mg.RubyAPI_expandArrayIfThereIsZeroOrOneValue();
        }

        mg.loadCurrentScope(isInClassBuilder(), isInSingletonMethod(), isInGlobalScope(), isInBlock());
        mg.swap();
        mg.RubyModule_setClassVariable(name);
        if (is_multiple_assign) {
            mg.pop();
        }
    }

    public void visitInstanceVariableExpression(String name) {
        visitSelfExpression();
        cg_.getMethodGenerator().RubyValue_getInstanceVariable(name);
    }

    public void visitInstanceVariableAssignmentOperator(String name, boolean rhs_is_method_call,
            boolean is_multiple_assign) {
        MethodGenerator mg = cg_.getMethodGenerator();
        if (rhs_is_method_call) {
            mg.RubyAPI_expandArrayIfThereIsZeroOrOneValue();
        }

        visitSelfExpression();
        mg.swap();
        mg.RubyValue_setInstanceVariable(name);
        if (is_multiple_assign) {
            mg.pop();
        }
    }

    public void visitMrhs(int var, int index, boolean asterisk) {
        MethodGenerator mg = cg_.getMethodGenerator();
        mg.loadLocal(var);
        if (asterisk) {
            mg.RubyArray_collect(index);
        } else {
            mg.RubyArray_get(index);
        }
    }

    public int visitMultipleAssignment(boolean single_lhs, boolean has_mlhs, boolean has_mrhs) {
        MethodGenerator mg = cg_.getMethodGenerator();
        mg.dup();

        if (single_lhs && !has_mrhs) {
            return 0;
        }

        if (single_lhs) {
            mg.RubyAPI_expandArrayIfThereIsZeroOrOneValue2();
            return 0;
        } else if (has_mlhs) {
            mg.RubyAPI_expandArrayIfThereIsOnlyOneRubyArray();
            return mg.saveRubyArrayAsLocalVariable();
        } else {
            return mg.saveRubyArrayAsLocalVariable();
        }
    }

    public int visitNestedVariable(boolean single_lhs, boolean has_mlhs) {
        if (single_lhs) {
            return 0;
        } else {
            MethodGenerator mg = cg_.getMethodGenerator();
            mg.RubyAPI_convertToArrayIfNotYet();
            if (has_mlhs) {
                mg.RubyAPI_expandArrayIfThereIsOnlyOneRubyArray();
            }
            return mg.saveRubyArrayAsLocalVariable();
        }
    }

    public void visitBreak() {
        MethodGenerator mg = cg_.getMethodGenerator();
        if (isInBlock() && mg.getLabelManager().isAtTopLevel()) {
            invokeFinallyIfExist();
            mg.RubyBlock__break__();
            mg.returnValue();
        } else {
            invokeFinallyIfExist();
            mg.goTo(mg.getLabelManager().getCurrentBreak());
        }
    }

    public void visitNext() {
        MethodGenerator mg = cg_.getMethodGenerator();
        if (isInBlock() && mg.getLabelManager().isAtTopLevel()) {
            mg.returnValue();
        } else {
            mg.pop();
            mg.goTo(mg.getLabelManager().getCurrentNext());
        }
    }

    public void visitRedo() {
        MethodGenerator mg = cg_.getMethodGenerator();
        mg.goTo(mg.getLabelManager().getCurrentRedo());
    }

    public void visitRetry() {
        MethodGenerator mg = cg_.getMethodGenerator();
        if (isInBlock() && mg.getLabelManager().isAtTopLevel()) {
            mg.RubyBlock__retry__();
            mg.loadNil();
            mg.returnValue();
        } else {
            mg.goTo(mg.getEnsureLabelManager().getCurrentRetry());
        }
    }

    public void visitExclusiveRangeOperator() {
        MethodGenerator mg = cg_.getMethodGenerator();
        mg.push(true);
        mg.ObjectFactory_createRange();
    }

    public void visitInclusiveRangeOperator() {
        MethodGenerator mg = cg_.getMethodGenerator();
        mg.push(false);
        mg.ObjectFactory_createRange();
    }

    public void visitCurrentNamespaceConstant(String name) {
        if (isInGlobalScope()) {
            visitTopLevelConstant(name);
            return;
        }

        MethodGenerator mg = cg_.getMethodGenerator();
        mg.loadCurrentScope(isInClassBuilder(), isInSingletonMethod(), isInGlobalScope(), isInBlock());
        mg.RubyAPI_getCurrentNamespaceConstant(name);
    }

    public void visitConstant(String name) {
        cg_.getMethodGenerator().RubyAPI_getConstant(name);
    }

    public void visitTopLevelConstant(String name) {
        //quick access for builtin
        MethodGenerator mg = cg_.getMethodGenerator();
        if (mg.RubyRuntime_getBuiltinClass(name)) {
            return;
        } else if (mg.RubyRuntime_getBuiltinModule(name)) {
            return;
        }

        loadTopScope();
        mg.RubyAPI_getCurrentNamespaceConstant(name);
    }

    private void loadTopScope() {
        MethodGenerator mg = cg_.getMethodGenerator();
        if (isInGlobalScope()) {
            mg.loadCurrentClass(false);
        } else {
            mg.RubyRuntime_GlobalScope();
        }
    }

    public void visitCurrentNamespaceConstantAssignmentOperator(String name, boolean rhs_is_method_call,
            boolean is_multiple_assign) {
        if (isInGlobalScope()) {
            visitTopLevelConstantAssignmentOperator(name, rhs_is_method_call, is_multiple_assign);
            return;
        }

        cg_.getMethodGenerator().loadCurrentScope(isInClassBuilder(), isInSingletonMethod(), isInGlobalScope(),
                isInBlock());
        visitConstantAssignmentOperator(name, rhs_is_method_call, is_multiple_assign);
    }

    public void visitConstantAssignmentOperator(String name, boolean rhs_is_method_call,
            boolean is_multiple_assignment) {
        //TODO handle rhs_is_method_call and is_multiple_assignment
        cg_.getMethodGenerator().RubyAPI_setConstant(name);
    }

    public void visitTopLevelConstantAssignmentOperator(String name, boolean rhs_is_method_call,
            boolean is_multiple_assignment) {
        //TODO handle rhs_is_method_call and is_multiple_assignment
        //TODO work with eval/binding
        cg_.getMethodGenerator().RubyAPI_setTopLevelConstant(name);
    }

    public void visitDefinedPublicMethod(String name) {
        MethodGenerator mg = cg_.getMethodGenerator();
        mg.loadCurrentScope(isInClassBuilder(), isInSingletonMethod(), isInGlobalScope(), isInBlock());
        mg.RubyAPI_isDefinedPublicMethod(name);
    }

    public void visitDefinedCurrentNamespaceConstant(String name) {
        if (Types.isBuiltinClass(name) || Types.isBuiltinModule(name)) {
            visitStringExpression("constant");
            return;
        }

        MethodGenerator mg = cg_.getMethodGenerator();
        mg.loadCurrentScope(isInClassBuilder(), isInSingletonMethod(), isInGlobalScope(), isInBlock());
        mg.RubyAPI_isDefinedCurrentNamespaceConstant(name);
    }

    public void visitDefinedTopLevelConstant(String name) {
        if (Types.isBuiltinClass(name) || Types.isBuiltinModule(name)) {
            visitStringExpression("constant");
            return;
        }

        loadTopScope();
        cg_.getMethodGenerator().RubyAPI_isDefinedCurrentNamespaceConstant(name);
    }

    public void visitDefinedConstant(String name) {
        cg_.getMethodGenerator().RubyAPI_isDefinedCurrentNamespaceConstant(name);
    }

    public void visitDefinedMethod(String name) {
        cg_.getMethodGenerator().RubyAPI_isDefinedMethod(name);
    }

    public void visitDefinedSuperMethod() {
        if (cg_ instanceof ClassGeneratorForRubyMethod) {
            visitSelfExpression();
            cg_.getMethodGenerator()
                    .RubyAPI_isDefinedSuperMethod(((ClassGeneratorForRubyMethod) cg_).getOrginalMethodName());
        } else {
            visitNilExpression();
        }
    }

    public void visitDefinedLocalVariable(String name) {
        if (cg_.getSymbolTable().isDefinedInCurrentScope(name)) {
            visitStringExpression("local-variable");
        } else {
            visitNilExpression();
        }
    }

    public void visitDefinedInstanceVariable(String name) {
        if (cg_ instanceof ClassGeneratorForRubyMethod) {
            visitSelfExpression();
            cg_.getMethodGenerator().RubyAPI_isDefinedInstanceVariable(name);
        } else {
            visitNilExpression();
        }
    }

    public void visitDefinedYield() {
        if (cg_ instanceof ClassGeneratorForRubyMethod) {
            MethodGenerator mg = cg_.getMethodGenerator();
            mg.loadCurrentBlock();//.loadArg(2);
            mg.RubyAPI_isDefinedYield();
        } else {
            visitNilExpression();
        }
    }

    public boolean isDefinedInCurrentScope(String name) {
        return cg_.isDefinedInCurrentScope(name);
    }

    public void visitSpecialLambdaCallBegin() {
        visitSelfExpression();
    }

    public void visitSpecialLambdaCallEnd() {
        cg_.getMethodGenerator().RubyBlock_invoke(isInBlock());
    }

    public void visitPotentialProcCall() {
        MethodGenerator mg = cg_.getMethodGenerator();
        mg.dup();
        mg.instanceOf(Types.RUBY_PROC_TYPE);

        Label label1 = new Label();
        mg.ifZCmp(GeneratorAdapter.EQ, label1);

        mg.dup();
        mg.checkCast(Types.RUBY_PROC_TYPE);

        //check if in the right context
        //TODO have not considered all the situations
        mg.dup();
        mg.RubyProc_isDefinedInAnotherBlock();
        Label label2 = new Label();
        mg.ifZCmp(GeneratorAdapter.NE, label2);

        cg_.addVariableToBinding();//TODO should we use updateBinding()?
        mg.mark(label2);
        mg.pop();

        mg.mark(label1);
    }

    public void visitMultipleArrayAssign() {
        cg_.getMethodGenerator().RubyAPI_callArraySet();
    }

    // ---------------------------
    //   Interfaces for debugger
    // ---------------------------
    public Label visitLineLabel(int lineNumber) {
        if (enableDebug) {
            // store the current line, if debug is enabled
            currentLineLabel = cg_.getMethodGenerator().mark();
            cg_.getMethodGenerator().visitLineNumber(lineNumber, currentLineLabel);

            return currentLineLabel;
        }

        return null;
    }

}