package org.jruby.ir;
import org.jruby.Ruby;
import org.jruby.RubyInstanceConfig;
import org.jruby.RubySymbol;
import org.jruby.ast.*;
import org.jruby.ast.types.INameNode;
import org.jruby.compiler.NotCompilableException;
import org.jruby.runtime.ArgumentDescriptor;
import org.jruby.runtime.ArgumentType;
import org.jruby.ir.instructions.*;
import org.jruby.ir.instructions.defined.GetErrorInfoInstr;
import org.jruby.ir.instructions.defined.RestoreErrorInfoInstr;
import org.jruby.ir.interpreter.InterpreterContext;
import org.jruby.ir.operands.*;
import org.jruby.ir.operands.Boolean;
import org.jruby.ir.operands.Float;
import org.jruby.ir.transformations.inlining.SimpleCloneInfo;
import org.jruby.runtime.CallType;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.RubyEvent;
import org.jruby.runtime.Signature;
import org.jruby.util.ByteList;
import org.jruby.util.CommonByteLists;
import org.jruby.util.DefinedMessage;
import org.jruby.util.KeyValuePair;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.*;
import static org.jruby.ir.instructions.Instr.EMPTY_OPERANDS;
import static org.jruby.ir.instructions.RuntimeHelperCall.Methods.*;
import static org.jruby.ir.operands.ScopeModule.*;
public class IRBuilder {
static final UnexecutableNil U_NIL = UnexecutableNil.U_NIL;
public static Node buildAST(boolean isCommandLineScript, String arg) {
Ruby ruby = Ruby.getGlobalRuntime();
if (isCommandLineScript) return ruby.parse(ByteList.create(arg), "-e", null, 0, false);
FileInputStream fis = null;
try {
File file = new File(arg);
fis = new FileInputStream(file);
long size = file.length();
byte[] bytes = new byte[(int)size];
fis.read(bytes);
System.out.println("-- processing " + arg + " --");
return ruby.parse(new ByteList(bytes), arg, null, 0, false);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
} finally {
try { if (fis != null) fis.close(); } catch(Exception ignored) { }
}
}
private static class IRLoop {
public final IRScope container;
public final IRLoop parentLoop;
public final Label loopStartLabel;
public final Label loopEndLabel;
public final Label iterStartLabel;
public final Label iterEndLabel;
public final Variable loopResult;
public IRLoop(IRScope s, IRLoop outerLoop) {
container = s;
parentLoop = outerLoop;
loopStartLabel = s.getNewLabel("_LOOP_BEGIN");
loopEndLabel = s.getNewLabel("_LOOP_END");
iterStartLabel = s.getNewLabel("_ITER_BEGIN");
iterEndLabel = s.getNewLabel("_ITER_END");
loopResult = s.createTemporaryVariable();
s.setHasLoopsFlag();
}
}
private static class RescueBlockInfo {
Label entryLabel;
Variable savedExceptionVariable;
public RescueBlockInfo(Label l, Variable v) {
entryLabel = l;
savedExceptionVariable = v;
}
}
private static class EnsureBlockInfo {
Label regionStart;
Label start;
Label end;
Label dummyRescueBlockLabel;
Variable savedGlobalException;
boolean needsBacktrace;
Label bodyRescuer;
IRLoop innermostLoop;
RescueNode matchingRescueNode;
List<Instr> instrs;
public EnsureBlockInfo(IRScope s, RescueNode n, IRLoop l, Label bodyRescuer) {
regionStart = s.getNewLabel();
start = s.getNewLabel();
end = s.getNewLabel();
dummyRescueBlockLabel = s.getNewLabel();
instrs = new ArrayList<>();
savedGlobalException = null;
innermostLoop = l;
matchingRescueNode = n;
this.bodyRescuer = bodyRescuer;
needsBacktrace = true;
}
public void addInstr(Instr i) {
instrs.add(i);
}
public void addInstrAtBeginning(Instr i) {
instrs.add(0, i);
}
public void emitBody(IRBuilder b) {
b.addInstr(new LabelInstr(start));
for (Instr i: instrs) {
b.addInstr(i);
}
}
public void cloneIntoHostScope(IRBuilder builder) {
if (savedGlobalException != null) {
if (!needsBacktrace) builder.addInstr(builder.manager.needsBacktrace(true));
builder.addInstr(new PutGlobalVarInstr(builder.symbol("$!"), savedGlobalException));
}
if (instrs.size() == 0) return;
SimpleCloneInfo ii = new SimpleCloneInfo(builder.scope, true);
ii.renameLabel(start);
for (Instr i: instrs) {
if (i instanceof LabelInstr) ii.renameLabel(((LabelInstr)i).getLabel());
}
builder.addInstr(new LabelInstr(ii.getRenamedLabel(start)));
builder.addInstr(new ExceptionRegionStartMarkerInstr(bodyRescuer));
for (Instr instr: instrs) {
Instr clonedInstr = instr.clone(ii);
if (clonedInstr instanceof CallBase) {
CallBase call = (CallBase)clonedInstr;
Operand block = call.getClosureArg(null);
if (block instanceof WrappedIRClosure) builder.scope.addClosure(((WrappedIRClosure)block).getClosure());
}
builder.addInstr(clonedInstr);
}
builder.addInstr(new ExceptionRegionEndMarkerInstr());
}
}
private Stack<RescueBlockInfo> activeRescueBlockStack = new Stack<>();
private Stack<EnsureBlockInfo> activeEnsureBlockStack = new Stack<>();
private Stack<EnsureBlockInfo> ensureBodyBuildStack = new Stack<>();
private Stack<Label> activeRescuers = new Stack<>();
private Stack<IRLoop> loopStack = new Stack<>();
private int _lastProcessedLineNum = -1;
private boolean needsLineNumInfo = false;
public boolean underscoreVariableSeen = false;
public IRLoop getCurrentLoop() {
return loopStack.isEmpty() ? null : loopStack.peek();
}
protected IRBuilder parent;
protected IRManager manager;
protected IRScope scope;
protected List<Instr> instructions;
protected List<Object> argumentDescriptions;
protected boolean needsCodeCoverage;
private boolean executesOnce = true;
protected int afterPrologueIndex = 0;
private TemporaryVariable yieldClosureVariable = null;
private Variable currentModuleVariable = null;
private Variable currentScopeVariable;
public IRBuilder(IRManager manager, IRScope scope, IRBuilder parent) {
this.manager = manager;
this.scope = scope;
this.parent = parent;
this.instructions = new ArrayList<>(50);
this.activeRescuers.push(Label.UNRESCUED_REGION_LABEL);
if (parent != null) executesOnce = parent.executesOnce;
}
private boolean needsCodeCoverage() {
return needsCodeCoverage || parent != null && parent.needsCodeCoverage();
}
public void addArgumentDescription(ArgumentType type, RubySymbol name) {
if (argumentDescriptions == null) argumentDescriptions = new ArrayList<>();
argumentDescriptions.add(type);
argumentDescriptions.add(name);
}
public void addInstr(Instr instr) {
if (needsLineNumInfo) {
needsLineNumInfo = false;
addInstr(manager.newLineNumber(_lastProcessedLineNum));
if (RubyInstanceConfig.FULL_TRACE_ENABLED) {
addInstr(new TraceInstr(RubyEvent.LINE, methodNameFor(), getFileName(), _lastProcessedLineNum + 1));
if (needsCodeCoverage()) {
addInstr(new TraceInstr(RubyEvent.COVERAGE, methodNameFor(), getFileName(), _lastProcessedLineNum + 1));
}
}
}
if (ensureBodyBuildStack.empty()) {
instr.computeScopeFlags(scope);
if (hasListener()) manager.getIRScopeListener().addedInstr(scope, instr, instructions.size());
instructions.add(instr);
} else {
ensureBodyBuildStack.peek().addInstr(instr);
}
}
public void addInstrAtBeginning(Instr instr) {
if (ensureBodyBuildStack.empty()) {
instr.computeScopeFlags(scope);
if (hasListener()) manager.getIRScopeListener().addedInstr(scope, instr, 0);
instructions.add(0, instr);
} else {
ensureBodyBuildStack.peek().addInstrAtBeginning(instr);
}
}
private void emitEnsureBlocks(IRLoop loop) {
int n = activeEnsureBlockStack.size();
EnsureBlockInfo[] ebArray = activeEnsureBlockStack.toArray(new EnsureBlockInfo[n]);
for (int i = n-1; i >= 0; i--) {
EnsureBlockInfo ebi = ebArray[i];
if (loop != null && ebi.innermostLoop != loop) break;
ebi.cloneIntoHostScope(this);
}
}
private void determineIfWeNeedLineNumber(Node node) {
if (node.isNewline()) {
int currLineNum = node.getLine();
if (currLineNum != _lastProcessedLineNum) {
needsLineNumInfo = true;
_lastProcessedLineNum = currLineNum;
}
}
}
private Operand buildOperand(Variable result, Node node) throws NotCompilableException {
determineIfWeNeedLineNumber(node);
switch (node.getNodeType()) {
case ALIASNODE: return buildAlias((AliasNode) node);
case ANDNODE: return buildAnd((AndNode) node);
case ARGSCATNODE: return buildArgsCat((ArgsCatNode) node);
case ARGSPUSHNODE: return buildArgsPush((ArgsPushNode) node);
case ARRAYNODE: return buildArray((ArrayNode) node, false);
case ATTRASSIGNNODE: return buildAttrAssign(result, (AttrAssignNode) node);
case BACKREFNODE: return buildBackref(result, (BackRefNode) node);
case BEGINNODE: return buildBegin((BeginNode) node);
case BIGNUMNODE: return buildBignum((BignumNode) node);
case BLOCKNODE: return buildBlock((BlockNode) node);
case BREAKNODE: return buildBreak((BreakNode) node);
case CALLNODE: return buildCall(result, (CallNode) node, null, null);
case CASENODE: return buildCase((CaseNode) node);
case CLASSNODE: return buildClass((ClassNode) node);
case CLASSVARNODE: return buildClassVar((ClassVarNode) node);
case CLASSVARASGNNODE: return buildClassVarAsgn((ClassVarAsgnNode) node);
case COLON2NODE: return buildColon2((Colon2Node) node);
case COLON3NODE: return buildColon3((Colon3Node) node);
case COMPLEXNODE: return buildComplex((ComplexNode) node);
case CONSTDECLNODE: return buildConstDecl((ConstDeclNode) node);
case CONSTNODE: return searchConst(((ConstNode) node).getName());
case DASGNNODE: return buildDAsgn((DAsgnNode) node);
case DEFINEDNODE: return buildGetDefinition(((DefinedNode) node).getExpressionNode());
case DEFNNODE: return buildDefn((MethodDefNode) node);
case DEFSNODE: return buildDefs((DefsNode) node);
case DOTNODE: return buildDot((DotNode) node);
case DREGEXPNODE: return buildDRegexp(result, (DRegexpNode) node);
case DSTRNODE: return buildDStr(result, (DStrNode) node);
case DSYMBOLNODE: return buildDSymbol(result, (DSymbolNode) node);
case DVARNODE: return buildDVar((DVarNode) node);
case DXSTRNODE: return buildDXStr(result, (DXStrNode) node);
case ENCODINGNODE: return buildEncoding((EncodingNode)node);
case ENSURENODE: return buildEnsureNode((EnsureNode) node);
case EVSTRNODE: return buildEvStr((EvStrNode) node);
case FALSENODE: return buildFalse();
case FCALLNODE: return buildFCall(result, (FCallNode) node);
case FIXNUMNODE: return buildFixnum((FixnumNode) node);
case FLIPNODE: return buildFlip((FlipNode) node);
case FLOATNODE: return buildFloat((FloatNode) node);
case FORNODE: return buildFor((ForNode) node);
case GLOBALASGNNODE: return buildGlobalAsgn((GlobalAsgnNode) node);
case GLOBALVARNODE: return buildGlobalVar(result, (GlobalVarNode) node);
case HASHNODE: return buildHash((HashNode) node);
case IFNODE: return buildIf(result, (IfNode) node);
case INSTASGNNODE: return buildInstAsgn((InstAsgnNode) node);
case INSTVARNODE: return buildInstVar((InstVarNode) node);
case ITERNODE: return buildIter((IterNode) node);
case LAMBDANODE: return buildLambda((LambdaNode)node);
case LITERALNODE: return buildLiteral((LiteralNode) node);
case LOCALASGNNODE: return buildLocalAsgn((LocalAsgnNode) node);
case LOCALVARNODE: return buildLocalVar((LocalVarNode) node);
case MATCH2NODE: return buildMatch2(result, (Match2Node) node);
case MATCH3NODE: return buildMatch3(result, (Match3Node) node);
case MATCHNODE: return buildMatch(result, (MatchNode) node);
case MODULENODE: return buildModule((ModuleNode) node);
case MULTIPLEASGNNODE: return buildMultipleAsgn19((MultipleAsgnNode) node);
case NEXTNODE: return buildNext((NextNode) node);
case NTHREFNODE: return buildNthRef((NthRefNode) node);
case NILNODE: return buildNil();
case OPASGNANDNODE: return buildOpAsgnAnd((OpAsgnAndNode) node);
case OPASGNCONSTDECLNODE: return buildOpAsgnConstDeclNode((OpAsgnConstDeclNode) node);
case OPASGNNODE: return buildOpAsgn((OpAsgnNode) node);
case OPASGNORNODE: return buildOpAsgnOr((OpAsgnOrNode) node);
case OPELEMENTASGNNODE: return buildOpElementAsgn((OpElementAsgnNode) node);
case ORNODE: return buildOr((OrNode) node);
case PREEXENODE: return buildPreExe((PreExeNode) node);
case POSTEXENODE: return buildPostExe((PostExeNode) node);
case RATIONALNODE: return buildRational((RationalNode) node);
case REDONODE: return buildRedo((RedoNode) node);
case REGEXPNODE: return buildRegexp((RegexpNode) node);
case RESCUEBODYNODE:
throw new NotCompilableException("rescue body is handled by rescue compilation at: " + scope.getFile() + ":" + node.getLine());
case RESCUENODE: return buildRescue((RescueNode) node);
case RETRYNODE: return buildRetry();
case RETURNNODE: return buildReturn((ReturnNode) node);
case ROOTNODE:
throw new NotCompilableException("Use buildRoot(); Root node at: " + scope.getFile() + ":" + node.getLine());
case SCLASSNODE: return buildSClass((SClassNode) node);
case SELFNODE: return buildSelf();
case SPLATNODE: return buildSplat((SplatNode) node);
case STRNODE: return buildStr((StrNode) node);
case SUPERNODE: return buildSuper((SuperNode) node);
case SVALUENODE: return buildSValue((SValueNode) node);
case SYMBOLNODE: return buildSymbol((SymbolNode) node);
case TRUENODE: return buildTrue();
case UNDEFNODE: return buildUndef(node);
case UNTILNODE: return buildUntil((UntilNode) node);
case VALIASNODE: return buildVAlias((VAliasNode) node);
case VCALLNODE: return buildVCall(result, (VCallNode) node);
case WHILENODE: return buildWhile((WhileNode) node);
case WHENNODE: assert false : "When nodes are handled by case node compilation."; return null;
case XSTRNODE: return buildXStr((XStrNode) node);
case YIELDNODE: return buildYield((YieldNode) node, result);
case ZARRAYNODE: return buildZArray(result);
case ZSUPERNODE: return buildZSuper((ZSuperNode) node);
default: throw new NotCompilableException("Unknown node encountered in builder: " + node.getClass());
}
}
private boolean hasListener() {
return manager.getIRScopeListener() != null;
}
public IRBuilder newIRBuilder(IRManager manager, IRScope newScope) {
return new IRBuilder(manager, newScope, this);
}
public static IRBuilder topIRBuilder(IRManager manager, IRScope newScope) {
return new IRBuilder(manager, newScope, null);
}
public Operand build(Node node) {
return build(null, node);
}
public Operand build(Variable result, Node node) {
if (node == null) return null;
boolean savedExecuteOnce = executesOnce;
try {
if (executesOnce) executesOnce = node.executesOnce();
if (hasListener()) manager.getIRScopeListener().startBuildOperand(node, scope);
Operand operand = buildOperand(result, node);
if (hasListener()) manager.getIRScopeListener().endBuildOperand(node, scope, operand);
return operand;
} finally {
executesOnce = savedExecuteOnce;
}
}
private InterpreterContext buildLambdaInner(LambdaNode node) {
prepareImplicitState();
addCurrentScopeAndModule();
receiveBlockArgs(node);
Operand closureRetVal = node.getBody() == null ? manager.getNil() : build(node.getBody());
if (closureRetVal != U_NIL) addInstr(new ReturnInstr(closureRetVal));
handleBreakAndReturnsInLambdas();
return scope.allocateInterpreterContext(instructions);
}
public Operand buildLambda(LambdaNode node) {
IRClosure closure = new IRClosure(manager, scope, node.getLine(), node.getScope(), Signature.from(node), needsCodeCoverage);
newIRBuilder(manager, closure).buildLambdaInner(node);
Variable lambda = createTemporaryVariable();
WrappedIRClosure lambdaBody = new WrappedIRClosure(closure.getSelf(), closure);
addInstr(new BuildLambdaInstr(lambda, lambdaBody));
return lambda;
}
public Operand buildEncoding(EncodingNode node) {
Variable ret = createTemporaryVariable();
addInstr(new GetEncodingInstr(ret, node.getEncoding()));
return ret;
}
public Operand buildMultipleAsgn19(MultipleAsgnNode multipleAsgnNode) {
Node valueNode = multipleAsgnNode.getValueNode();
Operand values = build(valueNode);
Variable ret = getValueInTemporaryVariable(values);
if (valueNode instanceof ArrayNode) {
buildMultipleAsgn19Assignment(multipleAsgnNode, null, ret);
} else {
Variable tmp = createTemporaryVariable();
addInstr(new ToAryInstr(tmp, ret));
buildMultipleAsgn19Assignment(multipleAsgnNode, null, tmp);
}
return ret;
}
protected Variable copyAndReturnValue(Operand val) {
return addResultInstr(new CopyInstr(createTemporaryVariable(), val));
}
protected Operand buildWithOrder(Node node, boolean preserveOrder) {
Operand value = build(node);
return preserveOrder && !(value instanceof ImmutableLiteral) ? copyAndReturnValue(value) : value;
}
protected Operand buildLazyWithOrder(CallNode node, Label lazyLabel, Label endLabel, boolean preserveOrder) {
Operand value = buildCall(null, node, lazyLabel, endLabel);
return preserveOrder && !(value instanceof ImmutableLiteral) ? copyAndReturnValue(value) : value;
}
protected Variable getValueInTemporaryVariable(Operand val) {
if (val != null && val instanceof TemporaryVariable) return (Variable) val;
return copyAndReturnValue(val);
}
protected Operand buildAttrAssignCallArgs(List<Operand> argsList, Node args, boolean containsAssignment) {
if (args == null) return manager.getNil();
switch (args.getNodeType()) {
case ARRAYNODE: {
Operand last = manager.getNil();
for (Node n: ((ListNode) args).children()) {
last = buildWithOrder(n, containsAssignment);
argsList.add(last);
}
return last;
}
case ARGSPUSHNODE: {
ArgsPushNode argsPushNode = (ArgsPushNode)args;
Operand lhs = build(argsPushNode.getFirstNode());
Operand rhs = build(argsPushNode.getSecondNode());
Variable res = createTemporaryVariable();
addInstr(new BuildCompoundArrayInstr(res, lhs, rhs, true));
argsList.add(new Splat(res));
return rhs;
}
case SPLATNODE: {
Splat rhs = new Splat(buildSplat((SplatNode)args));
argsList.add(rhs);
return rhs;
}
}
throw new NotCompilableException("Invalid node for attrassign call args: " + args.getClass().getSimpleName() +
":" + scope.getFile() + ":" + args.getLine());
}
protected Operand[] buildCallArgs(Node args) {
return buildCallArgsExcept(args, null);
}
protected Operand[] buildCallArgsExcept(Node args, Node excludeKeywordArg) {
switch (args.getNodeType()) {
case ARGSCATNODE:
case ARGSPUSHNODE:
return new Operand[] { new Splat(addResultInstr(new BuildSplatInstr(createTemporaryVariable(), build(args), false))) };
case ARRAYNODE: {
Node[] children = ((ListNode) args).children();
int numberOfArgs = children.length - (excludeKeywordArg != null ? 1 : 0);
Operand[] builtArgs = new Operand[numberOfArgs];
boolean hasAssignments = args.containsVariableAssignment();
for (int i = 0; i < numberOfArgs; i++) {
builtArgs[i] = buildWithOrder(children[i], hasAssignments);
}
return builtArgs;
}
case SPLATNODE:
return new Operand[] { new Splat(addResultInstr(new BuildSplatInstr(createTemporaryVariable(), build(args), false))) };
}
throw new NotCompilableException("Invalid node for call args: " + args.getClass().getSimpleName() + ":" +
scope.getFile() + ":" + args.getLine());
}
public Operand[] setupCallArgs(Node args) {
return args == null ? Operand.EMPTY_ARRAY : buildCallArgs(args);
}
public static Operand[] addArg(Operand[] args, Operand extraArg) {
Operand[] newArgs = new Operand[args.length + 1];
System.arraycopy(args, 0, newArgs, 0, args.length);
newArgs[args.length] = extraArg;
return newArgs;
}
public void buildAssignment(Node node, Variable rhsVal) {
switch (node.getNodeType()) {
case ATTRASSIGNNODE:
buildAttrAssignAssignment(node, rhsVal);
break;
case CLASSVARASGNNODE:
addInstr(new PutClassVariableInstr(classVarDefinitionContainer(), ((ClassVarAsgnNode)node).getName(), rhsVal));
break;
case CONSTDECLNODE:
buildConstDeclAssignment((ConstDeclNode) node, rhsVal);
break;
case DASGNNODE: {
DAsgnNode variable = (DAsgnNode) node;
int depth = variable.getDepth();
addInstr(new CopyInstr(getLocalVariable(variable.getName(), depth), rhsVal));
break;
}
case GLOBALASGNNODE:
addInstr(new PutGlobalVarInstr(((GlobalAsgnNode)node).getName(), rhsVal));
break;
case INSTASGNNODE:
addInstr(new PutFieldInstr(buildSelf(), ((InstAsgnNode)node).getName(), rhsVal));
break;
case LOCALASGNNODE: {
LocalAsgnNode localVariable = (LocalAsgnNode) node;
int depth = localVariable.getDepth();
addInstr(new CopyInstr(getLocalVariable(localVariable.getName(), depth), rhsVal));
break;
}
case ZEROARGNODE:
throw new NotCompilableException("Shouldn't get here; zeroarg does not do assignment: " + node);
case MULTIPLEASGNNODE: {
Variable tmp = createTemporaryVariable();
addInstr(new ToAryInstr(tmp, rhsVal));
buildMultipleAsgn19Assignment((MultipleAsgnNode)node, null, tmp);
break;
}
default:
throw new NotCompilableException("Can't build assignment node: " + node);
}
}
protected LocalVariable getBlockArgVariable(RubySymbol name, int depth) {
if (!(scope instanceof IRFor)) throw new NotCompilableException("Cannot ask for block-arg variable in 1.9 mode");
return getLocalVariable(name, depth);
}
protected void receiveBlockArg(Variable v, Operand argsArray, int argIndex, boolean isSplat) {
if (argsArray != null) {
if (isSplat) addInstr(new RestArgMultipleAsgnInstr(v, argsArray, argIndex));
else addInstr(new ReqdArgMultipleAsgnInstr(v, argsArray, argIndex));
} else {
addInstr(isSplat ? new ReceiveRestArgInstr(v, argIndex, argIndex) : new ReceivePreReqdArgInstr(v, argIndex));
}
}
public void buildVersionSpecificBlockArgsAssignment(Node node) {
if (!(scope instanceof IRFor)) throw new NotCompilableException("Should not have come here for block args assignment in 1.9 mode: " + node);
switch (node.getNodeType()) {
case MULTIPLEASGNNODE: {
ListNode sourceArray = ((MultipleAsgnNode) node).getPre();
int i = 0;
for (Node an: sourceArray.children()) {
buildBlockArgsAssignment(an, null, i, false);
i++;
}
break;
}
default:
throw new NotCompilableException("Can't build assignment node: " + node);
}
}
public void buildBlockArgsAssignment(Node node, Operand argsArray, int argIndex, boolean isSplat) {
Variable v;
switch (node.getNodeType()) {
case ATTRASSIGNNODE:
v = createTemporaryVariable();
receiveBlockArg(v, argsArray, argIndex, isSplat);
buildAttrAssignAssignment(node, v);
break;
case DASGNNODE: {
DAsgnNode dynamicAsgn = (DAsgnNode) node;
v = getBlockArgVariable(dynamicAsgn.getName(), dynamicAsgn.getDepth());
receiveBlockArg(v, argsArray, argIndex, isSplat);
break;
}
case CLASSVARASGNNODE:
v = createTemporaryVariable();
receiveBlockArg(v, argsArray, argIndex, isSplat);
addInstr(new PutClassVariableInstr(classVarDefinitionContainer(), ((ClassVarAsgnNode)node).getName(), v));
break;
case CONSTDECLNODE:
v = createTemporaryVariable();
receiveBlockArg(v, argsArray, argIndex, isSplat);
buildConstDeclAssignment((ConstDeclNode) node, v);
break;
case GLOBALASGNNODE:
v = createTemporaryVariable();
receiveBlockArg(v, argsArray, argIndex, isSplat);
addInstr(new PutGlobalVarInstr(((GlobalAsgnNode)node).getName(), v));
break;
case INSTASGNNODE:
v = createTemporaryVariable();
receiveBlockArg(v, argsArray, argIndex, isSplat);
addInstr(new PutFieldInstr(buildSelf(), ((InstAsgnNode)node).getName(), v));
break;
case LOCALASGNNODE: {
LocalAsgnNode localVariable = (LocalAsgnNode) node;
v = getBlockArgVariable(localVariable.getName(), localVariable.getDepth());
receiveBlockArg(v, argsArray, argIndex, isSplat);
break;
}
case ZEROARGNODE:
throw new NotCompilableException("Shouldn't get here; zeroarg does not do assignment: " + node);
default:
buildVersionSpecificBlockArgsAssignment(node);
}
}
public Operand buildAlias(final AliasNode alias) {
Operand newName = build(alias.getNewName());
Operand oldName = build(alias.getOldName());
addInstr(new AliasInstr(newName, oldName));
return manager.getNil();
}
public Operand buildAnd(final AndNode andNode) {
if (andNode.getFirstNode().getNodeType().alwaysTrue()) {
build(andNode.getFirstNode());
return build(andNode.getSecondNode());
} else if (andNode.getFirstNode().getNodeType().alwaysFalse()) {
return build(andNode.getFirstNode());
} else {
Label l = getNewLabel();
Operand v1 = build(andNode.getFirstNode());
Variable ret = getValueInTemporaryVariable(v1);
addInstr(createBranch(v1, manager.getFalse(), l));
Operand v2 = build(andNode.getSecondNode());
addInstr(new CopyInstr(ret, v2));
addInstr(new LabelInstr(l));
return ret;
}
}
public Operand buildArray(ArrayNode node, boolean operandOnly) {
Node[] nodes = node.children();
Operand[] elts = new Operand[nodes.length];
boolean containsAssignments = node.containsVariableAssignment();
for (int i = 0; i < nodes.length; i++) {
elts[i] = buildWithOrder(nodes[i], containsAssignments);
}
Operand array = new Array(elts);
return operandOnly ? array : copyAndReturnValue(array);
}
public Operand buildArgsCat(final ArgsCatNode argsCatNode) {
Operand lhs = build(argsCatNode.getFirstNode());
Operand rhs = build(argsCatNode.getSecondNode());
return addResultInstr(new BuildCompoundArrayInstr(createTemporaryVariable(), lhs, rhs, false));
}
public Operand buildArgsPush(final ArgsPushNode node) {
Operand lhs = build(node.getFirstNode());
Operand rhs = build(node.getSecondNode());
return addResultInstr(new BuildCompoundArrayInstr(createTemporaryVariable(), lhs, rhs, true));
}
private Operand buildAttrAssign(Variable result, AttrAssignNode attrAssignNode) {
boolean containsAssignment = attrAssignNode.containsVariableAssignment();
Operand obj = buildWithOrder(attrAssignNode.getReceiverNode(), containsAssignment);
Label lazyLabel = null;
Label endLabel = null;
if (result == null) result = createTemporaryVariable();
if (attrAssignNode.isLazy()) {
lazyLabel = getNewLabel();
endLabel = getNewLabel();
addInstr(new BNilInstr(lazyLabel, obj));
}
List<Operand> args = new ArrayList<>();
Node argsNode = attrAssignNode.getArgsNode();
Operand lastArg = buildAttrAssignCallArgs(args, argsNode, containsAssignment);
addInstr(AttrAssignInstr.create(scope, obj, attrAssignNode.getName(), args.toArray(new Operand[args.size()]), scope.maybeUsingRefinements()));
addInstr(new CopyInstr(result, lastArg));
if (attrAssignNode.isLazy()) {
addInstr(new JumpInstr(endLabel));
addInstr(new LabelInstr(lazyLabel));
addInstr(new CopyInstr(result, manager.getNil()));
addInstr(new LabelInstr(endLabel));
}
return result;
}
public Operand buildAttrAssignAssignment(Node node, Operand value) {
final AttrAssignNode attrAssignNode = (AttrAssignNode) node;
Operand obj = build(attrAssignNode.getReceiverNode());
Operand[] args = setupCallArgs(attrAssignNode.getArgsNode());
args = addArg(args, value);
addInstr(AttrAssignInstr.create(scope, obj, attrAssignNode.getName(), args, scope.maybeUsingRefinements()));
return value;
}
public Operand buildBackref(Variable result, BackRefNode node) {
if (result == null) result = createTemporaryVariable();
return addResultInstr(new BuildBackrefInstr(result, node.getType()));
}
public Operand buildBegin(BeginNode beginNode) {
return build(beginNode.getBodyNode());
}
public Operand buildBignum(BignumNode node) {
return new Bignum(node.getValue());
}
public Operand buildBlock(BlockNode node) {
Operand retVal = null;
for (Node child : node.children()) {
retVal = build(child);
}
return retVal;
}
public Operand buildBreak(BreakNode breakNode) {
IRLoop currLoop = getCurrentLoop();
if (currLoop != null) {
if (!activeEnsureBlockStack.empty()) emitEnsureBlocks(currLoop);
addInstr(new CopyInstr(currLoop.loopResult, build(breakNode.getValueNode())));
addInstr(new JumpInstr(currLoop.loopEndLabel));
} else {
if (scope instanceof IRClosure) {
IRScope returnScope = scope.getLexicalParent();
if (scope instanceof IREvalScript || returnScope == null) {
throwSyntaxError(breakNode, "Can't escape from eval with redo");
} else {
addInstr(new BreakInstr(build(breakNode.getValueNode()), returnScope.getId()));
}
} else {
throwSyntaxError(breakNode, "Invalid break");
}
}
return U_NIL;
}
private void throwSyntaxError(Node node, String message) {
String errorMessage = getFileName() + ":" + (node.getLine() + 1) + ": " + message;
throw scope.getManager().getRuntime().newSyntaxError(errorMessage);
}
private void handleNonlocalReturnInMethod() {
Label rBeginLabel = getNewLabel();
Label rEndLabel = getNewLabel();
Label gebLabel = getNewLabel();
addInstrAtBeginning(new ExceptionRegionStartMarkerInstr(gebLabel));
addInstrAtBeginning(new LabelInstr(rBeginLabel));
addInstr( new ExceptionRegionEndMarkerInstr());
addInstr(new LabelInstr(gebLabel));
Variable exc = createTemporaryVariable();
addInstr(new ReceiveJRubyExceptionInstr(exc));
Variable ret = createTemporaryVariable();
addInstr(new RuntimeHelperCall(ret, HANDLE_NONLOCAL_RETURN, new Operand[]{exc} ));
addInstr(new ReturnInstr(ret));
addInstr(new LabelInstr(rEndLabel));
}
private Operand receiveBreakException(Operand block, CodeBlock codeBlock) {
if (block == null ||
!(block instanceof WrappedIRClosure) ||
!(((WrappedIRClosure)block).getClosure()).flags.contains(IRFlags.HAS_BREAK_INSTRS)) {
return codeBlock.run();
}
Label rBeginLabel = getNewLabel();
Label rEndLabel = getNewLabel();
Label rescueLabel = getNewLabel();
addInstr(new LabelInstr(rBeginLabel));
addInstr(new ExceptionRegionStartMarkerInstr(rescueLabel));
Variable callResult = (Variable)codeBlock.run();
addInstr(new JumpInstr(rEndLabel));
addInstr(new ExceptionRegionEndMarkerInstr());
addInstr(new LabelInstr(rescueLabel));
Variable exc = createTemporaryVariable();
addInstr(new ReceiveJRubyExceptionInstr(exc));
addInstr(new RuntimeHelperCall(callResult, HANDLE_PROPAGATED_BREAK, new Operand[]{exc} ));
addInstr(new LabelInstr(rEndLabel));
return callResult;
}
private void receiveBreakException(Operand block, final CallInstr callInstr) {
receiveBreakException(block, new CodeBlock() { public Operand run() { addInstr(callInstr); return callInstr.getResult(); } });
}
public Operand buildCall(Variable result, CallNode callNode, Label lazyLabel, Label endLabel) {
Node receiverNode = callNode.getReceiverNode();
RubySymbol name = callNode.getName();
String id = name.idString();
if (receiverNode instanceof StrNode && (id.equals("freeze") || id.equals("-@"))) {
StrNode asString = (StrNode) receiverNode;
return new FrozenString(asString.getValue(), asString.getCodeRange(), scope.getFile(), asString.getLine());
}
boolean compileLazyLabel = false;
if (callNode.isLazy()) {
if (lazyLabel == null) {
compileLazyLabel = true;
lazyLabel = getNewLabel();
endLabel = getNewLabel();
}
}
Operand receiver;
if (receiverNode instanceof CallNode && ((CallNode) receiverNode).isLazy()) {
receiver = buildLazyWithOrder((CallNode) receiverNode, lazyLabel, endLabel, callNode.containsVariableAssignment());
} else {
receiver = buildWithOrder(receiverNode, callNode.containsVariableAssignment());
}
if (result == null) result = createTemporaryVariable();
ArrayNode argsAry;
if (!callNode.isLazy() &&
id.equals("[]") &&
callNode.getArgsNode() instanceof ArrayNode &&
(argsAry = (ArrayNode) callNode.getArgsNode()).size() == 1 &&
argsAry.get(0) instanceof StrNode &&
!scope.maybeUsingRefinements() &&
receiverNode instanceof HashNode &&
callNode.getIterNode() == null) {
StrNode keyNode = (StrNode) argsAry.get(0);
addInstr(ArrayDerefInstr.create(scope, result, receiver, new FrozenString(keyNode.getValue(), keyNode.getCodeRange(), scope.getFile(), keyNode.getLine())));
return result;
}
if (callNode.isLazy()) {
addInstr(new BNilInstr(lazyLabel, receiver));
}
HashNode keywordArgs = getPossibleKeywordArgument(callNode.getArgsNode());
CallInstr callInstr;
Operand block;
if (keywordArgs != null) {
Operand[] args = buildCallArgsExcept(callNode.getArgsNode(), keywordArgs);
List<KeyValuePair<Operand, Operand>> kwargs = buildKeywordArguments(keywordArgs);
block = setupCallClosure(callNode.getIterNode());
callInstr = CallInstr.createWithKwargs(scope, CallType.NORMAL, result, name, receiver, args, block, kwargs);
} else {
Operand[] args = setupCallArgs(callNode.getArgsNode());
block = setupCallClosure(callNode.getIterNode());
callInstr = CallInstr.create(scope, result, name, receiver, args, block);
}
determineIfWeNeedLineNumber(callNode);
determineIfProcNew(receiverNode, name, callInstr);
receiveBreakException(block, callInstr);
if (compileLazyLabel) {
addInstr(new JumpInstr(endLabel));
addInstr(new LabelInstr(lazyLabel));
addInstr(new CopyInstr(result, manager.getNil()));
addInstr(new LabelInstr(endLabel));
}
return result;
}
private List<KeyValuePair<Operand, Operand>> buildKeywordArguments(HashNode keywordArgs) {
List<KeyValuePair<Operand, Operand>> kwargs = new ArrayList<>();
for (KeyValuePair<Node, Node> pair: keywordArgs.getPairs()) {
kwargs.add(new KeyValuePair<Operand, Operand>((Symbol) build(pair.getKey()), build(pair.getValue())));
}
return kwargs;
}
private void determineIfProcNew(Node receiverNode, RubySymbol name, CallInstr callInstr) {
if (CommonByteLists.NEW_METHOD.equals(name.getBytes()) &&
receiverNode instanceof ConstNode && ((ConstNode)receiverNode).getName().idString().equals("Proc")) {
callInstr.setProcNew(true);
}
}
private HashNode getPossibleKeywordArgument(Node argsNode) {
if (argsNode instanceof BlockPassNode) {
return null;
}
if (argsNode instanceof ArrayNode) {
ArrayNode argsList = (ArrayNode) argsNode;
if (argsList.isEmpty()) return null;
Node lastNode = argsList.getLast();
if (lastNode instanceof HashNode) {
HashNode hash = (HashNode) lastNode;
if (hash.hasOnlySymbolKeys() && !hash.isEmpty()) return (HashNode) lastNode;
}
}
return null;
}
public Operand buildCase(CaseNode caseNode) {
NodeType seenType = null;
for (Node aCase : caseNode.getCases().children()) {
WhenNode whenNode = (WhenNode)aCase;
NodeType exprNodeType = whenNode.getExpressionNodes().getNodeType();
if (seenType == null) {
seenType = exprNodeType;
} else if (seenType != exprNodeType) {
seenType = null;
break;
}
}
if (seenType != null) {
switch (seenType) {
case FIXNUMNODE:
return buildFixnumCase(caseNode);
}
}
Operand value = build(caseNode.getCaseNode());
if (value == null) value = UndefinedValue.UNDEFINED;
Label endLabel = getNewLabel();
boolean hasElse = (caseNode.getElseNode() != null);
Label elseLabel = getNewLabel();
Variable result = createTemporaryVariable();
List<Label> labels = new ArrayList<>();
Map<Label, Node> bodies = new HashMap<>();
for (Node aCase : caseNode.getCases().children()) {
WhenNode whenNode = (WhenNode)aCase;
Label bodyLabel = getNewLabel();
Variable eqqResult = createTemporaryVariable();
labels.add(bodyLabel);
Operand expression = buildWithOrder(whenNode.getExpressionNodes(), whenNode.containsVariableAssignment());
Node exprNodes = whenNode.getExpressionNodes();
boolean needsSplat = exprNodes instanceof ArgsPushNode || exprNodes instanceof SplatNode || exprNodes instanceof ArgsCatNode;
addInstr(new EQQInstr(scope, eqqResult, expression, value, needsSplat, scope.maybeUsingRefinements()));
addInstr(createBranch(eqqResult, manager.getTrue(), bodyLabel));
bodies.put(bodyLabel, whenNode.getBodyNode());
}
addInstr(new JumpInstr(elseLabel));
if (hasElse) {
labels.add(elseLabel);
bodies.put(elseLabel, caseNode.getElseNode());
}
for (Label whenLabel: labels) {
addInstr(new LabelInstr(whenLabel));
Operand bodyValue = build(bodies.get(whenLabel));
if (bodyValue != null) {
addInstr(new CopyInstr(result, bodyValue));
addInstr(new JumpInstr(endLabel));
}
}
if (!hasElse) {
addInstr(new LabelInstr(elseLabel));
addInstr(new CopyInstr(result, manager.getNil()));
addInstr(new JumpInstr(endLabel));
}
addInstr(new LabelInstr(endLabel));
return result;
}
private Operand buildFixnumCase(CaseNode caseNode) {
Map<Integer, Label> jumpTable = new HashMap<>();
Map<Node, Label> nodeBodies = new HashMap<>();
for (Node aCase : caseNode.getCases().children()) {
WhenNode whenNode = (WhenNode) aCase;
Label bodyLabel = getNewLabel();
FixnumNode expr = (FixnumNode) whenNode.getExpressionNodes();
long exprLong = expr.getValue();
if (exprLong > Integer.MAX_VALUE) throw new NotCompilableException("optimized fixnum case has long-ranged when at " + getFileName() + ":" + caseNode.getLine());
if (jumpTable.get((int) exprLong) == null) {
jumpTable.put((int) exprLong, bodyLabel);
}
nodeBodies.put(whenNode, bodyLabel);
}
Map.Entry<Integer, Label>[] jumpEntries = jumpTable.entrySet().toArray(new Map.Entry[jumpTable.size()]);
Arrays.sort(jumpEntries, new Comparator<Map.Entry<Integer, Label>>() {
@Override
public int compare(Map.Entry<Integer, Label> o1, Map.Entry<Integer, Label> o2) {
return Integer.compare(o1.getKey(), o2.getKey());
}
});
int[] jumps = new int[jumpTable.size()];
Label[] targets = new Label[jumps.length];
int i = 0;
for (Map.Entry<Integer, Label> jumpEntry : jumpEntries) {
jumps[i] = jumpEntry.getKey();
targets[i] = jumpEntry.getValue();
i++;
}
Operand value = build(caseNode.getCaseNode());
Label eqqPath = getNewLabel();
Label endLabel = getNewLabel();
boolean hasElse = (caseNode.getElseNode() != null);
Label elseLabel = getNewLabel();
Variable result = createTemporaryVariable();
addInstr(new BSwitchInstr(jumps, value, eqqPath, targets, elseLabel));
addInstr(new LabelInstr(eqqPath));
List<Label> labels = new ArrayList<>();
Map<Label, Node> bodies = new HashMap<>();
for (Node aCase : caseNode.getCases().children()) {
WhenNode whenNode = (WhenNode)aCase;
Label bodyLabel = nodeBodies.get(whenNode);
if (bodyLabel == null) bodyLabel = getNewLabel();
Variable eqqResult = createTemporaryVariable();
labels.add(bodyLabel);
Operand expression = build(whenNode.getExpressionNodes());
if (expression instanceof StringLiteral) {
expression = ((StringLiteral) expression).frozenString;
}
addInstr(new EQQInstr(scope, eqqResult, expression, value, false, scope.maybeUsingRefinements()));
addInstr(createBranch(eqqResult, manager.getTrue(), bodyLabel));
bodies.put(bodyLabel, whenNode.getBodyNode());
}
addInstr(new JumpInstr(elseLabel));
if (hasElse) {
labels.add(elseLabel);
bodies.put(elseLabel, caseNode.getElseNode());
}
for (Label whenLabel: labels) {
addInstr(new LabelInstr(whenLabel));
Operand bodyValue = build(bodies.get(whenLabel));
if (bodyValue != null) {
addInstr(new CopyInstr(result, bodyValue));
addInstr(new JumpInstr(endLabel));
}
}
if (!hasElse) {
addInstr(new LabelInstr(elseLabel));
addInstr(new CopyInstr(result, manager.getNil()));
addInstr(new JumpInstr(endLabel));
}
addInstr(new LabelInstr(endLabel));
return result;
}
public Operand buildClass(ClassNode classNode) {
boolean executesOnce = this.executesOnce;
Node superNode = classNode.getSuperNode();
Colon3Node cpath = classNode.getCPath();
Operand superClass = (superNode == null) ? null : build(superNode);
ByteList className = cpath.getName().getBytes();
Operand container = getContainerFromCPath(cpath);
IRClassBody body = new IRClassBody(manager, scope, className, classNode.getLine(), classNode.getScope(), executesOnce);
Variable bodyResult = addResultInstr(new DefineClassInstr(createTemporaryVariable(), body, container, superClass));
newIRBuilder(manager, body).buildModuleOrClassBody(classNode.getBodyNode(), classNode.getLine(), classNode.getEndLine());
return bodyResult;
}
public Operand buildSClass(SClassNode sclassNode) {
Operand receiver = build(sclassNode.getReceiverNode());
IRModuleBody body = new IRMetaClassBody(manager, scope, manager.getMetaClassName().getBytes(), sclassNode.getLine(), sclassNode.getScope());
Variable sClassVar = addResultInstr(new DefineMetaClassInstr(createTemporaryVariable(), receiver, body));
Variable processBodyResult = addResultInstr(new ProcessModuleBodyInstr(createTemporaryVariable(), sClassVar, getYieldClosureVariable()));
newIRBuilder(manager, body).buildModuleOrClassBody(sclassNode.getBodyNode(), sclassNode.getLine(), sclassNode.getEndLine());
return processBodyResult;
}
public Operand buildClassVar(ClassVarNode node) {
Variable ret = createTemporaryVariable();
addInstr(new GetClassVariableInstr(ret, classVarDefinitionContainer(), node.getName()));
return ret;
}
private Variable addResultInstr(ResultInstr instr) {
addInstr((Instr) instr);
return instr.getResult();
}
public Operand buildClassVarAsgn(final ClassVarAsgnNode classVarAsgnNode) {
Operand val = build(classVarAsgnNode.getValueNode());
addInstr(new PutClassVariableInstr(classVarDefinitionContainer(), classVarAsgnNode.getName(), val));
return val;
}
@Deprecated
public Operand classVarDeclarationContainer() {
return classVarContainer(true);
}
public Operand classVarDefinitionContainer() {
return classVarContainer(false);
}
public Operand classVarContainer(boolean declContext) {
int n = 0;
IRScope cvarScope = scope;
while (cvarScope != null && !(cvarScope instanceof IREvalScript) && !cvarScope.isNonSingletonClassBody()) {
if (!(cvarScope instanceof IRFor)) {
n++;
}
cvarScope = cvarScope.getLexicalParent();
}
if (cvarScope != null && cvarScope.isNonSingletonClassBody()) {
return ScopeModule.ModuleFor(n);
} else {
return addResultInstr(new GetClassVarContainerModuleInstr(createTemporaryVariable(),
getCurrentScopeVariable(), declContext ? null : buildSelf()));
}
}
public Operand buildConstDecl(ConstDeclNode node) {
return buildConstDeclAssignment(node, build(node.getValueNode()));
}
private Operand findContainerModule() {
int nearestModuleBodyDepth = scope.getNearestModuleReferencingScopeDepth();
return (nearestModuleBodyDepth == -1) ? getCurrentModuleVariable() : ScopeModule.ModuleFor(nearestModuleBodyDepth);
}
private Operand startingSearchScope() {
int nearestModuleBodyDepth = scope.getNearestModuleReferencingScopeDepth();
return nearestModuleBodyDepth == -1 ? getCurrentScopeVariable() : CurrentScope.INSTANCE;
}
public Operand buildConstDeclAssignment(ConstDeclNode constDeclNode, Operand value) {
Node constNode = constDeclNode.getConstNode();
if (constNode == null) {
return putConstant(constDeclNode.getName(), value);
} else if (constNode.getNodeType() == NodeType.COLON2NODE) {
return putConstant((Colon2Node) constNode, value);
} else {
return putConstant((Colon3Node) constNode, value);
}
}
private Operand putConstant(RubySymbol name, Operand value) {
addInstr(new PutConstInstr(findContainerModule(), name, value));
return value;
}
private Operand putConstant(Colon3Node node, Operand value) {
addInstr(new PutConstInstr(new ObjectClass(), node.getName(), value));
return value;
}
private Operand putConstant(Colon2Node node, Operand value) {
addInstr(new PutConstInstr(build(node.getLeftNode()), node.getName(), value));
return value;
}
private Operand putConstantAssignment(OpAsgnConstDeclNode node, Operand value) {
Node constNode = node.getFirstNode();
if (constNode instanceof Colon2Node) return putConstant((Colon2Node) constNode, value);
return putConstant((Colon3Node) constNode, value);
}
private Operand searchModuleForConst(Operand startingModule, RubySymbol name) {
return addResultInstr(new SearchModuleForConstInstr(createTemporaryVariable(), startingModule, name, true));
}
private Operand searchConst(RubySymbol name) {
return addResultInstr(new SearchConstInstr(createTemporaryVariable(), name, startingSearchScope(), false));
}
public Operand buildColon2(final Colon2Node colon2) {
Node lhs = colon2.getLeftNode();
if (lhs == null) return searchConst(colon2.getName());
return searchModuleForConst(build(lhs), colon2.getName());
}
public Operand buildColon3(Colon3Node node) {
return searchModuleForConst(new ObjectClass(), node.getName());
}
public Operand buildComplex(ComplexNode node) {
return new Complex((ImmutableLiteral) build(node.getNumber()));
}
interface CodeBlock {
public Operand run();
}
private Operand protectCodeWithRescue(CodeBlock protectedCode, CodeBlock rescueBlock) {
Variable rv = createTemporaryVariable();
Label rBeginLabel = getNewLabel();
Label rEndLabel = getNewLabel();
Label rescueLabel = getNewLabel();
addInstr(new LabelInstr(rBeginLabel));
addInstr(new ExceptionRegionStartMarkerInstr(rescueLabel));
Object v1 = protectedCode.run();
addInstr(new CopyInstr(rv, (Operand)v1));
addInstr(new JumpInstr(rEndLabel));
addInstr(new ExceptionRegionEndMarkerInstr());
Label caughtLabel = getNewLabel();
Variable exc = createTemporaryVariable();
Variable excType = createTemporaryVariable();
addInstr(new LabelInstr(rescueLabel));
addInstr(new ReceiveRubyExceptionInstr(exc));
addInstr(new InheritanceSearchConstInstr(excType, new ObjectClass(),
manager.runtime.newSymbol(CommonByteLists.EXCEPTION)));
outputExceptionCheck(excType, exc, caughtLabel);
addInstr(new ThrowExceptionInstr(exc));
addInstr(new LabelInstr(caughtLabel));
Object v2 = rescueBlock.run();
if (v2 != null) addInstr(new CopyInstr(rv, manager.getNil()));
addInstr(new LabelInstr(rEndLabel));
return rv;
}
public Operand buildGetDefinition(Node node) {
switch (node.getNodeType()) {
case CLASSVARASGNNODE: case CLASSVARDECLNODE: case CONSTDECLNODE:
case DASGNNODE: case GLOBALASGNNODE: case LOCALASGNNODE:
case MULTIPLEASGNNODE: case OPASGNNODE: case OPASGNANDNODE: case OPASGNORNODE:
case OPELEMENTASGNNODE: case INSTASGNNODE:
return new FrozenString(DefinedMessage.ASSIGNMENT.getText());
case ORNODE: case ANDNODE: case DREGEXPNODE: case DSTRNODE:
return new FrozenString(DefinedMessage.EXPRESSION.getText());
case FALSENODE:
return new FrozenString(DefinedMessage.FALSE.getText());
case LOCALVARNODE: case DVARNODE:
return new FrozenString(DefinedMessage.LOCAL_VARIABLE.getText());
case MATCH2NODE: case MATCH3NODE:
return new FrozenString(DefinedMessage.METHOD.getText());
case NILNODE:
return new FrozenString(DefinedMessage.NIL.getText());
case SELFNODE:
return new FrozenString(DefinedMessage.SELF.getText());
case TRUENODE:
return new FrozenString(DefinedMessage.TRUE.getText());
case ARRAYNODE: {
ArrayNode array = (ArrayNode) node;
Label undefLabel = getNewLabel();
Label doneLabel = getNewLabel();
Variable tmpVar = createTemporaryVariable();
for (Node elt: array.children()) {
Operand result = buildGetDefinition(elt);
addInstr(createBranch(result, manager.getNil(), undefLabel));
}
addInstr(new CopyInstr(tmpVar, new FrozenString(DefinedMessage.EXPRESSION.getText())));
addInstr(new JumpInstr(doneLabel));
addInstr(new LabelInstr(undefLabel));
addInstr(new CopyInstr(tmpVar, manager.getNil()));
addInstr(new LabelInstr(doneLabel));
return tmpVar;
}
case BACKREFNODE:
return addResultInstr(
new RuntimeHelperCall(
createTemporaryVariable(),
IS_DEFINED_BACKREF,
new Operand[] {new FrozenString(DefinedMessage.GLOBAL_VARIABLE.getText())}
)
);
case GLOBALVARNODE:
return addResultInstr(
new RuntimeHelperCall(
createTemporaryVariable(),
IS_DEFINED_GLOBAL,
new Operand[] {
new FrozenString(((GlobalVarNode) node).getName()),
new FrozenString(DefinedMessage.GLOBAL_VARIABLE.getText())
}
)
);
case NTHREFNODE: {
return addResultInstr(
new RuntimeHelperCall(
createTemporaryVariable(),
IS_DEFINED_NTH_REF,
new Operand[] {
manager.newFixnum(((NthRefNode) node).getMatchNumber()),
new FrozenString(DefinedMessage.GLOBAL_VARIABLE.getText())
}
)
);
}
case INSTVARNODE:
return addResultInstr(
new RuntimeHelperCall(
createTemporaryVariable(),
IS_DEFINED_INSTANCE_VAR,
new Operand[] {
buildSelf(),
new FrozenString(((InstVarNode) node).getName()),
new FrozenString(DefinedMessage.INSTANCE_VARIABLE.getText())
}
)
);
case CLASSVARNODE:
return addResultInstr(
new RuntimeHelperCall(
createTemporaryVariable(),
IS_DEFINED_CLASS_VAR,
new Operand[]{
classVarDefinitionContainer(),
new FrozenString(((ClassVarNode) node).getName()),
new FrozenString(DefinedMessage.CLASS_VARIABLE.getText())
}
)
);
case SUPERNODE: {
Label undefLabel = getNewLabel();
Variable tmpVar = addResultInstr(
new RuntimeHelperCall(
createTemporaryVariable(),
IS_DEFINED_SUPER,
new Operand[] {
buildSelf(),
new FrozenString(DefinedMessage.SUPER.getText())
}
)
);
addInstr(createBranch(tmpVar, manager.getNil(), undefLabel));
Operand superDefnVal = buildGetArgumentDefinition(((SuperNode) node).getArgsNode(), DefinedMessage.SUPER.getText());
return buildDefnCheckIfThenPaths(undefLabel, superDefnVal);
}
case VCALLNODE:
return addResultInstr(
new RuntimeHelperCall(
createTemporaryVariable(),
IS_DEFINED_METHOD,
new Operand[] {
buildSelf(),
new FrozenString(((VCallNode) node).getName()),
manager.getFalse(),
new FrozenString(DefinedMessage.METHOD.getText())
}
)
);
case YIELDNODE:
return buildDefinitionCheck(new BlockGivenInstr(createTemporaryVariable(), getYieldClosureVariable()), DefinedMessage.YIELD.getText());
case ZSUPERNODE:
return addResultInstr(
new RuntimeHelperCall(
createTemporaryVariable(),
IS_DEFINED_SUPER,
new Operand[] {
buildSelf(),
new FrozenString(DefinedMessage.SUPER.getText())
}
)
);
case CONSTNODE: {
Label defLabel = getNewLabel();
Label doneLabel = getNewLabel();
Variable tmpVar = createTemporaryVariable();
RubySymbol constName = ((ConstNode) node).getName();
addInstr(new LexicalSearchConstInstr(tmpVar, startingSearchScope(), constName));
addInstr(BNEInstr.create(defLabel, tmpVar, UndefinedValue.UNDEFINED));
addInstr(new InheritanceSearchConstInstr(tmpVar, findContainerModule(), constName));
addInstr(BNEInstr.create(defLabel, tmpVar, UndefinedValue.UNDEFINED));
addInstr(new CopyInstr(tmpVar, manager.getNil()));
addInstr(new JumpInstr(doneLabel));
addInstr(new LabelInstr(defLabel));
addInstr(new CopyInstr(tmpVar, new FrozenString(DefinedMessage.CONSTANT.getText())));
addInstr(new LabelInstr(doneLabel));
return tmpVar;
}
case COLON3NODE: case COLON2NODE: {
final Colon3Node colon = (Colon3Node) node;
final RubySymbol name = colon.getName();
final Variable errInfo = createTemporaryVariable();
addInstr(new GetErrorInfoInstr(errInfo));
CodeBlock protectedCode = new CodeBlock() {
public Operand run() {
if (!(colon instanceof Colon2Node)) {
return addResultInstr(
new RuntimeHelperCall(
createTemporaryVariable(),
IS_DEFINED_CONSTANT_OR_METHOD,
new Operand[] {
new ObjectClass(),
new FrozenString(name),
new FrozenString(DefinedMessage.CONSTANT.getText()),
new FrozenString(DefinedMessage.METHOD.getText())
}
)
);
}
Label bad = getNewLabel();
Label done = getNewLabel();
Variable result = createTemporaryVariable();
Operand test = buildGetDefinition(((Colon2Node) colon).getLeftNode());
addInstr(createBranch(test, manager.getNil(), bad));
Operand lhs = build(((Colon2Node) colon).getLeftNode());
addInstr(
new RuntimeHelperCall(
result,
IS_DEFINED_CONSTANT_OR_METHOD,
new Operand[] {
lhs,
new FrozenString(name),
new FrozenString(DefinedMessage.CONSTANT.getText()),
new FrozenString(DefinedMessage.METHOD.getText())
}
)
);
addInstr(new JumpInstr(done));
addInstr(new LabelInstr(bad));
addInstr(new CopyInstr(result, manager.getNil()));
addInstr(new LabelInstr(done));
return result;
}
};
CodeBlock rescueBlock = new CodeBlock() {
public Operand run() {
addInstr(new RestoreErrorInfoInstr(errInfo));
return manager.getNil();
}
};
return protectCodeWithRescue(protectedCode, rescueBlock);
}
case FCALLNODE: {
Label undefLabel = getNewLabel();
Variable tmpVar = addResultInstr(
new RuntimeHelperCall(
createTemporaryVariable(),
IS_DEFINED_METHOD,
new Operand[]{
buildSelf(),
new Symbol(((FCallNode) node).getName()),
manager.getFalse(),
new FrozenString(DefinedMessage.METHOD.getText())
}
)
);
addInstr(createBranch(tmpVar, manager.getNil(), undefLabel));
Operand argsCheckDefn = buildGetArgumentDefinition(((FCallNode) node).getArgsNode(), "method");
return buildDefnCheckIfThenPaths(undefLabel, argsCheckDefn);
}
case CALLNODE: {
final CallNode callNode = (CallNode) node;
CodeBlock protectedCode = new CodeBlock() {
public Operand run() {
final Label undefLabel = getNewLabel();
Operand receiverDefn = buildGetDefinition(callNode.getReceiverNode());
addInstr(createBranch(receiverDefn, manager.getNil(), undefLabel));
Variable tmpVar = createTemporaryVariable();
addInstr(
new RuntimeHelperCall(
tmpVar,
IS_DEFINED_CALL,
new Operand[]{
build(callNode.getReceiverNode()),
new Symbol(callNode.getName()),
new FrozenString(DefinedMessage.METHOD.getText())
}
)
);
return buildDefnCheckIfThenPaths(undefLabel, tmpVar);
}
};
CodeBlock rescueBlock = new CodeBlock() {
public Operand run() { return manager.getNil(); }
};
return protectCodeWithRescue(protectedCode, rescueBlock);
}
case ATTRASSIGNNODE: {
final AttrAssignNode attrAssign = (AttrAssignNode) node;
CodeBlock protectedCode = new CodeBlock() {
public Operand run() {
final Label undefLabel = getNewLabel();
Operand receiverDefn = buildGetDefinition(attrAssign.getReceiverNode());
addInstr(createBranch(receiverDefn, manager.getNil(), undefLabel));
Variable tmpVar = createTemporaryVariable();
Operand receiver = build(attrAssign.getReceiverNode());
addInstr(
new RuntimeHelperCall(
tmpVar,
IS_DEFINED_METHOD,
new Operand[] {
receiver,
new Symbol(attrAssign.getName()),
manager.getTrue(),
new FrozenString(DefinedMessage.METHOD.getText())
}
)
);
addInstr(createBranch(tmpVar, manager.getNil(), undefLabel));
Operand argsCheckDefn = buildGetArgumentDefinition(attrAssign.getArgsNode(), "assignment");
return buildDefnCheckIfThenPaths(undefLabel, argsCheckDefn);
}
};
CodeBlock rescueBlock = new CodeBlock() {
public Operand run() { return manager.getNil(); }
};
return protectCodeWithRescue(protectedCode, rescueBlock);
}
default:
return new FrozenString("expression");
}
}
protected Variable buildDefnCheckIfThenPaths(Label undefLabel, Operand defVal) {
Label defLabel = getNewLabel();
Variable tmpVar = getValueInTemporaryVariable(defVal);
addInstr(new JumpInstr(defLabel));
addInstr(new LabelInstr(undefLabel));
addInstr(new CopyInstr(tmpVar, manager.getNil()));
addInstr(new LabelInstr(defLabel));
return tmpVar;
}
protected Variable buildDefinitionCheck(ResultInstr definedInstr, String definedReturnValue) {
Label undefLabel = getNewLabel();
addInstr((Instr) definedInstr);
addInstr(createBranch(definedInstr.getResult(), manager.getFalse(), undefLabel));
return buildDefnCheckIfThenPaths(undefLabel, new FrozenString(definedReturnValue));
}
public Operand buildGetArgumentDefinition(final Node node, String type) {
if (node == null) return new StringLiteral(type);
Operand rv = new FrozenString(type);
boolean failPathReqd = false;
Label failLabel = getNewLabel();
if (node instanceof ArrayNode) {
for (int i = 0; i < ((ArrayNode) node).size(); i++) {
Node iterNode = ((ArrayNode) node).get(i);
Operand def = buildGetDefinition(iterNode);
if (def == manager.getNil()) {
rv = manager.getNil();
break;
} else if (!def.hasKnownValue()) {
failPathReqd = true;
addInstr(createBranch(def, manager.getNil(), failLabel));
}
}
} else {
Operand def = buildGetDefinition(node);
if (def == manager.getNil()) {
rv = manager.getNil();
} else if (!def.hasKnownValue()) {
failPathReqd = true;
addInstr(createBranch(def, manager.getNil(), failLabel));
}
}
return failPathReqd ? buildDefnCheckIfThenPaths(failLabel, rv) : rv;
}
public Operand buildDAsgn(final DAsgnNode dasgnNode) {
int depth = dasgnNode.getDepth();
Variable arg = getLocalVariable(dasgnNode.getName(), depth);
Operand value = build(dasgnNode.getValueNode());
if (arg == value) return value;
addInstr(new CopyInstr(arg, value));
return value;
}
protected InterpreterContext defineMethodInner(DefNode defNode, IRScope parent, boolean needsCodeCoverage) {
this.needsCodeCoverage = needsCodeCoverage;
if (RubyInstanceConfig.FULL_TRACE_ENABLED) {
addInstr(manager.newLineNumber(scope.getLine() + 1));
addInstr(new TraceInstr(RubyEvent.CALL, getName(), getFileName(), scope.getLine() + 1));
}
prepareImplicitState();
int nearestScopeDepth = parent.getNearestModuleReferencingScopeDepth();
addInstr(new CopyInstr(getCurrentScopeVariable(), CurrentScope.INSTANCE));
addInstr(new CopyInstr(getCurrentModuleVariable(), ScopeModule.ModuleFor(nearestScopeDepth == -1 ? 1 : nearestScopeDepth)));
receiveMethodArgs(defNode.getArgsNode());
Operand rv = build(defNode.getBodyNode());
if (RubyInstanceConfig.FULL_TRACE_ENABLED) {
addInstr(new LineNumberInstr(defNode.getEndLine() + 1));
addInstr(new TraceInstr(RubyEvent.RETURN, getName(), getFileName(), defNode.getEndLine() + 1));
}
if (rv != null) addInstr(new ReturnInstr(rv));
scope.computeScopeFlagsEarly(instructions);
if (scope.canReceiveNonlocalReturns()) handleNonlocalReturnInMethod();
ArgumentDescriptor[] argDesc;
if (argumentDescriptions == null) {
argDesc = ArgumentDescriptor.EMPTY_ARRAY;
} else {
argDesc = new ArgumentDescriptor[argumentDescriptions.size() / 2];
for (int i = 0; i < argumentDescriptions.size(); i += 2) {
ArgumentType type = (ArgumentType) argumentDescriptions.get(i);
RubySymbol symbol = (RubySymbol) argumentDescriptions.get(i+1);
argDesc[i / 2] = new ArgumentDescriptor(type, symbol);
}
}
((IRMethod) scope).setArgumentDescriptors(argDesc);
return scope.allocateInterpreterContext(instructions);
}
private IRMethod defineNewMethod(MethodDefNode defNode, boolean isInstanceMethod) {
return new IRMethod(manager, scope, defNode, defNode.getName().getBytes(), isInstanceMethod, defNode.getLine(),
defNode.getScope(), needsCodeCoverage());
}
public Operand buildDefn(MethodDefNode node) {
IRMethod method = defineNewMethod(node, true);
addInstr(new DefineInstanceMethodInstr(method));
return new Symbol(node.getName());
}
public Operand buildDefs(DefsNode node) {
Operand container = build(node.getReceiverNode());
IRMethod method = defineNewMethod(node, false);
addInstr(new DefineClassMethodInstr(container, method));
return new Symbol(node.getName());
}
protected LocalVariable getArgVariable(RubySymbol name, int depth) {
return scope instanceof IRFor ? getLocalVariable(name, depth) : getNewLocalVariable(name, 0);
}
private void addArgReceiveInstr(Variable v, int argIndex, Signature signature) {
boolean post = signature != null;
if (post) {
addInstr(new ReceivePostReqdArgInstr(v, argIndex, signature.pre(), signature.opt(), signature.hasRest(), signature.post()));
} else {
addInstr(new ReceivePreReqdArgInstr(v, argIndex));
}
}
private Variable argumentResult(RubySymbol name) {
boolean isUnderscore = name.getBytes().realSize() == 1 && name.getBytes().charAt(0) == '_';
if (isUnderscore && underscoreVariableSeen) {
return createTemporaryVariable();
} else {
if (isUnderscore) underscoreVariableSeen = true;
return getNewLocalVariable(name, 0);
}
}
public void receiveRequiredArg(Node node, int argIndex, Signature signature) {
switch (node.getNodeType()) {
case ARGUMENTNODE: {
RubySymbol argName = ((ArgumentNode)node).getName();
if (scope instanceof IRMethod) addArgumentDescription(ArgumentType.req, argName);
addArgReceiveInstr(argumentResult(argName), argIndex, signature);
break;
}
case MULTIPLEASGNNODE: {
MultipleAsgnNode childNode = (MultipleAsgnNode) node;
Variable v = createTemporaryVariable();
addArgReceiveInstr(v, argIndex, signature);
if (scope instanceof IRMethod) addArgumentDescription(ArgumentType.anonreq, null);
Variable tmp = createTemporaryVariable();
addInstr(new ToAryInstr(tmp, v));
buildMultipleAsgn19Assignment(childNode, tmp, null);
break;
}
default: throw new NotCompilableException("Can't build assignment node: " + node);
}
}
protected void receiveNonBlockArgs(final ArgsNode argsNode) {
Signature signature = scope.getStaticScope().getSignature();
if (scope instanceof IRMethod) {
addInstr(new CheckArityInstr(signature.required(), signature.opt(), signature.hasRest(), argsNode.hasKwargs(),
signature.keyRest()));
} else if (scope instanceof IRClosure && argsNode.hasKwargs()) {
addInstr(new CheckArityInstr(signature.required(), signature.opt(), signature.hasRest(), argsNode.hasKwargs(),
signature.keyRest()));
}
int argIndex = 0;
Node[] args = argsNode.getArgs();
int preCount = signature.pre();
for (int i = 0; i < preCount; i++, argIndex++) {
receiveRequiredArg(args[i], argIndex, null);
}
int opt = signature.opt() > 0 ? signature.opt() : 0;
if (opt > 0) {
int optIndex = argsNode.getOptArgIndex();
for (int j = 0; j < opt; j++, argIndex++) {
Label variableAssigned = getNewLabel();
OptArgNode optArg = (OptArgNode)args[optIndex + j];
RubySymbol argName = optArg.getName();
Variable argVar = argumentResult(argName);
if (scope instanceof IRMethod) addArgumentDescription(ArgumentType.opt, argName);
addInstr(new ReceiveOptArgInstr(argVar, signature.required(), signature.pre(), j));
addInstr(BNEInstr.create(variableAssigned, argVar, UndefinedValue.UNDEFINED));
addInstr(new CopyInstr(argVar, manager.getNil()));
build(optArg.getValue());
addInstr(new LabelInstr(variableAssigned));
}
}
if (signature.hasRest()) {
RestArgNode restArgNode = argsNode.getRestArgNode();
if (scope instanceof IRMethod) {
addArgumentDescription(restArgNode.isAnonymous() ?
ArgumentType.anonrest : ArgumentType.rest, restArgNode.getName());
}
RubySymbol argName = restArgNode.isAnonymous() ?
scope.getManager().getRuntime().newSymbol(CommonByteLists.STAR) : restArgNode.getName();
addInstr(new ReceiveRestArgInstr(argumentResult(argName), signature.required() + opt, argIndex));
}
int postCount = argsNode.getPostCount();
int postIndex = argsNode.getPostIndex();
for (int i = 0; i < postCount; i++) {
receiveRequiredArg(args[postIndex + i], i, signature);
}
}
protected void receiveBlockArg(final ArgsNode argsNode) {
BlockArgNode blockArg = argsNode.getBlock();
if (blockArg != null) {
RubySymbol argName = blockArg.getName();
Variable blockVar = argumentResult(argName);
if (scope instanceof IRMethod) addArgumentDescription(ArgumentType.block, argName);
Variable tmp = createTemporaryVariable();
addInstr(new LoadImplicitClosureInstr(tmp));
addInstr(new ReifyClosureInstr(blockVar, tmp));
}
}
private void prepareImplicitState() {
addInstr(manager.getReceiveSelfInstr());
if (scope instanceof IRMethod || scope instanceof IRMetaClassBody) {
addInstr(new LoadImplicitClosureInstr(getYieldClosureVariable()));
} else {
addInstr(new LoadFrameClosureInstr(getYieldClosureVariable()));
}
}
public void receiveArgs(final ArgsNode argsNode) {
receiveNonBlockArgs(argsNode);
Node[] args = argsNode.getArgs();
int required = argsNode.getRequiredArgsCount();
if (argsNode.hasKwargs()) {
int keywordIndex = argsNode.getKeywordsIndex();
int keywordsCount = argsNode.getKeywordCount();
for (int i = 0; i < keywordsCount; i++) {
KeywordArgNode kwarg = (KeywordArgNode) args[keywordIndex + i];
AssignableNode kasgn = kwarg.getAssignable();
RubySymbol key = ((INameNode) kasgn).getName();
Variable av = getNewLocalVariable(key, 0);
Label l = getNewLabel();
if (scope instanceof IRMethod) addKeyArgDesc(kasgn, key);
addInstr(new ReceiveKeywordArgInstr(av, key, required));
addInstr(BNEInstr.create(l, av, UndefinedValue.UNDEFINED));
if (!isRequiredKeywordArgumentValue(kasgn)) {
addInstr(new CopyInstr(av, buildNil()));
build(kasgn);
} else {
addInstr(new RaiseRequiredKeywordArgumentError(key));
}
addInstr(new LabelInstr(l));
}
}
KeywordRestArgNode keyRest = argsNode.getKeyRest();
if (keyRest != null) {
RubySymbol key = keyRest.getName();
ArgumentType type = ArgumentType.keyrest;
if (key == null || key.getBytes().realSize() == 0) type = ArgumentType.anonkeyrest;
Variable av = getNewLocalVariable(key, 0);
if (scope instanceof IRMethod) addArgumentDescription(type, key);
addInstr(new ReceiveKeywordRestArgInstr(av, required));
}
receiveBlockArg(argsNode);
}
private void addKeyArgDesc(AssignableNode kasgn, RubySymbol key) {
if (isRequiredKeywordArgumentValue(kasgn)) {
addArgumentDescription(ArgumentType.keyreq, key);
} else {
addArgumentDescription(ArgumentType.key, key);
}
}
private boolean isRequiredKeywordArgumentValue(AssignableNode kasgn) {
return (kasgn.getValueNode().getNodeType()) == NodeType.REQUIRED_KEYWORD_ARGUMENT_VALUE;
}
public void buildArgsMasgn(Node node, Operand argsArray, boolean isMasgnRoot, int preArgsCount, int postArgsCount, int index, boolean isSplat) {
Variable v;
switch (node.getNodeType()) {
case DASGNNODE: {
DAsgnNode dynamicAsgn = (DAsgnNode) node;
v = getArgVariable(dynamicAsgn.getName(), dynamicAsgn.getDepth());
if (isSplat) addInstr(new RestArgMultipleAsgnInstr(v, argsArray, preArgsCount, postArgsCount, index));
else addInstr(new ReqdArgMultipleAsgnInstr(v, argsArray, preArgsCount, postArgsCount, index));
break;
}
case LOCALASGNNODE: {
LocalAsgnNode localVariable = (LocalAsgnNode) node;
v = getArgVariable(localVariable.getName(), localVariable.getDepth());
if (isSplat) addInstr(new RestArgMultipleAsgnInstr(v, argsArray, preArgsCount, postArgsCount, index));
else addInstr(new ReqdArgMultipleAsgnInstr(v, argsArray, preArgsCount, postArgsCount, index));
break;
}
case MULTIPLEASGNNODE: {
MultipleAsgnNode childNode = (MultipleAsgnNode) node;
if (!isMasgnRoot) {
v = createTemporaryVariable();
if (isSplat) addInstr(new RestArgMultipleAsgnInstr(v, argsArray, preArgsCount, postArgsCount, index));
else addInstr(new ReqdArgMultipleAsgnInstr(v, argsArray, preArgsCount, postArgsCount, index));
Variable tmp = createTemporaryVariable();
addInstr(new ToAryInstr(tmp, v));
argsArray = tmp;
}
buildMultipleAsgn19Assignment(childNode, argsArray, null);
break;
}
default:
throw new NotCompilableException("Shouldn't get here: " + node);
}
}
public void buildMultipleAsgn19Assignment(final MultipleAsgnNode multipleAsgnNode, Operand argsArray, Operand values) {
final ListNode masgnPre = multipleAsgnNode.getPre();
final List<Tuple<Node, Variable>> assigns = new ArrayList<>();
int i = 0;
if (masgnPre != null) {
for (Node an: masgnPre.children()) {
if (values == null) {
buildArgsMasgn(an, argsArray, false, -1, -1, i, false);
} else {
Variable rhsVal = createTemporaryVariable();
addInstr(new ReqdArgMultipleAsgnInstr(rhsVal, values, i));
assigns.add(new Tuple<>(an, rhsVal));
}
i++;
}
}
Node restNode = multipleAsgnNode.getRest();
int postArgsCount = multipleAsgnNode.getPostCount();
if (restNode != null && !(restNode instanceof StarNode)) {
if (values == null) {
buildArgsMasgn(restNode, argsArray, false, i, postArgsCount, 0, true);
} else {
Variable rhsVal = createTemporaryVariable();
addInstr(new RestArgMultipleAsgnInstr(rhsVal, values, i, postArgsCount, 0));
assigns.add(new Tuple<>(restNode, rhsVal));
}
}
final ListNode masgnPost = multipleAsgnNode.getPost();
if (masgnPost != null) {
int j = 0;
for (Node an: masgnPost.children()) {
if (values == null) {
buildArgsMasgn(an, argsArray, false, i, postArgsCount, j, false);
} else {
Variable rhsVal = createTemporaryVariable();
addInstr(new ReqdArgMultipleAsgnInstr(rhsVal, values, i, postArgsCount, j));
assigns.add(new Tuple<>(an, rhsVal));
}
j++;
}
}
for (Tuple<Node, Variable> assign: assigns) {
buildAssignment(assign.a, assign.b);
}
}
private void handleBreakAndReturnsInLambdas() {
Label rEndLabel = getNewLabel();
Label rescueLabel = Label.getGlobalEnsureBlockLabel();
addInstrAtBeginning(new ExceptionRegionStartMarkerInstr(rescueLabel));
addInstr(new ExceptionRegionEndMarkerInstr());
addInstr(new LabelInstr(rescueLabel));
Variable exc = createTemporaryVariable();
addInstr(new ReceiveJRubyExceptionInstr(exc));
Variable ret = createTemporaryVariable();
addInstr(new RuntimeHelperCall(ret, RuntimeHelperCall.Methods.HANDLE_BREAK_AND_RETURNS_IN_LAMBDA, new Operand[]{exc} ));
addInstr(new ReturnOrRethrowSavedExcInstr(ret));
addInstr(new LabelInstr(rEndLabel));
}
public void receiveMethodArgs(final ArgsNode argsNode) {
receiveArgs(argsNode);
}
public void receiveBlockArgs(final IterNode node) {
Node args = node.getVarNode();
if (args instanceof ArgsNode) {
((IRClosure) scope).setArgumentDescriptors(Helpers.argsNodeToArgumentDescriptors(((ArgsNode) args)));
receiveArgs((ArgsNode)args);
} else {
buildBlockArgsAssignment(args, null, 0, false);
}
}
public Operand buildDot(final DotNode dotNode) {
return addResultInstr(new BuildRangeInstr(createTemporaryVariable(), build(dotNode.getBeginNode()),
build(dotNode.getEndNode()), dotNode.isExclusive()));
}
private Operand dynamicPiece(Node pieceNode) {
Operand piece = pieceNode instanceof StrNode ? buildStrRaw((StrNode) pieceNode) : build(pieceNode);
if (piece instanceof StringLiteral) {
piece = ((StringLiteral)piece).frozenString;
}
return piece == null ? manager.getNil() : piece;
}
public Operand buildDRegexp(Variable result, DRegexpNode node) {
Node[] nodePieces = node.children();
Operand[] pieces = new Operand[nodePieces.length];
for (int i = 0; i < pieces.length; i++) {
pieces[i] = dynamicPiece(nodePieces[i]);
}
if (result == null) result = createTemporaryVariable();
addInstr(new BuildDynRegExpInstr(result, pieces, node.getOptions()));
return result;
}
public Operand buildDStr(Variable result, DStrNode node) {
Node[] nodePieces = node.children();
Operand[] pieces = new Operand[nodePieces.length];
for (int i = 0; i < pieces.length; i++) {
pieces[i] = dynamicPiece(nodePieces[i]);
}
if (result == null) result = createTemporaryVariable();
boolean debuggingFrozenStringLiteral = manager.getInstanceConfig().isDebuggingFrozenStringLiteral();
addInstr(new BuildCompoundStringInstr(result, pieces, node.getEncoding(), node.isFrozen(), debuggingFrozenStringLiteral, getFileName(), node.getLine()));
return result;
}
public Operand buildDSymbol(Variable result, DSymbolNode node) {
Node[] nodePieces = node.children();
Operand[] pieces = new Operand[nodePieces.length];
for (int i = 0; i < pieces.length; i++) {
pieces[i] = dynamicPiece(nodePieces[i]);
}
if (result == null) result = createTemporaryVariable();
boolean debuggingFrozenStringLiteral = manager.getInstanceConfig().isDebuggingFrozenStringLiteral();
addInstr(new BuildCompoundStringInstr(result, pieces, node.getEncoding(), false, debuggingFrozenStringLiteral, getFileName(), node.getLine()));
return copyAndReturnValue(new DynamicSymbol(result));
}
public Operand buildDVar(DVarNode node) {
return getLocalVariable(node.getName(), node.getDepth());
}
public Operand buildDXStr(Variable result, DXStrNode node) {
Node[] nodePieces = node.children();
Operand[] pieces = new Operand[nodePieces.length];
for (int i = 0; i < pieces.length; i++) {
pieces[i] = dynamicPiece(nodePieces[i]);
}
Variable stringResult = createTemporaryVariable();
if (result == null) result = createTemporaryVariable();
boolean debuggingFrozenStringLiteral = manager.getInstanceConfig().isDebuggingFrozenStringLiteral();
addInstr(new BuildCompoundStringInstr(stringResult, pieces, node.getEncoding(), false, debuggingFrozenStringLiteral, getFileName(), node.getLine()));
return addResultInstr(CallInstr.create(scope, CallType.FUNCTIONAL, result, manager.getRuntime().newSymbol("`"), Self.SELF, new Operand[] { stringResult }, null));
}
public Operand buildEnsureNode(final EnsureNode ensureNode) {
return buildEnsureInternal(ensureNode.getBodyNode(), ensureNode.getEnsureNode());
}
public Operand buildEnsureInternal(Node ensureBodyNode, Node ensurerNode) {
final Variable savedGlobalException = createTemporaryVariable();
addInstr(new GetGlobalVariableInstr(savedGlobalException, symbol("$!")));
EnsureBlockInfo ebi = new EnsureBlockInfo(scope,
(ensureBodyNode instanceof RescueNode) ? (RescueNode)ensureBodyNode : null,
getCurrentLoop(),
activeRescuers.peek());
if (ensureBodyNode != null && ensureBodyNode instanceof RescueNode) {
ebi.savedGlobalException = savedGlobalException;
}
ensureBodyBuildStack.push(ebi);
Operand ensureRetVal = ensurerNode == null ? manager.getNil() : build(ensurerNode);
ensureBodyBuildStack.pop();
activeEnsureBlockStack.push(ebi);
addInstr(new LabelInstr(ebi.regionStart));
addInstr(new ExceptionRegionStartMarkerInstr(ebi.dummyRescueBlockLabel));
activeRescuers.push(ebi.dummyRescueBlockLabel);
Variable ensureExprValue = createTemporaryVariable();
Operand rv = ensureBodyNode instanceof RescueNode ? buildRescueInternal((RescueNode) ensureBodyNode, ebi) : build(ensureBodyNode);
addInstr(new ExceptionRegionEndMarkerInstr());
activeRescuers.pop();
boolean isEnsureExpr = ensurerNode != null && rv != U_NIL && !(ensureBodyNode instanceof RescueNode);
if (isEnsureExpr) {
addInstr(new CopyInstr(ensureExprValue, rv));
ebi.cloneIntoHostScope(this);
addInstr(new JumpInstr(ebi.end));
}
activeEnsureBlockStack.pop();
Variable exc = createTemporaryVariable();
addInstr(new LabelInstr(ebi.dummyRescueBlockLabel));
addInstr(new ReceiveJRubyExceptionInstr(exc));
if (ensurerNode != null) {
ebi.emitBody(this);
}
if (ensureRetVal == U_NIL) rv = U_NIL;
addInstr(new ThrowExceptionInstr(exc));
addInstr(new LabelInstr(ebi.end));
return isEnsureExpr ? ensureExprValue : rv;
}
public Operand buildEvStr(EvStrNode node) {
TemporaryVariable result = createTemporaryVariable();
addInstr(new AsStringInstr(scope, result, build(node.getBody()), scope.maybeUsingRefinements()));
return result;
}
public Operand buildFalse() {
return manager.getFalse();
}
public Operand buildFCall(Variable result, FCallNode fcallNode) {
Node callArgsNode = fcallNode.getArgsNode();
if (result == null) result = createTemporaryVariable();
HashNode keywordArgs = getPossibleKeywordArgument(fcallNode.getArgsNode());
CallInstr callInstr;
Operand block;
if (keywordArgs != null) {
Operand[] args = buildCallArgsExcept(fcallNode.getArgsNode(), keywordArgs);
List<KeyValuePair<Operand, Operand>> kwargs = buildKeywordArguments(keywordArgs);
block = setupCallClosure(fcallNode.getIterNode());
callInstr = CallInstr.createWithKwargs(scope, CallType.FUNCTIONAL, result, fcallNode.getName(), buildSelf(), args, block, kwargs);
} else {
Operand[] args = setupCallArgs(callArgsNode);
determineIfMaybeRefined(fcallNode.getName(), args);
block = setupCallClosure(fcallNode.getIterNode());
if (CommonByteLists.DEFINE_METHOD_METHOD.equals(fcallNode.getName().getBytes()) && block instanceof WrappedIRClosure) {
IRClosure closure = ((WrappedIRClosure) block).getClosure();
if (!closure.getFlags().contains(IRFlags.ACCESS_PARENTS_LOCAL_VARIABLES) &&
fcallNode.getIterNode() instanceof IterNode) {
closure.setSource((IterNode) fcallNode.getIterNode());
}
}
callInstr = CallInstr.create(scope, CallType.FUNCTIONAL, result, fcallNode.getName(), buildSelf(), args, block);
}
determineIfWeNeedLineNumber(fcallNode);
receiveBreakException(block, callInstr);
return result;
}
private Operand setupCallClosure(Node node) {
if (node == null) return null;
switch (node.getNodeType()) {
case ITERNODE:
return build(node);
case BLOCKPASSNODE:
Node bodyNode = ((BlockPassNode)node).getBodyNode();
return bodyNode instanceof SymbolNode && !scope.maybeUsingRefinements() ?
new SymbolProc(((SymbolNode)bodyNode).getName()) : build(bodyNode);
default:
throw new NotCompilableException("ERROR: Encountered a method with a non-block, non-blockpass iter node at: " + node);
}
}
private void determineIfMaybeRefined(RubySymbol methodName, Operand[] args) {
IRScope outerScope = scope.getNearestTopLocalVariableScope();
if (!(outerScope instanceof IRMethod) && args.length == 1
&& (
CommonByteLists.USING_METHOD.equals(methodName.getBytes())
|| CommonByteLists.REFINE_METHOD.equals(methodName.getBytes())
)) {
scope.setIsMaybeUsingRefinements();
}
}
public Operand buildFixnum(FixnumNode node) {
return manager.newFixnum(node.getValue());
}
public Operand buildFlip(FlipNode flipNode) {
Ruby runtime = scope.getManager().getRuntime();
Operand exceptionClass = searchModuleForConst(new ObjectClass(), runtime.newSymbol("NotImplementedError"));
Operand exception = addResultInstr(CallInstr.create(scope, createTemporaryVariable(), runtime.newSymbol("new"), exceptionClass, new Operand[]{new StringLiteral("flip-flop is no longer supported in JRuby")}, null));
addInstr(new ThrowExceptionInstr(exception));
return manager.getNil();
}
public Operand buildFloat(FloatNode node) {
return new Float(node.getValue());
}
public Operand buildFor(ForNode forNode) {
Variable result = createTemporaryVariable();
Operand receiver = build(forNode.getIterNode());
Operand forBlock = buildForIter(forNode);
CallInstr callInstr = new CallInstr(scope, CallType.NORMAL, result, manager.runtime.newSymbol(CommonByteLists.EACH), receiver, EMPTY_OPERANDS,
forBlock, scope.maybeUsingRefinements());
receiveBreakException(forBlock, callInstr);
return result;
}
public Operand buildForIter(final ForNode forNode) {
IRClosure closure = new IRFor(manager, scope, forNode.getLine(), forNode.getScope(), Signature.from(forNode));
newIRBuilder(manager, closure).buildIterInner(forNode);
return new WrappedIRClosure(buildSelf(), closure);
}
public Operand buildGlobalAsgn(GlobalAsgnNode globalAsgnNode) {
Operand value = build(globalAsgnNode.getValueNode());
addInstr(new PutGlobalVarInstr(globalAsgnNode.getName(), value));
return value;
}
public Operand buildGlobalVar(Variable result, GlobalVarNode node) {
if (result == null) result = createTemporaryVariable();
return addResultInstr(new GetGlobalVariableInstr(result, node.getName()));
}
public Operand buildHash(HashNode hashNode) {
List<KeyValuePair<Operand, Operand>> args = new ArrayList<>();
boolean hasAssignments = hashNode.containsVariableAssignment();
Variable hash = null;
for (KeyValuePair<Node, Node> pair: hashNode.getPairs()) {
Node key = pair.getKey();
Operand keyOperand;
if (key == null) {
if (hash == null) {
hash = copyAndReturnValue(new Hash(args));
args = new ArrayList<>();
} else if (!args.isEmpty()) {
addInstr(new RuntimeHelperCall(hash, MERGE_KWARGS, new Operand[] { hash, new Hash(args) }));
args = new ArrayList<>();
}
Operand splat = buildWithOrder(pair.getValue(), hasAssignments);
addInstr(new RuntimeHelperCall(hash, MERGE_KWARGS, new Operand[] { hash, splat}));
continue;
} else {
keyOperand = buildWithOrder(key, hasAssignments);
}
args.add(new KeyValuePair<>(keyOperand, buildWithOrder(pair.getValue(), hasAssignments)));
}
if (hash == null) {
hash = copyAndReturnValue(new Hash(args));
} else if (!args.isEmpty()) {
addInstr(new RuntimeHelperCall(hash, MERGE_KWARGS, new Operand[] { hash, new Hash(args) }));
}
return hash;
}
public Operand buildIf(Variable result, final IfNode ifNode) {
Node actualCondition = ifNode.getCondition();
Label falseLabel = getNewLabel();
Label doneLabel = getNewLabel();
Operand thenResult;
addInstr(createBranch(build(actualCondition), manager.getFalse(), falseLabel));
boolean thenNull = false;
boolean elseNull = false;
boolean thenUnil = false;
boolean elseUnil = false;
if (ifNode.getThenBody() != null) {
thenResult = build(result, ifNode.getThenBody());
if (thenResult != U_NIL) {
result = getValueInTemporaryVariable(thenResult);
addInstr(new JumpInstr(doneLabel));
} else {
if (result == null) result = createTemporaryVariable();
thenUnil = true;
}
} else {
thenNull = true;
if (result == null) result = createTemporaryVariable();
addInstr(new CopyInstr(result, manager.getNil()));
addInstr(new JumpInstr(doneLabel));
}
addInstr(new LabelInstr(falseLabel));
if (ifNode.getElseBody() != null) {
Operand elseResult = build(ifNode.getElseBody());
if (elseResult != U_NIL) {
addInstr(new CopyInstr(result, elseResult));
} else {
elseUnil = true;
}
} else {
elseNull = true;
addInstr(new CopyInstr(result, manager.getNil()));
}
if (thenNull && elseNull) {
addInstr(new LabelInstr(doneLabel));
return manager.getNil();
} else if (thenUnil && elseUnil) {
return U_NIL;
} else {
addInstr(new LabelInstr(doneLabel));
return result;
}
}
public Operand buildInstAsgn(final InstAsgnNode instAsgnNode) {
Operand val = build(instAsgnNode.getValueNode());
addInstr(new PutFieldInstr(buildSelf(), instAsgnNode.getName(), val));
return val;
}
public Operand buildInstVar(InstVarNode node) {
return addResultInstr(new GetFieldInstr(createTemporaryVariable(), buildSelf(), node.getName()));
}
private InterpreterContext buildIterInner(IterNode iterNode) {
boolean forNode = iterNode instanceof ForNode;
prepareImplicitState();
if (RubyInstanceConfig.FULL_TRACE_ENABLED) {
addInstr(new TraceInstr(RubyEvent.B_CALL, getName(), getFileName(), scope.getLine() + 1));
}
if (!forNode) addCurrentScopeAndModule();
receiveBlockArgs(iterNode);
if (forNode) addCurrentScopeAndModule();
afterPrologueIndex = instructions.size() - 1;
Operand closureRetVal = iterNode.getBodyNode() == null ? manager.getNil() : build(iterNode.getBodyNode());
if (RubyInstanceConfig.FULL_TRACE_ENABLED) {
addInstr(new TraceInstr(RubyEvent.B_RETURN, getName(), getFileName(), iterNode.getEndLine() + 1));
}
if (closureRetVal != U_NIL) addInstr(new ReturnInstr(closureRetVal));
if (!forNode) handleBreakAndReturnsInLambdas();
return scope.allocateInterpreterContext(instructions);
}
public Operand buildIter(final IterNode iterNode) {
IRClosure closure = new IRClosure(manager, scope, iterNode.getLine(), iterNode.getScope(), Signature.from(iterNode), needsCodeCoverage);
newIRBuilder(manager, closure).buildIterInner(iterNode);
return new WrappedIRClosure(buildSelf(), closure);
}
public Operand buildLiteral(LiteralNode literalNode) {
return new StringLiteral(literalNode.getSymbolName());
}
public Operand buildLocalAsgn(LocalAsgnNode localAsgnNode) {
Variable variable = getLocalVariable(localAsgnNode.getName(), localAsgnNode.getDepth());
Operand value = build(variable, localAsgnNode.getValueNode());
if (variable == value) return value;
addInstr(new CopyInstr(variable, value));
return value;
}
public Operand buildLocalVar(LocalVarNode node) {
return getLocalVariable(node.getName(), node.getDepth());
}
public Operand buildMatch(Variable result, MatchNode matchNode) {
Operand regexp = build(matchNode.getRegexpNode());
Variable tempLastLine = createTemporaryVariable();
addResultInstr(new GetGlobalVariableInstr(tempLastLine, symbol("$_")));
if (result == null) result = createTemporaryVariable();
return addResultInstr(new MatchInstr(scope, result, regexp, tempLastLine));
}
public Operand buildMatch2(Variable result, Match2Node matchNode) {
Operand receiver = build(matchNode.getReceiverNode());
Operand value = build(matchNode.getValueNode());
if (result == null) result = createTemporaryVariable();
addInstr(new MatchInstr(scope, result, receiver, value));
if (matchNode instanceof Match2CaptureNode) {
Match2CaptureNode m2c = (Match2CaptureNode)matchNode;
for (int slot: m2c.getScopeOffsets()) {
int depth = slot >> 16;
int offset = slot & 0xffff;
RubySymbol var = manager.runtime.newSymbol(getVarNameFromScopeTree(scope, depth, offset));
addInstr(new SetCapturedVarInstr(getLocalVariable(var, depth), result, var));
}
}
return result;
}
private String getVarNameFromScopeTree(IRScope scope, int depth, int offset) {
if (depth == 0) {
return scope.getStaticScope().getVariables()[offset];
}
return getVarNameFromScopeTree(scope.getLexicalParent(), depth - 1, offset);
}
public Operand buildMatch3(Variable result, Match3Node matchNode) {
Operand receiver = build(matchNode.getReceiverNode());
Operand value = build(matchNode.getValueNode());
if (result == null) result = createTemporaryVariable();
return addResultInstr(new MatchInstr(scope, result, receiver, value));
}
private Operand getContainerFromCPath(Colon3Node cpath) {
Operand container;
if (cpath instanceof Colon2Node) {
Node leftNode = ((Colon2Node) cpath).getLeftNode();
if (leftNode != null) {
container = build(leftNode);
} else {
container = findContainerModule();
}
} else {
container = new ObjectClass();
}
return container;
}
public Operand buildModule(ModuleNode moduleNode) {
boolean executesOnce = this.executesOnce;
Colon3Node cpath = moduleNode.getCPath();
ByteList moduleName = cpath.getName().getBytes();
Operand container = getContainerFromCPath(cpath);
IRModuleBody body = new IRModuleBody(manager, scope, moduleName, moduleNode.getLine(), moduleNode.getScope(), executesOnce);
Variable bodyResult = addResultInstr(new DefineModuleInstr(createTemporaryVariable(), body, container));
newIRBuilder(manager, body).buildModuleOrClassBody(moduleNode.getBodyNode(), moduleNode.getLine(), moduleNode.getEndLine());
return bodyResult;
}
public Operand buildNext(final NextNode nextNode) {
IRLoop currLoop = getCurrentLoop();
Operand rv = build(nextNode.getValueNode());
if (!activeEnsureBlockStack.empty()) emitEnsureBlocks(currLoop);
if (currLoop != null) {
addInstr(new JumpInstr(currLoop.iterEndLabel));
} else {
addInstr(new ThreadPollInstr(true));
if (scope instanceof IRClosure) {
if (scope instanceof IREvalScript) {
throwSyntaxError(nextNode, "Can't escape from eval with next");
} else {
addInstr(new ReturnInstr(rv));
}
} else {
throwSyntaxError(nextNode, "Invalid next");
}
}
return U_NIL;
}
public Operand buildNthRef(NthRefNode nthRefNode) {
return copyAndReturnValue(new NthRef(scope, nthRefNode.getMatchNumber()));
}
public Operand buildNil() {
return manager.getNil();
}
public Operand buildOpAsgn(OpAsgnNode opAsgnNode) {
Label l;
Variable readerValue = createTemporaryVariable();
Variable writerValue = createTemporaryVariable();
Node receiver = opAsgnNode.getReceiverNode();
CallType callType = receiver instanceof SelfNode ? CallType.FUNCTIONAL : CallType.NORMAL;
Operand v1 = build(opAsgnNode.getReceiverNode());
Label lazyLabel = null;
Label endLabel = null;
Variable result = createTemporaryVariable();
if (opAsgnNode.isLazy()) {
lazyLabel = getNewLabel();
endLabel = getNewLabel();
addInstr(new BNilInstr(lazyLabel, v1));
}
addInstr(CallInstr.create(scope, callType, readerValue, opAsgnNode.getVariableSymbolName(), v1, EMPTY_OPERANDS, null));
boolean isOrOr = opAsgnNode.isOr();
if (isOrOr || opAsgnNode.isAnd()) {
l = getNewLabel();
addInstr(createBranch(readerValue, isOrOr ? manager.getTrue() : manager.getFalse(), l));
Operand v2 = build(opAsgnNode.getValueNode());
addInstr(CallInstr.create(scope, callType, writerValue, opAsgnNode.getVariableSymbolNameAsgn(), v1, new Operand[] {v2}, null));
addInstr(new CopyInstr(readerValue, v2));
addInstr(new LabelInstr(l));
if (!opAsgnNode.isLazy()) return readerValue;
addInstr(new CopyInstr(result, readerValue));
} else {
Operand v2 = build(opAsgnNode.getValueNode());
Variable setValue = createTemporaryVariable();
addInstr(CallInstr.create(scope, setValue, opAsgnNode.getOperatorSymbolName(), readerValue, new Operand[]{v2}, null));
addInstr(CallInstr.create(scope, callType, writerValue, opAsgnNode.getVariableSymbolNameAsgn(), v1, new Operand[] {setValue}, null));
if (!opAsgnNode.isLazy()) return setValue;
addInstr(new CopyInstr(result, setValue));
}
addInstr(new JumpInstr(endLabel));
addInstr(new LabelInstr(lazyLabel));
addInstr(new CopyInstr(result, manager.getNil()));
addInstr(new LabelInstr(endLabel));
return result;
}
private Operand buildColon2ForConstAsgnDeclNode(Node lhs, Variable valueResult, boolean constMissing) {
Variable leftModule = createTemporaryVariable();
RubySymbol name;
if (lhs instanceof Colon2Node) {
Colon2Node colon2Node = (Colon2Node) lhs;
name = colon2Node.getName();
Operand leftValue = build(colon2Node.getLeftNode());
copy(leftModule, leftValue);
} else {
copy(leftModule, new ObjectClass());
name = ((Colon3Node) lhs).getName();
}
addInstr(new SearchModuleForConstInstr(valueResult, leftModule, name, false, constMissing));
return leftModule;
}
public Operand buildOpAsgnConstDeclNode(OpAsgnConstDeclNode node) {
if (node.isOr()) {
Variable result = createTemporaryVariable();
Label done = getNewLabel();
Operand module = buildColon2ForConstAsgnDeclNode(node.getFirstNode(), result, false);
addInstr(BNEInstr.create(done, result, UndefinedValue.UNDEFINED));
Operand rhsValue = build(node.getSecondNode());
copy(result, rhsValue);
addInstr(new PutConstInstr(module, ((Colon3Node) node.getFirstNode()).getName(), rhsValue));
addInstr(new LabelInstr(done));
return result;
} else if (node.isAnd()) {
Variable result = createTemporaryVariable();
Label done = getNewLabel();
Operand module = buildColon2ForConstAsgnDeclNode(node.getFirstNode(), result, true);
addInstr(new BFalseInstr(done, result));
Operand rhsValue = build(node.getSecondNode());
copy(result, rhsValue);
addInstr(new PutConstInstr(module, ((Colon3Node) node.getFirstNode()).getName(), rhsValue));
addInstr(new LabelInstr(done));
return result;
}
Variable result = createTemporaryVariable();
Operand lhs = build(node.getFirstNode());
Operand rhs = build(node.getSecondNode());
addInstr(CallInstr.create(scope, result, node.getSymbolOperator(), lhs, new Operand[] { rhs }, null));
return addResultInstr(new CopyInstr(createTemporaryVariable(), putConstantAssignment(node, result)));
}
public Operand buildOpAsgnAnd(OpAsgnAndNode andNode) {
Label l = getNewLabel();
Operand v1 = build(andNode.getFirstNode());
Variable result = getValueInTemporaryVariable(v1);
addInstr(createBranch(v1, manager.getFalse(), l));
Operand v2 = build(andNode.getSecondNode());
addInstr(new CopyInstr(result, v2));
addInstr(new LabelInstr(l));
return result;
}
public Operand buildOpAsgnOr(final OpAsgnOrNode orNode) {
Label l1 = getNewLabel();
Label l2 = null;
Variable flag = createTemporaryVariable();
Operand v1;
boolean needsDefnCheck = orNode.getFirstNode().needsDefinitionCheck();
if (needsDefnCheck) {
l2 = getNewLabel();
v1 = buildGetDefinition(orNode.getFirstNode());
addInstr(new CopyInstr(flag, v1));
addInstr(createBranch(flag, manager.getNil(), l2));
}
v1 = build(orNode.getFirstNode());
addInstr(new CopyInstr(flag, v1));
Variable result = getValueInTemporaryVariable(v1);
if (needsDefnCheck) {
addInstr(new LabelInstr(l2));
}
addInstr(createBranch(flag, manager.getTrue(), l1));
Operand v2 = build(orNode.getSecondNode());
addInstr(new CopyInstr(result, v2));
addInstr(new LabelInstr(l1));
return result;
}
public Operand buildOpElementAsgn(OpElementAsgnNode node) {
if (node.isOr()) return buildOpElementAsgnWith(node, manager.getTrue());
if (node.isAnd()) return buildOpElementAsgnWith(node, manager.getFalse());
return buildOpElementAsgnWithMethod(node);
}
private Operand buildOpElementAsgnWith(OpElementAsgnNode opElementAsgnNode, Boolean truthy) {
Node receiver = opElementAsgnNode.getReceiverNode();
CallType callType = receiver instanceof SelfNode ? CallType.FUNCTIONAL : CallType.NORMAL;
Operand array = buildWithOrder(receiver, opElementAsgnNode.containsVariableAssignment());
Label endLabel = getNewLabel();
Variable elt = createTemporaryVariable();
Operand[] argList = setupCallArgs(opElementAsgnNode.getArgsNode());
addInstr(CallInstr.create(scope, callType, elt, symbol(ArrayDerefInstr.AREF), array, argList, null));
addInstr(createBranch(elt, truthy, endLabel));
Operand value = build(opElementAsgnNode.getValueNode());
argList = addArg(argList, value);
addInstr(CallInstr.create(scope, callType, elt, symbol(ArrayDerefInstr.ASET), array, argList, null));
addInstr(new CopyInstr(elt, value));
addInstr(new LabelInstr(endLabel));
return elt;
}
public Operand buildOpElementAsgnWithMethod(OpElementAsgnNode opElementAsgnNode) {
Node receiver = opElementAsgnNode.getReceiverNode();
CallType callType = receiver instanceof SelfNode ? CallType.FUNCTIONAL : CallType.NORMAL;
Operand array = buildWithOrder(receiver, opElementAsgnNode.containsVariableAssignment());
Operand[] argList = setupCallArgs(opElementAsgnNode.getArgsNode());
Variable elt = createTemporaryVariable();
addInstr(CallInstr.create(scope, callType, elt, symbol(ArrayDerefInstr.AREF), array, argList, null));
Operand value = build(opElementAsgnNode.getValueNode());
RubySymbol operation = opElementAsgnNode.getOperatorSymbolName();
addInstr(CallInstr.create(scope, callType, elt, operation, elt, new Operand[] { value }, null));
Variable tmp = createTemporaryVariable();
argList = addArg(argList, elt);
addInstr(CallInstr.create(scope, callType, tmp, symbol(ArrayDerefInstr.ASET), array, argList, null));
return elt;
}
public Operand buildOr(final OrNode orNode) {
if (orNode.getFirstNode().getNodeType().alwaysTrue()) return build(orNode.getFirstNode());
if (orNode.getFirstNode().getNodeType().alwaysFalse()) {
build(orNode.getFirstNode());
return build(orNode.getSecondNode());
}
Label endOfExprLabel = getNewLabel();
Operand left = build(orNode.getFirstNode());
Variable result = getValueInTemporaryVariable(left);
addInstr(createBranch(left, manager.getTrue(), endOfExprLabel));
Operand right = build(orNode.getSecondNode());
addInstr(new CopyInstr(result, right));
addInstr(new LabelInstr(endOfExprLabel));
return result;
}
private InterpreterContext buildPrePostExeInner(Node body) {
addInstr(new CopyInstr(getCurrentScopeVariable(), CurrentScope.INSTANCE));
addInstr(new CopyInstr(getCurrentModuleVariable(), SCOPE_MODULE[0]));
build(body);
addInstr(new ReturnInstr(new Nil()));
return scope.allocateInterpreterContext(instructions);
}
private List<Instr> buildPreExeInner(Node body) {
build(body);
return instructions;
}
public Operand buildPostExe(PostExeNode postExeNode) {
IRScope topLevel = scope.getRootLexicalScope();
IRScope nearestLVarScope = scope.getNearestTopLocalVariableScope();
IRClosure endClosure = new IRClosure(manager, scope, postExeNode.getLine(), nearestLVarScope.getStaticScope(),
Signature.from(postExeNode), CommonByteLists._END_, true);
endClosure.setIsEND();
newIRBuilder(manager, endClosure).buildPrePostExeInner(postExeNode.getBodyNode());
addInstr(new RecordEndBlockInstr(topLevel, new WrappedIRClosure(buildSelf(), endClosure)));
return manager.getNil();
}
public Operand buildPreExe(PreExeNode preExeNode) {
IRBuilder builder = newIRBuilder(manager, scope);
builder.currentScopeVariable = currentScopeVariable;
List<Instr> beginInstrs = builder.buildPreExeInner(preExeNode.getBodyNode());
instructions.addAll(afterPrologueIndex, beginInstrs);
afterPrologueIndex += beginInstrs.size();
return manager.getNil();
}
public Operand buildRational(RationalNode rationalNode) {
return new Rational((ImmutableLiteral) build(rationalNode.getNumerator()),
(ImmutableLiteral) build(rationalNode.getDenominator()));
}
public Operand buildRedo(RedoNode redoNode) {
if (!activeEnsureBlockStack.empty()) {
emitEnsureBlocks(getCurrentLoop());
}
IRLoop currLoop = getCurrentLoop();
if (currLoop != null) {
addInstr(new JumpInstr(currLoop.iterStartLabel));
} else {
if (scope instanceof IRClosure) {
if (scope instanceof IREvalScript) {
throwSyntaxError(redoNode, "Can't escape from eval with redo");
} else {
addInstr(new ThreadPollInstr(true));
Label startLabel = new Label(scope.getId() + "_START", 0);
instructions.add(afterPrologueIndex, new LabelInstr(startLabel));
addInstr(new JumpInstr(startLabel));
}
} else {
throwSyntaxError(redoNode, "Invalid redo");
}
}
return manager.getNil();
}
public Operand buildRegexp(RegexpNode reNode) {
return copyAndReturnValue(new Regexp(reNode.getValue(), reNode.getOptions()));
}
public Operand buildRescue(RescueNode node) {
return buildEnsureInternal(node, null);
}
private boolean canBacktraceBeRemoved(RescueNode rescueNode) {
if (RubyInstanceConfig.FULL_TRACE_ENABLED || !(rescueNode instanceof RescueModNode) &&
rescueNode.getElseNode() != null) return false;
RescueBodyNode rescueClause = rescueNode.getRescueNode();
if (rescueClause.getOptRescueNode() != null) return false;
if (rescueClause.getExceptionNodes() != null) return false;
Node body = rescueClause.getBodyNode();
if (body instanceof GlobalVarNode && ((GlobalVarNode) body).getName().idString().equals("$!")) return false;
return body instanceof SideEffectFree;
}
private Operand buildRescueInternal(RescueNode rescueNode, EnsureBlockInfo ensure) {
boolean needsBacktrace = !canBacktraceBeRemoved(rescueNode);
Label rBeginLabel = getNewLabel();
Label rEndLabel = ensure.end;
Label rescueLabel = getNewLabel();
ensure.needsBacktrace = needsBacktrace;
addInstr(new LabelInstr(rBeginLabel));
addInstr(new ExceptionRegionStartMarkerInstr(rescueLabel));
activeRescuers.push(rescueLabel);
addInstr(manager.needsBacktrace(needsBacktrace));
Operand tmp = manager.getNil();
Variable rv = createTemporaryVariable();
if (rescueNode.getBodyNode() != null) tmp = build(rescueNode.getBodyNode());
RescueBlockInfo rbi = new RescueBlockInfo(rBeginLabel, ensure.savedGlobalException);
activeRescueBlockStack.push(rbi);
addInstr(new ExceptionRegionEndMarkerInstr());
activeRescuers.pop();
if (rescueNode.getElseNode() != null) {
addInstr(new LabelInstr(getNewLabel()));
tmp = build(rescueNode.getElseNode());
}
if (tmp != U_NIL) {
addInstr(new CopyInstr(rv, tmp));
ensure.cloneIntoHostScope(this);
addInstr(new JumpInstr(rEndLabel));
}
addInstr(new LabelInstr(rescueLabel));
if (!needsBacktrace) addInstr(manager.needsBacktrace(true));
Variable exc = addResultInstr(new ReceiveRubyExceptionInstr(createTemporaryVariable()));
buildRescueBodyInternal(rescueNode.getRescueNode(), rv, exc, rEndLabel);
activeRescueBlockStack.pop();
return rv;
}
private void outputExceptionCheck(Operand excType, Operand excObj, Label caughtLabel) {
Variable eqqResult = addResultInstr(new RescueEQQInstr(createTemporaryVariable(), excType, excObj));
addInstr(createBranch(eqqResult, manager.getTrue(), caughtLabel));
}
private void buildRescueBodyInternal(RescueBodyNode rescueBodyNode, Variable rv, Variable exc, Label endLabel) {
final Node exceptionList = rescueBodyNode.getExceptionNodes();
Label uncaughtLabel = getNewLabel();
Label caughtLabel = getNewLabel();
if (exceptionList != null) {
if (exceptionList instanceof ListNode) {
Node[] exceptionNodes = ((ListNode) exceptionList).children();
for (int i = 0; i < exceptionNodes.length; i++) {
outputExceptionCheck(build(exceptionNodes[i]), exc, caughtLabel);
}
} else if (exceptionList instanceof SplatNode) {
outputExceptionCheck(build(((SplatNode)exceptionList).getValue()), exc, caughtLabel);
} else {
outputExceptionCheck(build(exceptionList), exc, caughtLabel);
}
} else {
outputExceptionCheck(manager.getStandardError(), exc, caughtLabel);
}
addInstr(new LabelInstr(uncaughtLabel));
if (rescueBodyNode.getOptRescueNode() != null) {
buildRescueBodyInternal(rescueBodyNode.getOptRescueNode(), rv, exc, endLabel);
} else {
addInstr(new ThrowExceptionInstr(exc));
}
addInstr(new LabelInstr(caughtLabel));
Node realBody = rescueBodyNode.getBodyNode();
Operand x = build(realBody);
if (x != U_NIL) {
addInstr(new CopyInstr(rv, x));
activeEnsureBlockStack.peek().cloneIntoHostScope(this);
addInstr(new JumpInstr(endLabel));
}
}
public Operand buildRetry() {
if (activeRescueBlockStack.empty()) {
addInstr(new ThrowExceptionInstr(IRException.RETRY_LocalJumpError));
} else {
addInstr(new ThreadPollInstr(true));
RescueBlockInfo rbi = activeRescueBlockStack.peek();
addInstr(new PutGlobalVarInstr(symbol("$!"), rbi.savedExceptionVariable));
addInstr(new JumpInstr(rbi.entryLabel));
scope.setHasLoopsFlag();
}
return manager.getNil();
}
private Operand processEnsureRescueBlocks(Operand retVal) {
if (!activeEnsureBlockStack.empty()) {
retVal = addResultInstr(new CopyInstr(createTemporaryVariable(), retVal));
emitEnsureBlocks(null);
}
return retVal;
}
public Operand buildReturn(ReturnNode returnNode) {
Operand retVal = build(returnNode.getValueNode());
if (scope instanceof IRClosure) {
if (scope.isWithinEND()) {
addInstr(new ThrowExceptionInstr(IRException.RETURN_LocalJumpError));
} else {
boolean definedWithinMethod = scope.getNearestMethod() != null;
if (!(scope instanceof IREvalScript) && !(scope instanceof IRFor))
addInstr(new CheckForLJEInstr(definedWithinMethod));
addInstr(new NonlocalReturnInstr(retVal,
definedWithinMethod ? scope.getNearestMethod().getId() : "--none--"));
}
} else if (scope.isModuleBody()) {
IRMethod sm = scope.getNearestMethod();
if (sm == null) addInstr(new ThrowExceptionInstr(IRException.RETURN_LocalJumpError));
if (sm != null) addInstr(new NonlocalReturnInstr(retVal, sm.getId()));
} else {
retVal = processEnsureRescueBlocks(retVal);
if (RubyInstanceConfig.FULL_TRACE_ENABLED) {
addInstr(new TraceInstr(RubyEvent.RETURN, getName(), getFileName(), returnNode.getLine() + 1));
}
addInstr(new ReturnInstr(retVal));
}
return U_NIL;
}
public InterpreterContext buildEvalRoot(RootNode rootNode) {
executesOnce = false;
needsCodeCoverage = false;
addInstr(manager.newLineNumber(scope.getLine()));
prepareImplicitState();
addCurrentScopeAndModule();
afterPrologueIndex = instructions.size() - 1;
Operand returnValue = rootNode.getBodyNode() == null ? manager.getNil() : build(rootNode.getBodyNode());
addInstr(new ReturnInstr(returnValue));
return scope.allocateInterpreterContext(instructions);
}
public static InterpreterContext buildRoot(IRManager manager, RootNode rootNode) {
String file = rootNode.getFile();
IRScriptBody script = new IRScriptBody(manager, file == null ? "(anon)" : file, rootNode.getStaticScope());
return topIRBuilder(manager, script).buildRootInner(rootNode);
}
private void addCurrentScopeAndModule() {
addInstr(new CopyInstr(getCurrentScopeVariable(), CurrentScope.INSTANCE));
addInstr(new CopyInstr(getCurrentModuleVariable(), SCOPE_MODULE[0]));
}
private InterpreterContext buildRootInner(RootNode rootNode) {
needsCodeCoverage = rootNode.needsCoverage();
prepareImplicitState();
addCurrentScopeAndModule();
afterPrologueIndex = instructions.size() - 1;
addInstr(new ReturnInstr(build(rootNode.getBodyNode())));
scope.computeScopeFlagsEarly(instructions);
if (scope.canReceiveNonlocalReturns()) handleNonlocalReturnInMethod();
return scope.allocateInterpreterContext(instructions);
}
public Variable buildSelf() {
return scope.getSelf();
}
public Operand buildSplat(SplatNode splatNode) {
return addResultInstr(new BuildSplatInstr(createTemporaryVariable(), build(splatNode.getValue()), true));
}
public Operand buildStr(StrNode strNode) {
Operand literal = buildStrRaw(strNode);
return literal instanceof FrozenString ? literal : copyAndReturnValue(literal);
}
public Operand buildStrRaw(StrNode strNode) {
if (strNode instanceof FileNode) return new Filename();
int line = strNode.getLine();
if (strNode.isFrozen()) return new FrozenString(strNode.getValue(), strNode.getCodeRange(), scope.getFile(), line);
return new StringLiteral(strNode.getValue(), strNode.getCodeRange(), scope.getFile(), line);
}
private Operand buildSuperInstr(Operand block, Operand[] args) {
CallInstr superInstr;
Variable ret = createTemporaryVariable();
if (scope instanceof IRMethod && scope.getLexicalParent() instanceof IRClassBody) {
if (((IRMethod) scope).isInstanceMethod) {
superInstr = new InstanceSuperInstr(scope, ret, getCurrentModuleVariable(), getName(), args, block, scope.maybeUsingRefinements());
} else {
superInstr = new ClassSuperInstr(scope, ret, getCurrentModuleVariable(), getName(), args, block, scope.maybeUsingRefinements());
}
} else {
superInstr = new UnresolvedSuperInstr(scope, ret, buildSelf(), args, block, scope.maybeUsingRefinements());
}
receiveBreakException(block, superInstr);
return ret;
}
public Operand buildSuper(SuperNode superNode) {
if (scope.isModuleBody()) return buildSuperInScriptBody();
Operand[] args = setupCallArgs(superNode.getArgsNode());
Operand block = setupCallClosure(superNode.getIterNode());
if (block == null) block = getYieldClosureVariable();
return buildSuperInstr(block, args);
}
private Operand buildSuperInScriptBody() {
return addResultInstr(new UnresolvedSuperInstr(scope, createTemporaryVariable(), buildSelf(), EMPTY_OPERANDS, null, scope.maybeUsingRefinements()));
}
public Operand buildSValue(SValueNode node) {
return copyAndReturnValue(new SValue(build(node.getValue())));
}
public Operand buildSymbol(SymbolNode node) {
return new Symbol(node.getName());
}
public Operand buildTrue() {
return manager.getTrue();
}
public Operand buildUndef(Node node) {
Operand methName = build(((UndefNode) node).getName());
return addResultInstr(new UndefMethodInstr(createTemporaryVariable(), methName));
}
private Operand buildConditionalLoop(Node conditionNode,
Node bodyNode, boolean isWhile, boolean isLoopHeadCondition) {
if (isLoopHeadCondition &&
((isWhile && conditionNode.getNodeType().alwaysFalse()) ||
(!isWhile && conditionNode.getNodeType().alwaysTrue()))) {
build(conditionNode);
return manager.getNil();
} else {
IRLoop loop = new IRLoop(scope, getCurrentLoop());
Variable loopResult = loop.loopResult;
Label setupResultLabel = getNewLabel();
loopStack.push(loop);
addInstr(new LabelInstr(loop.loopStartLabel));
if (isLoopHeadCondition) {
Operand cv = build(conditionNode);
addInstr(createBranch(cv, isWhile ? manager.getFalse() : manager.getTrue(), setupResultLabel));
}
addInstr(new LabelInstr(loop.iterStartLabel));
addInstr(new ThreadPollInstr(true));
if (bodyNode != null) build(bodyNode);
addInstr(new LabelInstr(loop.iterEndLabel));
if (isLoopHeadCondition) {
addInstr(new JumpInstr(loop.loopStartLabel));
} else {
Operand cv = build(conditionNode);
addInstr(createBranch(cv, isWhile ? manager.getTrue() : manager.getFalse(), loop.iterStartLabel));
}
addInstr(new LabelInstr(setupResultLabel));
addInstr(new CopyInstr(loopResult, manager.getNil()));
addInstr(new LabelInstr(loop.loopEndLabel));
loopStack.pop();
return loopResult;
}
}
public Operand buildUntil(final UntilNode untilNode) {
return buildConditionalLoop(untilNode.getConditionNode(), untilNode.getBodyNode(), false, untilNode.evaluateAtStart());
}
public Operand buildVAlias(VAliasNode valiasNode) {
addInstr(new GVarAliasInstr(new StringLiteral(valiasNode.getNewName()), new StringLiteral(valiasNode.getOldName())));
return manager.getNil();
}
public Operand buildVCall(Variable result, VCallNode node) {
if (result == null) result = createTemporaryVariable();
return addResultInstr(CallInstr.create(scope, CallType.VARIABLE, result, node.getName(), buildSelf(), EMPTY_OPERANDS, null));
}
public Operand buildWhile(final WhileNode whileNode) {
return buildConditionalLoop(whileNode.getConditionNode(), whileNode.getBodyNode(), true, whileNode.evaluateAtStart());
}
public Operand buildXStr(XStrNode node) {
return addResultInstr(CallInstr.create(scope, CallType.FUNCTIONAL, createTemporaryVariable(),
manager.getRuntime().newSymbol("`"), Self.SELF,
new Operand[] { new FrozenString(node.getValue(), node.getCodeRange(), scope.getFile(), node.getLine()) }, null));
}
public Operand buildYield(YieldNode node, Variable result) {
if (scope instanceof IRScriptBody) throwSyntaxError(node, "Invalid yield");
boolean unwrap = true;
Node argNode = node.getArgsNode();
if (argNode != null && (argNode instanceof ArrayNode) && ((ArrayNode)argNode).size() == 1) {
argNode = ((ArrayNode)argNode).getLast();
unwrap = false;
}
Variable ret = result == null ? createTemporaryVariable() : result;
Operand value = argNode instanceof ArrayNode && unwrap ? buildArray((ArrayNode)argNode, true) : build(argNode);
addInstr(new YieldInstr(ret, getYieldClosureVariable(), value, unwrap));
return ret;
}
public Operand copy(Operand value) {
return copy(null, value);
}
public Operand copy(Variable result, Operand value) {
return addResultInstr(new CopyInstr(result == null ? createTemporaryVariable() : result, value));
}
public Operand buildZArray(Variable result) {
return copy(result, new Array());
}
private Operand buildZSuperIfNest(final Operand block) {
final IRBuilder builder = this;
CodeBlock zsuperBuilder = new CodeBlock() {
public Operand run() {
Variable scopeDepth = createTemporaryVariable();
addInstr(new ArgScopeDepthInstr(scopeDepth));
Label allDoneLabel = getNewLabel();
int depthFromSuper = 0;
Label next = null;
IRBuilder superBuilder = builder;
IRScope superScope = scope;
Variable zsuperResult = createTemporaryVariable();
while (superScope instanceof IRClosure) {
if (next != null) addInstr(new LabelInstr(next));
next = getNewLabel();
addInstr(BNEInstr.create(next, manager.newFixnum(depthFromSuper), scopeDepth));
Operand[] args = adjustVariableDepth(getCallArgs(superScope, superBuilder), depthFromSuper);
addInstr(new ZSuperInstr(scope, zsuperResult, buildSelf(), args, block, scope.maybeUsingRefinements()));
addInstr(new JumpInstr(allDoneLabel));
superBuilder = superBuilder != null && superBuilder.parent != null ? superBuilder.parent : null;
superScope = superScope.getLexicalParent();
depthFromSuper++;
}
addInstr(new LabelInstr(next));
if (superScope instanceof IRMethod) {
Operand[] args = adjustVariableDepth(getCallArgs(superScope, superBuilder), depthFromSuper);
addInstr(new ZSuperInstr(scope, zsuperResult, buildSelf(), args, block, scope.maybeUsingRefinements()));
}
addInstr(new LabelInstr(allDoneLabel));
return zsuperResult;
}
};
return receiveBreakException(block, zsuperBuilder);
}
public Operand buildZSuper(ZSuperNode zsuperNode) {
if (scope.isModuleBody()) return buildSuperInScriptBody();
Operand block = setupCallClosure(zsuperNode.getIterNode());
if (block == null) block = getYieldClosureVariable();
if (scope instanceof IRMethod) {
return buildSuperInstr(block, getCallArgs(scope, this));
} else {
return buildZSuperIfNest(block);
}
}
private Operand[] adjustVariableDepth(Operand[] args, int depthFromSuper) {
Operand[] newArgs = new Operand[args.length];
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof Hash) {
newArgs[i] = ((Hash) args[i]).cloneForLVarDepth(depthFromSuper);
} else {
newArgs[i] = ((DepthCloneable) args[i]).cloneForDepth(depthFromSuper);
}
}
return newArgs;
}
private InterpreterContext buildModuleOrClassBody(Node bodyNode, int startLine, int endLine) {
addInstr(new TraceInstr(RubyEvent.CLASS, null, getFileName(), startLine + 1));
prepareImplicitState();
addCurrentScopeAndModule();
Operand bodyReturnValue = build(bodyNode);
addInstr(manager.newLineNumber(endLine));
addInstr(new TraceInstr(RubyEvent.END, null, getFileName(), endLine + 1));
addInstr(new ReturnInstr(bodyReturnValue));
return scope.allocateInterpreterContext(instructions);
}
private RubySymbol methodNameFor() {
IRScope method = scope.getNearestMethod();
return method == null ? null : method.getName();
}
private TemporaryVariable createTemporaryVariable() {
return scope.createTemporaryVariable();
}
public LocalVariable getLocalVariable(RubySymbol name, int scopeDepth) {
return scope.getLocalVariable(name, scopeDepth);
}
public LocalVariable getNewLocalVariable(RubySymbol name, int scopeDepth) {
return scope.getNewLocalVariable(name, scopeDepth);
}
public RubySymbol getName() {
return scope.getName();
}
private Label getNewLabel() {
return scope.getNewLabel();
}
private String getFileName() {
return scope.getFile();
}
private RubySymbol symbol(String id) {
return manager.runtime.newSymbol(id);
}
private RubySymbol symbol(ByteList bytelist) {
return manager.runtime.newSymbol(bytelist);
}
public static Operand[] getCallArgs(IRScope scope, IRBuilder builder) {
List<Operand> callArgs = new ArrayList<>(5);
List<KeyValuePair<Operand, Operand>> keywordArgs = new ArrayList<>(3);
if (builder != null) {
for (Instr instr : builder.instructions) {
extractCallOperands(callArgs, keywordArgs, instr);
}
} else {
for (Instr instr : scope.interpreterContext.getInstructions()) {
extractCallOperands(callArgs, keywordArgs, instr);
}
}
return getCallOperands(scope, callArgs, keywordArgs);
}
private static void extractCallOperands(List<Operand> callArgs, List<KeyValuePair<Operand, Operand>> keywordArgs, Instr instr) {
if (instr instanceof ReceiveKeywordRestArgInstr) {
keywordArgs.add(0, new KeyValuePair<Operand, Operand>(Symbol.KW_REST_ARG_DUMMY, ((ReceiveArgBase) instr).getResult()));
} else if (instr instanceof ReceiveKeywordArgInstr) {
ReceiveKeywordArgInstr receiveKwargInstr = (ReceiveKeywordArgInstr) instr;
keywordArgs.add(new KeyValuePair<Operand, Operand>(new Symbol(receiveKwargInstr.getKey()), receiveKwargInstr.getResult()));
} else if (instr instanceof ReceiveRestArgInstr) {
callArgs.add(new Splat(((ReceiveRestArgInstr) instr).getResult()));
} else if (instr instanceof ReceiveArgBase) {
callArgs.add(((ReceiveArgBase) instr).getResult());
}
}
private static Operand[] getCallOperands(IRScope scope, List<Operand> callArgs, List<KeyValuePair<Operand, Operand>> keywordArgs) {
if (scope.receivesKeywordArgs()) {
int i = 0;
Operand[] args = new Operand[callArgs.size() + 1];
for (Operand arg: callArgs) {
args[i++] = arg;
}
args[i] = new Hash(keywordArgs, true);
return args;
}
return callArgs.toArray(new Operand[callArgs.size()]);
}
public static Instr createBranch(Operand v1, Operand v2, Label jmpTarget) {
if (v2 instanceof Boolean) {
Boolean lhs = (Boolean) v2;
if (lhs.isTrue()) {
if (v1.isTruthyImmediate()) return new JumpInstr(jmpTarget);
if (v1.isFalseyImmediate()) return NopInstr.NOP;
return new BTrueInstr(jmpTarget, v1);
} else if (lhs.isFalse()) {
if (v1.isTruthyImmediate()) return NopInstr.NOP;
if (v1.isFalseyImmediate()) return new JumpInstr(jmpTarget);
return new BFalseInstr(jmpTarget, v1);
}
} else if (v2 instanceof Nil) {
if (v1 instanceof Nil) {
return new JumpInstr(jmpTarget);
} else {
return new BNilInstr(jmpTarget, v1);
}
}
if (v2 == UndefinedValue.UNDEFINED) {
if (v1 == UndefinedValue.UNDEFINED) {
return new JumpInstr(jmpTarget);
} else {
return new BUndefInstr(jmpTarget, v1);
}
}
throw new RuntimeException("BUG: no BEQ");
}
public TemporaryVariable getYieldClosureVariable() {
if (yieldClosureVariable == null) {
return yieldClosureVariable = createTemporaryVariable();
}
return yieldClosureVariable;
}
public Variable getCurrentModuleVariable() {
if (currentModuleVariable == null) currentModuleVariable = scope.createCurrentModuleVariable();
return currentModuleVariable;
}
public Variable getCurrentScopeVariable() {
if (currentScopeVariable == null) currentScopeVariable = scope.createCurrentScopeVariable();
return currentScopeVariable;
}
}