case OPASGNNODE:
case OPASGNANDNODE:
case OPASGNORNODE:
case OPELEMENTASGNNODE:
case INSTASGNNODE: // simple assignment cases
return new StringLiteral("assignment");
case DVARNODE:
return new StringLiteral("local-variable(in-block)");
case FALSENODE:
return new StringLiteral("false");
case TRUENODE:
return new StringLiteral("true");
case LOCALVARNODE:
return new StringLiteral("local-variable");
case MATCH2NODE:
case MATCH3NODE:
return new StringLiteral("method");
case NILNODE:
return new StringLiteral("nil");
case SELFNODE:
return new StringLiteral("self");
case CONSTNODE: {
Label defLabel = s.getNewLabel();
Label doneLabel = s.getNewLabel();
Variable tmpVar = s.getNewTemporaryVariable();
String constName = ((ConstNode) node).getName();
s.addInstr(new LexicalSearchConstInstr(tmpVar, startingSearchScope(s), constName));
s.addInstr(BNEInstr.create(tmpVar, UndefinedValue.UNDEFINED, defLabel));
s.addInstr(new InheritanceSearchConstInstr(tmpVar, findContainerModule(s), constName, false)); // SSS FIXME: should this be the current-module var or something else?
s.addInstr(BNEInstr.create(tmpVar, UndefinedValue.UNDEFINED, defLabel));
s.addInstr(new CopyInstr(tmpVar, manager.getNil()));
s.addInstr(new JumpInstr(doneLabel));
s.addInstr(new LabelInstr(defLabel));
s.addInstr(new CopyInstr(tmpVar, new StringLiteral("constant")));
s.addInstr(new LabelInstr(doneLabel));
return tmpVar;
}
case GLOBALVARNODE:
return buildDefinitionCheck(s, new GlobalIsDefinedInstr(s.getNewTemporaryVariable(), new StringLiteral(((GlobalVarNode) node).getName())), "global-variable");
case INSTVARNODE:
return buildDefinitionCheck(s, new HasInstanceVarInstr(s.getNewTemporaryVariable(), getSelf(s), new StringLiteral(((InstVarNode) node).getName())), "instance-variable");
case YIELDNODE:
return buildDefinitionCheck(s, new BlockGivenInstr(s.getNewTemporaryVariable()), "yield");
case BACKREFNODE:
return buildDefinitionCheck(s, new BackrefIsMatchDataInstr(s.getNewTemporaryVariable()), "$" + ((BackRefNode) node).getType());
case NTHREFNODE: {
// SSS FIXME: Is there a reason to do this all with low-level IR?
// Can't this all be folded into a Java method that would be part
// of the runtime library, which then can be used by buildDefinitionCheck method above?
// This runtime library would be used both by the interpreter & the compiled code!
/* -------------------------------------------------------------------------------------
* We have to generate IR for this:
* v = backref; (!(v instanceof RubyMatchData) || v.group(n).nil?) ? nil : "$#{n}"
*
* which happens to be identical to: (where nthRef implicitly fetches backref again!)
* v = backref; (!(v instanceof RubyMatchData) || nthRef(n).nil?) ? nil : "$#{n}"
*
* I am using the second form since it let us encode it in fewer IR instructions.
* But, note that this second form is not as clean as the first one plus it fetches backref twice!
* ------------------------------------------------------------------------------------- */
int n = ((NthRefNode) node).getMatchNumber();
Label undefLabel = s.getNewLabel();
Variable tmpVar = s.getNewTemporaryVariable();
s.addInstr(new BackrefIsMatchDataInstr(tmpVar));
s.addInstr(BEQInstr.create(tmpVar, manager.getFalse(), undefLabel));
// SSS FIXME:
// - Can/should I use BEQInstr(new NthRef(n), manager.getNil(), undefLabel)? instead of .nil? & compare with flag?
// - Or, even create a new IsNilInstr and NotNilInstr to represent optimized scenarios where
// the nil? method is not monkey-patched?
// This matters because if String.nil? is monkey-patched, the two sequences can behave differently.
s.addInstr(CallInstr.create(tmpVar, new MethAddr("nil?"), new NthRef(n), NO_ARGS, null));
s.addInstr(BEQInstr.create(tmpVar, manager.getTrue(), undefLabel));
return buildDefnCheckIfThenPaths(s, undefLabel, new StringLiteral("$" + n));
}
case COLON3NODE:
case COLON2NODE: {
// SSS FIXME: Is there a reason to do this all with low-level IR?
// Can't this all be folded into a Java method that would be part
// of the runtime library, which then can be used by buildDefinitionCheck method above?
// This runtime library would be used both by the interpreter & the compiled code!
final Colon3Node iVisited = (Colon3Node) node;
final String name = iVisited.getName();
// store previous exception for restoration if we rescue something
Variable errInfo = s.getNewTemporaryVariable();
s.addInstr(new GetErrorInfoInstr(errInfo));
CodeBlock protectedCode = new CodeBlock() {
public Operand run(Object[] args) {
IRScope s = (IRScope)args[0];
Node n = (Node)args[1];
String name = (String)args[2];
Operand v = (n instanceof Colon2Node) ? build(((Colon2Node)n).getLeftNode(), s) : new ObjectClass();
Variable tmpVar = s.getNewTemporaryVariable();
s.addInstr(new GetDefinedConstantOrMethodInstr(tmpVar, v, new StringLiteral(name)));
return tmpVar;
}
};
// rescue block
CodeBlock rescueBlock = new CodeBlock() {
public Operand run(Object[] args) {
// Nothing to do -- ignore the exception, and restore stashed error info!
IRScope m = (IRScope)args[0];
m.addInstr(new RestoreErrorInfoInstr((Operand) args[1]));
return manager.getNil();
}
};
// Try verifying definition, and if we get an JumpException exception, process it with the rescue block above
return protectCodeWithRescue(s, protectedCode, new Object[]{s, iVisited, name}, rescueBlock, new Object[] {s, errInfo});
}
case FCALLNODE: {
/* ------------------------------------------------------------------
* Generate IR for:
* r = self/receiver
* mc = r.metaclass
* return mc.methodBound(meth) ? buildGetArgumentDefn(..) : false
* ----------------------------------------------------------------- */
Label undefLabel = s.getNewLabel();
Variable tmpVar = s.getNewTemporaryVariable();
StringLiteral mName = new StringLiteral(((FCallNode)node).getName());
s.addInstr(new IsMethodBoundInstr(tmpVar, getSelf(s), mName));
s.addInstr(BEQInstr.create(tmpVar, manager.getFalse(), undefLabel));
Operand argsCheckDefn = buildGetArgumentDefinition(((FCallNode) node).getArgsNode(), s, "method");
return buildDefnCheckIfThenPaths(s, undefLabel, argsCheckDefn);
}
case VCALLNODE:
return buildDefinitionCheck(s, new IsMethodBoundInstr(s.getNewTemporaryVariable(), getSelf(s), new StringLiteral(((VCallNode) node).getName())), "method");
case CALLNODE: {
// SSS FIXME: Is there a reason to do this all with low-level IR?
// Can't this all be folded into a Java method that would be part
// of the runtime library?
Label undefLabel = s.getNewLabel();
CallNode iVisited = (CallNode) node;
Operand receiverDefn = buildGetDefinition(iVisited.getReceiverNode(), s);
s.addInstr(BEQInstr.create(receiverDefn, manager.getNil(), undefLabel));
// protected main block
CodeBlock protectedCode = new CodeBlock() {
public Operand run(Object[] args) {
IRScope s = (IRScope)args[0];
CallNode iVisited = (CallNode)args[1];
String methodName = iVisited.getName();
Variable tmpVar = s.getNewTemporaryVariable();
Operand receiver = build(iVisited.getReceiverNode(), s);
s.addInstr(new MethodDefinedInstr(tmpVar, receiver, new StringLiteral(methodName)));
return buildDefnCheckIfThenPaths(s, (Label)args[2], tmpVar);
}
};
// rescue block
CodeBlock rescueBlock = new CodeBlock() {
public Operand run(Object[] args) { return manager.getNil(); } // Nothing to do if we got an exception
};
// Try verifying definition, and if we get an exception, throw it out, and return nil
return protectCodeWithRescue(s, protectedCode, new Object[]{s, iVisited, undefLabel}, rescueBlock, null);
}
case CLASSVARNODE: {
// SSS FIXME: Is there a reason to do this all with low-level IR?
// Can't this all be folded into a Java method that would be part
// of the runtime library, which would be used both by the interpreter & the compiled code!
/* --------------------------------------------------------------------------
* Generate IR for this ruby pseudo-code:
* cm = tc.getCurrentScope.getStaticScope.getModule || self.metaclass
* cm.isClassVarDefined ? "class variable" : nil
* ------------------------------------------------------------------------------ */
ClassVarNode iVisited = (ClassVarNode) node;
Operand cm = classVarDefinitionContainer(s);
return buildDefinitionCheck(s, new ClassVarIsDefinedInstr(s.getNewTemporaryVariable(), cm, new StringLiteral(iVisited.getName())), "class variable");
}
case ATTRASSIGNNODE: {
Label undefLabel = s.getNewLabel();
AttrAssignNode iVisited = (AttrAssignNode) node;
Operand receiverDefn = buildGetDefinition(iVisited.getReceiverNode(), s);
s.addInstr(BEQInstr.create(receiverDefn, manager.getNil(), undefLabel));
// protected main block
CodeBlock protectedCode = new CodeBlock() {
public Operand run(Object[] args) {
/* --------------------------------------------------------------------------
* This basically combines checks from CALLNODE and FCALLNODE
*
* Generate IR for this sequence
*
* 1. r = receiver
* 2. mc = r.metaClass
* 3. v = mc.getVisibility(methodName)
* 4. f = !v || v.isPrivate? || (v.isProtected? && receiver/self?.kindof(mc.getRealClass))
* 5. return !f && mc.methodBound(attrmethod) ? buildGetArgumentDefn(..) : false
*
* Hide the complexity of instrs 2-4 into a verifyMethodIsPublicAccessible call
* which can executely entirely in Java-land. No reason to expose the guts in IR.
* ------------------------------------------------------------------------------ */
IRScope s = (IRScope)args[0];
AttrAssignNode iVisited = (AttrAssignNode)args[1];
Label undefLabel = (Label)args[2];
StringLiteral attrMethodName = new StringLiteral(iVisited.getName());
Variable tmpVar = s.getNewTemporaryVariable();
Operand receiver = build(iVisited.getReceiverNode(), s);
s.addInstr(new MethodIsPublicInstr(tmpVar, receiver, attrMethodName));
s.addInstr(BEQInstr.create(tmpVar, manager.getFalse(), undefLabel));
s.addInstr(new IsMethodBoundInstr(tmpVar, getSelf(s), attrMethodName));
s.addInstr(BEQInstr.create(tmpVar, manager.getFalse(), undefLabel));
Operand argsCheckDefn = buildGetArgumentDefinition(((AttrAssignNode) node).getArgsNode(), s, "assignment");
return buildDefnCheckIfThenPaths(s, undefLabel, argsCheckDefn);
}
};
// rescue block
CodeBlock rescueBlock = new CodeBlock() {
public Operand run(Object[] args) { return manager.getNil(); } // Nothing to do if we got an exception
};
// Try verifying definition, and if we get an JumpException exception, process it with the rescue block above
return protectCodeWithRescue(s, protectedCode, new Object[]{s, iVisited, undefLabel}, rescueBlock, null);
}
case ZSUPERNODE:
return buildDefinitionCheck(s, new SuperMethodBoundInstr(s.getNewTemporaryVariable(), getSelf(s)), "super");
case SUPERNODE: {
Label undefLabel = s.getNewLabel();
Variable tmpVar = s.getNewTemporaryVariable();
s.addInstr(new SuperMethodBoundInstr(tmpVar, getSelf(s)));
s.addInstr(BEQInstr.create(tmpVar, manager.getFalse(), undefLabel));
Operand superDefnVal = buildGetArgumentDefinition(((SuperNode) node).getArgsNode(), s, "super");
return buildDefnCheckIfThenPaths(s, undefLabel, superDefnVal);
}
default: {
// protected code
CodeBlock protectedCode = new CodeBlock() {
public Operand run(Object[] args) {
build((Node)args[0], (IRScope)args[1]);
// always an expression as long as we get through here without an exception!
return new StringLiteral("expression");
}
};
// rescue block
CodeBlock rescueBlock = new CodeBlock() {
public Operand run(Object[] args) { return manager.getNil(); } // Nothing to do if we got an exception