Java tutorial
/** * Copyright 2005-2007 Xue Yong Zhi * Distributed under the BSD License */ package com.xruby.compiler.codegen; import org.objectweb.asm.*; import org.objectweb.asm.commons.*; import com.xruby.runtime.lang.RubyBinding; import java.util.*; class FieldManager { private final FieldManager parent_; private final ClassGeneratorForRubyBlock cg_; private final Set<String> fields_ = new HashSet<String>();//assigned fields are fields as well private final Set<String> assigned_fields_ = new HashSet<String>(); private final Set<String> previous_blocks_ = new HashSet<String>(); public FieldManager(FieldManager parent, ClassGeneratorForRubyBlock cg) { parent_ = parent; cg_ = cg; } public void addPreviousBlock(String block) { previous_blocks_.add(block); } public void addField(String s) { fields_.add(s); if (null != parent_) { parent_.addField(s); } } public void addAssignedField(String s, boolean is_for_in_expression) { fields_.add(s); if (is_for_in_expression || cg_.isDefinedInOwnerScope(s)) { assigned_fields_.add(s); } if (null != parent_) { parent_.addField(s); parent_.addAssignedField(s, false); } } public String[] getPreviousBlocks() { return previous_blocks_.toArray(new String[previous_blocks_.size()]); } public String[] getFields() { return fields_.toArray(new String[fields_.size()]); } public String[] getAssignedFields() { return assigned_fields_.toArray(new String[assigned_fields_.size()]); } } class ClassGeneratorForRubyBlock extends ClassGenerator { private final SymbolTable symbol_table_of_the_current_scope_; private final int argc_; private final boolean has_asterisk_parameter_; private final int default_argc_; private final boolean is_for_in_expression_;//for..in expression does not introduce new scope private final RubyBinding binding_; private final FieldManager field_manager_; private final String file_name_; private final ClassGeneratorForRubyBlockHelper helper; private final ClassGenerator owner_; public ClassGeneratorForRubyBlock(String name, String file_name, int argc, boolean has_asterisk_parameter, int default_argc, ClassGenerator owner, boolean is_for_in_expression, boolean has_extra_comma, RubyBinding binding) { super(name); this.helper = ClassGeneratorForRubyBlockHelper.getHelper(argc, has_asterisk_parameter, is_for_in_expression, has_extra_comma); this.file_name_ = file_name; symbol_table_of_the_current_scope_ = owner.getSymbolTable(); mg_for_run_method_ = visitRubyBlock(); argc_ = argc; has_asterisk_parameter_ = has_asterisk_parameter; default_argc_ = default_argc; is_for_in_expression_ = is_for_in_expression; binding_ = binding; field_manager_ = new FieldManager( (owner instanceof ClassGeneratorForRubyBlock) ? ((ClassGeneratorForRubyBlock) owner).field_manager_ : null, this); owner_ = owner; } String getOrginalMethodName() { if (owner_ instanceof ClassGeneratorForRubyMethod) { return ((ClassGeneratorForRubyMethod) owner_).getOrginalMethodName(); } else if (owner_ instanceof ClassGeneratorForRubyBlock) { return ((ClassGeneratorForRubyBlock) owner_).getOrginalMethodName(); } else { return null; } } private String findAndSetupPreviousBlocks(String var) { if (owner_ instanceof ClassGeneratorForRubyBlock) { String s = ((ClassGeneratorForRubyBlock) owner_).findAndSetupPreviousBlocks(var); if (null != s) { getSymbolTable().addVariblesAssignedInBlock(s, -1, new String[] { var }); } return s; } else { if (((SymbolTableForBlock) getSymbolTable()).isDefinedInOwnerScope(var)) { field_manager_.addAssignedField(var, false); return name_; } else { return null; } } } public void callSuperMethod(boolean has_no_arg, boolean has_one_arg) { if (has_one_arg || (has_no_arg && OwnerHasOneArg())) { getMethodGenerator().RubyAPI_callSuperOneArgMethod(); } else if (has_no_arg && OwnerHasNoArg()) { getMethodGenerator().RubyAPI_callSuperNoArgMethod(); } else { getMethodGenerator().RubyAPI_callSuperMethod(); } } public void loadNoParameterForSuper() { if (OwnerHasOneArg()) { getMethodGenerator().RubyBlock_argOfCurrentMethod_(); } else { getMethodGenerator().RubyBlock_argsOfCurrentMethod_(); } } private boolean OwnerHasOneArg() { if (owner_ instanceof ClassGeneratorForRubyBlock) { return ((ClassGeneratorForRubyBlock) owner_).OwnerHasOneArg(); } else if (owner_ instanceof ClassGeneratorForOneArgRubyMethod) { return true; } else { assert (owner_ instanceof ClassGeneratorForRubyMethod); return false; } } private boolean OwnerHasNoArg() { if (owner_ instanceof ClassGeneratorForRubyBlock) { return ((ClassGeneratorForRubyBlock) owner_).OwnerHasNoArg(); } else if (owner_ instanceof ClassGeneratorForNoArgRubyMethod) { return true; } else { assert (owner_ instanceof ClassGeneratorForRubyMethod); return false; } } private void loadField(String name) { mg_for_run_method_.loadThis(); mg_for_run_method_.getField(Type.getType("L" + name_ + ";"), decorateName(name), Types.RUBY_VALUE_TYPE); } private void storeField(String name) { getMethodGenerator().loadThis(); getMethodGenerator().swap(); getMethodGenerator().putField(Type.getType("L" + name_ + ";"), decorateName(name), Types.RUBY_VALUE_TYPE); } private void addNewFieldToClass(String name) { FieldVisitor fv = cv_.visitField(Opcodes.ACC_PUBLIC, decorateName(name), Type.getDescriptor(Types.RUBY_VALUE_CLASS), null, null); fv.visitEnd(); } public String[] getFields() { return field_manager_.getFields(); } public String[] getAssignedFields() { return field_manager_.getAssignedFields(); } public String[] getPreviousBlocks() { return field_manager_.getPreviousBlocks(); } private void addNewBlockFieldToClass(String name) { FieldVisitor fv = cv_.visitField(Opcodes.ACC_PUBLIC, getNameFromFullpath(name), Type.getType("L" + name + ";").getDescriptor(), null, null); fv.visitEnd(); } static String getNameFromFullpath(String s) { return '$' + s.substring(s.lastIndexOf('/') + 1); } void getSharedBlock(String name) { int i = getSymbolTable().getBlock(name); if (i >= 0) { getMethodGenerator().loadLocal(i); } else { getMethodGenerator().loadThis(); if (name != name_) { getMethodGenerator().getField(Type.getType("L" + name_ + ";"), getNameFromFullpath(name), Type.getType("L" + name + ";")); } } } public void loadVariable(String name) { if (isDefinedInOwnerScope(name)) { //findAndSetupPreviousBlocks(name); SymbolTable.VariableAssignedInBlock record = getSymbolTable().getVariableAssignedInBlock(name); if (null != record) { //have been assigned in previous block. e.g. x=1;1.times{x=2};1.times{x=3} field_manager_.addPreviousBlock(record.block_name_); getSharedBlock(record.block_name_); mg_for_run_method_.getField(Type.getType("L" + record.block_name_ + ";"), decorateName(name), Types.RUBY_VALUE_TYPE); } else { field_manager_.addField(name); loadField(name); } } else { super.loadVariable(name); } } public void storeVariable(String name) { if (is_for_in_expression_ || isDefinedInOwnerScope(name)) { findAndSetupPreviousBlocks(name); SymbolTable.VariableAssignedInBlock record = getSymbolTable().getVariableAssignedInBlock(name); if (null != record) { field_manager_.addPreviousBlock(record.block_name_); getSharedBlock(record.block_name_); getMethodGenerator().swap(); getMethodGenerator().putField(Type.getType("L" + record.block_name_ + ";"), decorateName(name), Types.RUBY_VALUE_TYPE); } else { field_manager_.addAssignedField(name, is_for_in_expression_); storeField(name); if (is_for_in_expression_) { updateBinding(binding_, name); } } } else { super.storeVariable(name); } } private void initialFiledUsingBlockParameter(String name) { if (isDefinedInOwnerScope(name)) { field_manager_.addAssignedField(name, false); getMethodGenerator().loadThis(); super.loadVariable(name); getMethodGenerator().putField(Type.getType("L" + name_ + ";"), decorateName(name), Types.RUBY_VALUE_TYPE); } } public void addParameter(String name) { super.addParameter(name); initialFiledUsingBlockParameter(name); } public void setAsteriskParameter(String name, int argc) { super.setAsteriskParameter(name, argc); initialFiledUsingBlockParameter(name); } public void setBlockParameter(String name) { super.setBlockParameter(name); initialFiledUsingBlockParameter(name); } private MethodGenerator visitRubyBlock() { cv_.visit(CgConfig.TARGET_VERSION, Opcodes.ACC_PUBLIC, //need to modify fields when doing Proc#call, see RubyProc.java name_, null, // signature this.helper.getSuperName(), // superName null // interface ); // set source file's name, for debug if (file_name_ != null) { cv_.visitSource(file_name_, null); } return new MethodGenerator(Opcodes.ACC_PROTECTED, Method.getMethod(this.helper.getRunMethodName()), cv_, null, symbol_table_of_the_current_scope_, false); } static String buildContructorSignature() { return "void <init> (com.xruby.runtime.lang.RubyValue, com.xruby.runtime.lang.RubyValue, com.xruby.runtime.builtin.RubyArray, com.xruby.runtime.lang.RubyBlock, com.xruby.runtime.lang.RubyModule, com.xruby.runtime.lang.RubyMethod, boolean)"; } public void createFieldsAndConstructorOfRubyBlock(String[] commons, String[] blocks) { createConstructorOfRubyBlock(); for (String name : commons) { addNewFieldToClass(name); } for (String name : blocks) { addNewBlockFieldToClass(name); } } private void createConstructorOfRubyBlock() { MethodGenerator mg = new MethodGenerator(Opcodes.ACC_PUBLIC, Method.getMethod(buildContructorSignature()), cv_, null, symbol_table_of_the_current_scope_, false); mg.loadThis(); this.helper.pushBasciArgForSuperArg(mg, argc_, has_asterisk_parameter_, default_argc_); final int FIXED_PARAMETERS = 7; for (int i = 0; i < FIXED_PARAMETERS; ++i) { mg.loadArg(i); } mg.invokeConstructor(this.helper.getSuperClassType(), Method.getMethod(this.helper.getSuperCtorName())); mg.returnValue(); mg.endMethod(); } private void addVariableToBinding(String s) { MethodGenerator mg = getMethodGenerator(); mg.push(s); field_manager_.addField(s); loadField(s); mg.invokeVirtual(Types.RUBY_BINDING_TYPE, CgUtil.getMethod("addVariable", Types.RUBY_BINDING_TYPE, Type.getType(String.class), Types.RUBY_VALUE_TYPE)); } public void createBinding(boolean isInClassBuilder, boolean isInSingletonMethod, boolean isInGlobalScope, boolean isInBlock) { super.createBinding(isInClassBuilder, isInSingletonMethod, isInGlobalScope, isInBlock); //TODO one level look up is not enough Collection<String> vars = symbol_table_of_the_current_scope_.getLocalVariables(); for (String s : vars) { addVariableToBinding(s); } Collection<String> params = symbol_table_of_the_current_scope_.getParameters(); for (String s : params) { addVariableToBinding(s); } } public boolean isDefinedInOwnerScope(String name) { SymbolTableForBlock st = (SymbolTableForBlock) getSymbolTable(); return st.isDefinedInOwnerScope(name); } public boolean isDefinedInCurrentScope(String name) { if (isDefinedInOwnerScope(name)) { return true; } else { return super.isDefinedInCurrentScope(name); } } public void loadArgOfMethodForBlock() { getMethodGenerator().RubyBlock_argOfCurrentMethod_(); getMethodGenerator().RubyBlock_argsOfCurrentMethod_(); } }