
org.jruby.prism.builder.IRBuilderPrism Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jruby-prism Show documentation
Show all versions of jruby-prism Show documentation
Java portion of JRuby Prism parser support.
The newest version!
package org.jruby.prism.builder;
import org.jcodings.Encoding;
import org.jcodings.specific.ASCIIEncoding;
import org.jcodings.specific.EUCJPEncoding;
import org.jcodings.specific.USASCIIEncoding;
import org.jcodings.specific.UTF8Encoding;
import org.jcodings.specific.Windows_31JEncoding;
import org.jruby.ParseResult;
import org.jruby.Ruby;
import org.jruby.RubyBignum;
import org.jruby.RubyFloat;
import org.jruby.RubyKernel;
import org.jruby.RubyNumeric;
import org.jruby.RubyString;
import org.jruby.RubySymbol;
import org.jruby.compiler.NotCompilableException;
import org.jruby.ir.IRClosure;
import org.jruby.ir.IRManager;
import org.jruby.ir.IRMethod;
import org.jruby.ir.IRModuleBody;
import org.jruby.ir.IRScope;
import org.jruby.ir.IRScriptBody;
import org.jruby.ir.Tuple;
import org.jruby.ir.builder.IRBuilder;
import org.jruby.ir.builder.LazyMethodDefinition;
import org.jruby.ir.instructions.*;
import org.jruby.ir.instructions.defined.GetErrorInfoInstr;
import org.jruby.ir.instructions.defined.RestoreErrorInfoInstr;
import org.jruby.ir.operands.Array;
import org.jruby.ir.operands.Bignum;
import org.jruby.ir.operands.Complex;
import org.jruby.ir.operands.CurrentScope;
import org.jruby.ir.operands.Fixnum;
import org.jruby.ir.operands.Float;
import org.jruby.ir.operands.FrozenString;
import org.jruby.ir.operands.Hash;
import org.jruby.ir.operands.ImmutableLiteral;
import org.jruby.ir.operands.Label;
import org.jruby.ir.operands.LocalVariable;
import org.jruby.ir.operands.MutableString;
import org.jruby.ir.operands.NullBlock;
import org.jruby.ir.operands.Operand;
import org.jruby.ir.operands.Rational;
import org.jruby.ir.operands.Regexp;
import org.jruby.ir.operands.Splat;
import org.jruby.ir.operands.Symbol;
import org.jruby.ir.operands.SymbolProc;
import org.jruby.ir.operands.UndefinedValue;
import org.jruby.ir.operands.Variable;
import org.jruby.parser.StaticScope;
import org.jruby.parser.StaticScopeFactory;
import org.jruby.runtime.ArgumentDescriptor;
import org.jruby.runtime.ArgumentType;
import org.jruby.runtime.CallType;
import org.jruby.runtime.Signature;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ByteList;
import org.jruby.util.DefinedMessage;
import org.jruby.util.KCode;
import org.jruby.util.KeyValuePair;
import org.jruby.util.RegexpOptions;
import org.jruby.util.StringSupport;
import org.jruby.util.cli.Options;
import org.prism.Nodes;
import org.prism.Nodes.*;
import org.jruby.prism.parser.ParseResultPrism;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static org.jruby.ir.instructions.RuntimeHelperCall.Methods.*;
import static org.jruby.runtime.CallType.VARIABLE;
import static org.jruby.runtime.ThreadContext.*;
import static org.jruby.util.CommonByteLists.*;
import static org.jruby.util.RubyStringBuilder.inspectIdentifierByteList;
import static org.jruby.util.RubyStringBuilder.str;
import static org.jruby.util.StringSupport.CR_UNKNOWN;
public class IRBuilderPrism extends IRBuilder {
byte[] source;
Nodes.Source nodeSource;
StaticScope staticScope;
public IRBuilderPrism(IRManager manager, IRScope scope, IRBuilder parent, IRBuilder variableBuilder, Encoding encoding) {
super(manager, scope, parent, variableBuilder, encoding);
if (parent != null) {
source = ((IRBuilderPrism) parent).source;
nodeSource = ((IRBuilderPrism) parent).nodeSource;
}
staticScope = scope.getStaticScope();
staticScope.setFile(scope.getFile()); // staticScope and IRScope contain the same field.
}
// FIXME: Delete once we are no longer depedent on source code
public void setSourceFrom(IRBuilderPrism other) {
if (other.nodeSource == null) throw new RuntimeException("WETF");
this.nodeSource = other.nodeSource;
this.source = other.source;
}
protected void hackPostExeSource(IRBuilder builder) {
setSourceFrom((IRBuilderPrism) builder);
}
// FIXME: Delete once we are no longer depedent on source code
public void setSourceFrom(Nodes.Source nodeSource, byte[] source) {
this.nodeSource = nodeSource;
this.source = source;
}
@Override
public Operand build(ParseResult result) {
// FIXME: Missing support for executes once
this.executesOnce = false;
this.source = ((ParseResultPrism) result).getSource();
this.nodeSource = ((ParseResultPrism) result).getSourceNode();
// System.out.println("NAME: " + fileName);
// System.out.println(((ParseResultPrism) result).getRoot());
return build(((ProgramNode) result.getAST()).statements);
}
protected Operand build(Node node) {
return build(null, node);
}
/*
* @param result preferred result variable (this reduces temp vars pinning values).
* @param node to be built
*/
protected Operand build(Variable result, Node node) {
if (node == null) return nil();
if (node.hasNewLineFlag()) determineIfWeNeedLineNumber(getLine(node), true, false, node instanceof DefNode);
if (node instanceof AliasGlobalVariableNode) {
return buildAliasGlobalVariable((AliasGlobalVariableNode) node);
} else if (node instanceof AliasMethodNode) {
return buildAliasMethod((AliasMethodNode) node);
} else if (node instanceof AndNode) {
return buildAnd((AndNode) node);
} else if (node instanceof ArrayNode) {
return buildArray((ArrayNode) node); // MISSING: ArrayPatternNode
} else if (node instanceof AssocSplatNode) {
return buildAssocSplat(result, (AssocSplatNode) node);
} else if (node instanceof BackReferenceReadNode) {
return buildBackReferenceRead(result, (BackReferenceReadNode) node);
} else if (node instanceof BeginNode) {
return buildBegin((BeginNode) node);
} else if (node instanceof BlockArgumentNode) {
return buildBlockArgument((BlockArgumentNode) node);
} else if (node instanceof BlockNode) {
return buildBlock((BlockNode) node);
// BlockParameterNode processed during call building.
} else if (node instanceof BreakNode) {
return buildBreak((BreakNode) node);
} else if (node instanceof CallNode) {
return buildCall(result, (CallNode) node, ((CallNode) node).name);
} else if (node instanceof CallAndWriteNode) {
return buildCallAndWrite((CallAndWriteNode) node);
} else if (node instanceof CallOrWriteNode) {
return buildCallOrWrite((CallOrWriteNode) node);
} else if (node instanceof CallOperatorWriteNode) { // foo.bar += baz
return buildCallOperatorWrite((CallOperatorWriteNode) node);
} else if (node instanceof CaseNode) {
return buildCase((CaseNode) node);
} else if (node instanceof CaseMatchNode) {
return buildCaseMatch((CaseMatchNode) node);
} else if (node instanceof ClassNode) {
return buildClass((ClassNode) node);
} else if (node instanceof ClassVariableAndWriteNode) {
return buildClassAndVariableWrite((ClassVariableAndWriteNode) node);
} else if (node instanceof ClassVariableOperatorWriteNode) {
return buildClassVariableOperatorWrite((ClassVariableOperatorWriteNode) node);
} else if (node instanceof ClassVariableOrWriteNode) {
return buildClassOrVariableWrite((ClassVariableOrWriteNode) node);
} else if (node instanceof ClassVariableReadNode) {
return buildClassVariableRead(result, (ClassVariableReadNode) node);
} else if (node instanceof ClassVariableWriteNode) {
return buildClassVariableWrite((ClassVariableWriteNode) node);
} else if (node instanceof ConstantAndWriteNode) {
return buildConstantAndWrite((ConstantAndWriteNode) node);
} else if (node instanceof ConstantOperatorWriteNode) {
return buildConstantOperatorWrite((ConstantOperatorWriteNode) node);
} else if (node instanceof ConstantOrWriteNode) {
return buildConstantOrWrite((ConstantOrWriteNode) node);
} else if (node instanceof ConstantPathNode) {
return buildConstantPath(result, (ConstantPathNode) node);
} else if (node instanceof ConstantPathAndWriteNode) {
return buildConstantPathAndWrite((ConstantPathAndWriteNode) node);
} else if (node instanceof ConstantPathOperatorWriteNode) {
return buildConstantPathOperatorWrite((ConstantPathOperatorWriteNode) node);
} else if (node instanceof ConstantPathOrWriteNode) {
return buildConstantPathOrWrite((ConstantPathOrWriteNode) node);
} else if (node instanceof ConstantPathOrWriteNode) {
return buildConstantOrWritePath((ConstantPathOrWriteNode) node);
} else if (node instanceof ConstantPathWriteNode) {
return buildConstantWritePath((ConstantPathWriteNode) node);
// ConstantPathTargetNode processed in multiple assignment
} else if (node instanceof ConstantReadNode) {
return buildConstantRead((ConstantReadNode) node);
} else if (node instanceof ConstantWriteNode) {
return buildConstantWrite((ConstantWriteNode) node);
} else if (node instanceof DefNode) {
return buildDef((DefNode) node);
} else if (node instanceof DefinedNode) {
return buildDefined((DefinedNode) node);
} else if (node instanceof ElseNode) {
return buildElse((ElseNode) node);
} else if (node instanceof EmbeddedVariableNode) {
return build(((EmbeddedVariableNode) node).variable);
// EmbeddedStatementsNode handle in interpolated processing
// MISSING: EnsureNode ???? begin will process stuff (and possibly ensure but it is unclear)
} else if (node instanceof FalseNode) {
return fals();
} else if (node instanceof FloatNode) { // MISSING: FindPatternNode
return buildFloat((FloatNode) node);
} else if (node instanceof FlipFlopNode) {
return buildFlipFlop((FlipFlopNode) node);
} else if (node instanceof ForNode) {
return buildFor((ForNode) node);
// ForwardingArgumentsNode, ForwardingParametersNode process by def and call sides respectively
} else if (node instanceof ForwardingSuperNode) {
return buildForwardingSuper(result, (ForwardingSuperNode) node);
} else if (node instanceof GlobalVariableAndWriteNode) {
return buildGlobalVariableAndWrite((GlobalVariableAndWriteNode) node);
} else if (node instanceof GlobalVariableOperatorWriteNode) {
return buildGlobalVariableOperatorWrite((GlobalVariableOperatorWriteNode) node);
} else if (node instanceof GlobalVariableOrWriteNode) {
return buildGlobalVariableOrWrite((GlobalVariableOrWriteNode) node);
} else if (node instanceof GlobalVariableReadNode) {
return buildGlobalVariableRead(result, (GlobalVariableReadNode) node);
// GlobalVariableTargetNode processed by muliple assignment
} else if (node instanceof GlobalVariableWriteNode) {
return buildGlobalVariableWrite((GlobalVariableWriteNode) node);
} else if (node instanceof HashNode) {
return buildHash(((HashNode) node).elements, containsVariableAssignment(node));
} else if (node instanceof IfNode) {
return buildIf(result, (IfNode) node);
} else if (node instanceof ImaginaryNode) {
return buildImaginary((ImaginaryNode) node);
} else if (node instanceof ImplicitNode) {
// Making a huge assumption the implicit node is what we always want for execution?
return build(((ImplicitNode) node).value);
} else if (node instanceof IndexAndWriteNode) {
return buildIndexAndWrite((IndexAndWriteNode) node);
} else if (node instanceof IndexOrWriteNode) {
return buildIndexOrWrite((IndexOrWriteNode) node);
} else if (node instanceof IndexOperatorWriteNode) {
return buildIndexOperatorWrite((IndexOperatorWriteNode) node);
} else if (node instanceof InstanceVariableAndWriteNode) {
return buildInstanceVariableAndWrite((InstanceVariableAndWriteNode) node);
} else if (node instanceof InstanceVariableOperatorWriteNode) {
return buildInstanceVariableOperatorWrite((InstanceVariableOperatorWriteNode) node);
} else if (node instanceof InstanceVariableOrWriteNode) {
return buildInstanceVariableOrWrite((InstanceVariableOrWriteNode) node);
} else if (node instanceof InstanceVariableReadNode) {
return buildInstanceVariableRead((InstanceVariableReadNode) node);
// InstanceVariableTargetNode processed by multiple assignment
} else if (node instanceof InstanceVariableWriteNode) {
return buildInstanceVariableWrite((InstanceVariableWriteNode) node);
} else if (node instanceof IntegerNode) {
return buildInteger((IntegerNode) node);
} else if (node instanceof InterpolatedMatchLastLineNode) {
return buildInterpolatedMatchLastLine(result, (InterpolatedMatchLastLineNode) node);
} else if (node instanceof InterpolatedRegularExpressionNode) {
return buildInterpolatedRegularExpression(result, (InterpolatedRegularExpressionNode) node);
} else if (node instanceof InterpolatedStringNode) {
return buildInterpolatedString(result, (InterpolatedStringNode) node);
} else if (node instanceof InterpolatedSymbolNode) {
return buildInterpolatedSymbol(result, (InterpolatedSymbolNode) node);
} else if (node instanceof InterpolatedXStringNode) {
return buildInterpolatedXString(result, (InterpolatedXStringNode) node);
} else if (node instanceof KeywordHashNode) {
return buildKeywordHash((KeywordHashNode) node, new int[1]); // FIXME: we don't care about flags but this is odd (seems to only be for array syntax with kwrest?).
// KeywordParameterNode, KeywordRestParameterNode processed by call
} else if (node instanceof LambdaNode) {
return buildLambda((LambdaNode) node);
} else if (node instanceof LocalVariableAndWriteNode) {
return buildLocalAndVariableWrite((LocalVariableAndWriteNode) node);
} else if (node instanceof LocalVariableOperatorWriteNode) {
return buildLocalVariableOperatorWrite((LocalVariableOperatorWriteNode) node);
} else if (node instanceof LocalVariableOrWriteNode) {
return buildLocalOrVariableWrite((LocalVariableOrWriteNode) node);
} else if (node instanceof LocalVariableReadNode) {
return buildLocalVariableRead((LocalVariableReadNode) node);
// LocalVariableTargetNode processed by multiple assignment
} else if (node instanceof LocalVariableWriteNode) {
return buildLocalVariableWrite((LocalVariableWriteNode) node);
} else if (node instanceof MatchLastLineNode) {
return buildMatchLastLine(result, (MatchLastLineNode) node);
} else if (node instanceof MatchPredicateNode) {
return buildMatchPredicate((MatchPredicateNode) node);
} else if (node instanceof MatchRequiredNode) {
return buildMatchRequired((MatchRequiredNode) node);
} else if (node instanceof MatchWriteNode) {
return buildMatchWrite(result, (MatchWriteNode) node);
} else if (node instanceof MissingNode) {
return buildMissing((MissingNode) node);
} else if (node instanceof ModuleNode) {
return buildModule((ModuleNode) node);
} else if (node instanceof MultiWriteNode) {
return buildMultiWriteNode((MultiWriteNode) node);
} else if (node instanceof NextNode) {
return buildNext((NextNode) node);
} else if (node instanceof NilNode) {
return nil();
// NoKeywordsParameterNode processed by def // MISSING: NoKeywordsParameterNode
} else if (node instanceof NumberedReferenceReadNode) {
return buildNumberedReferenceRead((NumberedReferenceReadNode) node);
// OptionalParameterNode processed by def
} else if (node instanceof OrNode) {
return buildOr((OrNode) node);
// ParametersNode processed by def
} else if (node instanceof ParenthesesNode) {
return build(((ParenthesesNode) node).body);
} else if (node instanceof PinnedExpressionNode) {
return build(((PinnedExpressionNode) node).expression);
} else if (node instanceof PinnedVariableNode) {
return build(((PinnedVariableNode) node).variable);
} else if (node instanceof PostExecutionNode) {
return buildPostExecution((PostExecutionNode) node);
} else if (node instanceof PreExecutionNode) {
return buildPreExecution((PreExecutionNode) node);
} else if (node instanceof ProgramNode) {
return buildProgram((ProgramNode) node);
} else if (node instanceof RangeNode) {
return buildRange((RangeNode) node);
} else if (node instanceof RationalNode) {
return buildRational((RationalNode) node);
} else if (node instanceof RedoNode) {
return buildRedo((RedoNode) node);
} else if (node instanceof RegularExpressionNode) {
return buildRegularExpression((RegularExpressionNode) node);
// RequiredDestructuredParamterNode, RequiredParameterNode processed by def
} else if (node instanceof RescueModifierNode) {
return buildRescueModifier((RescueModifierNode) node);
// RescueNode handled by begin
// RestParameterNode handled by def
} else if (node instanceof RetryNode) {
return buildRetry((RetryNode) node);
} else if (node instanceof ReturnNode) {
return buildReturn((ReturnNode) node);
} else if (node instanceof SelfNode) {
return buildSelf();
} else if (node instanceof SingletonClassNode) {
return buildSingletonClass((SingletonClassNode) node);
} else if (node instanceof SourceEncodingNode) {
return buildSourceEncoding();
} else if (node instanceof SourceFileNode) {
return buildSourceFile();
} else if (node instanceof SourceLineNode) {
return buildSourceLine(node);
} else if (node instanceof SplatNode) {
return buildSplat((SplatNode) node);
} else if (node instanceof StatementsNode) {
return buildStatements((StatementsNode) node);
} else if (node instanceof StringNode) {
return buildString((StringNode) node);
} else if (node instanceof SuperNode) {
return buildSuper(result, (SuperNode) node);
} else if (node instanceof SymbolNode) {
return buildSymbol((SymbolNode) node);
} else if (node instanceof TrueNode) {
return tru();
} else if (node instanceof UndefNode) {
return buildUndef((UndefNode) node);
} else if (node instanceof UnlessNode) {
return buildUnless(result, (UnlessNode) node);
} else if (node instanceof UntilNode) {
return buildUntil((UntilNode) node);
// WhenNode processed by case
} else if (node instanceof WhileNode) {
return buildWhile((WhileNode) node);
} else if (node instanceof XStringNode) {
return buildXString(result, (XStringNode) node);
} else if (node instanceof YieldNode) {
return buildYield(result, (YieldNode) node);
} else {
throw new RuntimeException("Unhandled Node type: " + node);
}
}
private Operand buildCallOperatorWrite(CallOperatorWriteNode node) {
return buildOpAsgn(node.receiver, node.value, node.read_name, node.write_name, node.binary_operator, node.isSafeNavigation());
}
private Operand buildImaginary(ImaginaryNode node) {
return new Complex((ImmutableLiteral) build(node.numeric));
}
private Operand buildAliasGlobalVariable(AliasGlobalVariableNode node) {
return buildVAlias(globalVariableName(node.new_name), globalVariableName(node.old_name));
}
private Operand buildAliasMethod(AliasMethodNode node) {
return buildAlias(build(node.new_name), build(node.old_name));
}
private Operand buildAnd(AndNode node) {
return buildAnd(build(node.left), () -> build(node.right), binaryType(node.left));
}
private Operand[] buildArguments(ArgumentsNode node) {
return node == null ? Operand.EMPTY_ARRAY : buildNodeList(node.arguments);
}
private Operand buildAssocSplat(Variable result, AssocSplatNode node) {
return build(result, node.value);
}
private Operand[] buildNodeList(Node[] list) {
if (list == null || list.length == 0) return Operand.EMPTY_ARRAY;
Operand[] args = new Operand[list.length];
for (int i = 0; i < list.length; i++) {
args[i] = build(list[i]);
}
return args;
}
private Operand buildArgumentsAsArgument(ArgumentsNode node) {
Operand[] args = buildArguments(node);
return args.length == 0 ? nil() : args.length == 1 ? args[0] : new Array(args);
}
private Operand buildArray(ArrayNode node) {
Node[] children = node.elements;
Operand[] elts = new Operand[children.length];
Variable result = temp();
int splatIndex = -1;
Operand keywordRestSplat = null;
for (int i = 0; i < children.length; i++) {
Node child = children[i];
if (child instanceof SplatNode) {
int length = i - splatIndex - 1;
if (length == 0) {
// FIXME: This is wasteful to force this all through argscat+empty array
if (splatIndex == -1) copy(result, new Array());
addResultInstr(new BuildCompoundArrayInstr(result, result, build(((SplatNode) child).expression), false, false));
} else {
Operand[] lhs = new Operand[length];
System.arraycopy(elts, splatIndex + 1, lhs, 0, length);
// no actual splat until now.
if (splatIndex == -1) {
copy(result, new Array(lhs));
} else {
addResultInstr(new BuildCompoundArrayInstr(result, result, new Array(lhs), false, false));
}
addResultInstr(new BuildCompoundArrayInstr(result, result, build(((SplatNode) child).expression), false, false));
}
splatIndex = i;
} else if (child instanceof KeywordHashNode) {
keywordRestSplat = build(child);
elts[i] = keywordRestSplat;
} else {
elts[i] = build(child);
}
}
if (keywordRestSplat != null) {
Variable test = addResultInstr(new RuntimeHelperCall(temp(), IS_HASH_EMPTY, new Operand[]{ keywordRestSplat }));
if_else(test, tru(),
() -> copy(result, new Array(removeArg(elts))),
() -> copy(result, new Array(elts)));
return result;
}
// FIXME: Can we just return the operand only in this case.
// No splats present. Just make a simple array Operand.
if (splatIndex == -1) return copy(result, new Array(elts));
int length = children.length - splatIndex - 1;
if (length > 0) {
Operand[] rhs = new Operand[length];
System.arraycopy(elts, splatIndex + 1, rhs, 0, length);
addResultInstr(new BuildCompoundArrayInstr(result, result, new Array(rhs), false, false));
}
return result;
}
// This method is called to build assignments for a multiple-assignment instruction
protected void buildAssignment(Node node, Operand rhsVal) {
if (node == null) return; // case of 'a, = something'
if (node instanceof CallTargetNode) {
buildAttrAssignAssignment(((CallTargetNode) node).receiver, ((CallTargetNode) node).name, Node.EMPTY_ARRAY, rhsVal);
} else if (node instanceof IndexTargetNode) {
Node[] arguments = ((IndexTargetNode) node).arguments == null ? Node.EMPTY_ARRAY : ((IndexTargetNode) node).arguments.arguments;
buildAttrAssignAssignment(((IndexTargetNode) node).receiver, symbol("[]="), arguments, rhsVal);
} else if (node instanceof ClassVariableTargetNode) {
addInstr(new PutClassVariableInstr(classVarDefinitionContainer(), ((ClassVariableTargetNode) node).name, rhsVal));
} else if (node instanceof ConstantPathTargetNode) {
Operand parent = buildModuleParent(((ConstantPathTargetNode) node).parent);
RubySymbol name = ((ConstantPathTargetNode) node).name;
addInstr(new PutConstInstr(parent, name, rhsVal));
} else if (node instanceof ConstantTargetNode) {
addInstr(new PutConstInstr(getCurrentModuleVariable(), ((ConstantTargetNode) node).name, rhsVal));
} else if (node instanceof LocalVariableTargetNode) {
LocalVariableTargetNode variable = (LocalVariableTargetNode) node;
copy(getLocalVariable(variable.name, variable.depth), rhsVal);
} else if (node instanceof GlobalVariableTargetNode) {
addInstr(new PutGlobalVarInstr(((GlobalVariableTargetNode) node).name, rhsVal));
} else if (node instanceof InstanceVariableTargetNode) {
addInstr(new PutFieldInstr(buildSelf(), ((InstanceVariableTargetNode) node).name, rhsVal));
} else if (node instanceof MultiTargetNode) {
Variable rhs = addResultInstr(new ToAryInstr(temp(), rhsVal));
buildMultiAssignment(((MultiTargetNode) node).lefts, ((MultiTargetNode) node).rest, ((MultiTargetNode) node).rights, rhs);
} else if (node instanceof MultiWriteNode) { // FIXME: Is this possible with Multitarget now existing?
Variable rhs = addResultInstr(new ToAryInstr(temp(), rhsVal));
buildMultiAssignment(((MultiWriteNode) node).lefts, ((MultiWriteNode) node).rest, ((MultiWriteNode) node).rights, rhs);
} else if (node instanceof RequiredParameterNode) {
RequiredParameterNode variable = (RequiredParameterNode) node;
copy(getLocalVariable(variable.name, 0), rhsVal);
} else if (node instanceof SplatNode) { // FIXME: Audit this...is there really a splat here and it has phatom expression field?
buildSplat(rhsVal);
} else {
throw notCompilable("Can't build assignment node", node);
}
}
private Operand buildModuleParent(Node parent) {
return parent == null ? getCurrentModuleVariable() : build(parent);
}
@Override
protected Operand[] buildAttrAssignCallArgs(Node argsNode, Operand[] rhs, boolean containsAssignment) {
Operand[] args = buildCallArgs(argsNode, new int[] { 0 });
rhs[0] = args[args.length - 1];
return args;
}
public Operand buildAttrAssignAssignment(Node receiver, RubySymbol name, Node[] arguments, Operand value) {
Operand obj = build(receiver);
int[] flags = new int[] { 0 };
Operand[] args = buildCallArgsArray(arguments, flags);
args = addArg(args, value);
addInstr(AttrAssignInstr.create(scope, obj, name, args, flags[0], scope.maybeUsingRefinements()));
return value;
}
// FIXME(feature): optimization simplifying this from other globals
private Operand buildBackReferenceRead(Variable result, BackReferenceReadNode node) {
return buildGlobalVar(result, node.name);
}
private Operand buildBreak(BreakNode node) {
return buildBreak(() -> node.arguments == null ? nil() : buildYieldArgs(node.arguments.arguments, new int[1]), getLine(node));
}
private Operand buildBegin(BeginNode node) {
if (node.rescue_clause != null) {
RescueNode rescue = node.rescue_clause;
Node ensureBody = node.ensure_clause != null ? node.ensure_clause.statements : null;
return buildEnsureInternal(node.statements, node.else_clause, rescue.exceptions, rescue.statements,
rescue.consequent, false, ensureBody, true, rescue.reference);
} else if (node.ensure_clause != null) {
EnsureNode ensure = node.ensure_clause;
return buildEnsureInternal(node.statements, null, null, null, null, false, ensure.statements, false, null);
}
return build(node.statements);
}
private Operand buildBlock(BlockNode node) {
StaticScope staticScope = createStaticScopeFrom(node.locals, StaticScope.Type.BLOCK);
Signature signature = calculateSignature(node.parameters);
staticScope.setSignature(signature);
return buildIter(node.parameters, node.body, staticScope, signature, getLine(node), getEndLine(node));
}
protected Variable receiveBlockArg(Variable v, Operand argsArray, int argIndex, boolean isSplat) {
if (argsArray != null) {
// We are in a nested receive situation -- when we are not at the root of a masgn tree
// Ex: We are trying to receive (b,c) in this example: "|a, (b,c), d| = ..."
if (isSplat) addInstr(new RestArgMultipleAsgnInstr(v, argsArray, argIndex));
else addInstr(new ReqdArgMultipleAsgnInstr(v, argsArray, argIndex));
} else {
// argsArray can be null when the first node in the args-node-ast is a multiple-assignment
// For example, for-nodes
// FIXME: We can have keywords here but this is more complicated to get here
Variable keywords = copy(UndefinedValue.UNDEFINED);
addInstr(isSplat ? new ReceiveRestArgInstr(v, keywords, argIndex, argIndex) : new ReceivePreReqdArgInstr(v, keywords, argIndex));
}
return v;
}
@Override
protected void receiveForArgs(Node node) {
Variable keywords = copy(temp(), UndefinedValue.UNDEFINED);
// FIXME: I think this should rip out receivePre and stop sharingh with method defs
// FIXME: pattern of use seems to be recv pre then assign value so this may be done with less if (or at least through separate methods between value and receiving the value from args).
if (node instanceof MultiTargetNode) { // for loops
buildBlockArgsAssignment(node, null, 0, false);
} else if (node instanceof ClassVariableTargetNode || node instanceof LocalVariableTargetNode ||
node instanceof InstanceVariableTargetNode || node instanceof ConstantTargetNode) {
receivePreArg(node, keywords, 0);
} else {
throw notCompilable("missing arg processing for `for`", node);
}
}
private ArgumentDescriptor[] parametersToArgumentDescriptors(NumberedParametersNode node) {
ArgumentDescriptor[] descriptors = new ArgumentDescriptor[node.maximum];
for (int i = 0; i < node.maximum; i++) {
descriptors[i] = new ArgumentDescriptor(ArgumentType.req, symbol("_" + (i + 1)));
}
return descriptors;
}
public void receiveBlockArgs(Node node) {
if (node == null) return;
if (node instanceof NumberedParametersNode) {
NumberedParametersNode params = (NumberedParametersNode) node;
((IRClosure) scope).setArgumentDescriptors(parametersToArgumentDescriptors((NumberedParametersNode) node));
Variable keywords = addResultInstr(new ReceiveKeywordsInstr(temp(), true, true));
for (int i = 0; i < params.maximum; i++) {
RubySymbol name = symbol("_" + (i + 1));
addInstr(new ReceivePreReqdArgInstr(argumentResult(name), keywords, i));
}
addInstr(new CheckArityInstr(params.maximum, 0, false, params.maximum, keywords));
} else {
// FIXME: Missing locals? Not sure how we handle those but I would have thought with a scope?
buildParameters(((BlockParametersNode) node).parameters);
((IRClosure) scope).setArgumentDescriptors(createArgumentDescriptor());
}
}
private void buildBlockArgsAssignment(Node node, Operand argsArray, int argIndex, boolean isSplat) {
if (node instanceof CallNode) { // attribute assignment: a[0], b = 1, 2
buildAttrAssignAssignment(((CallNode) node).receiver, ((CallNode) node).name, ((CallNode) node).arguments.arguments,
receiveBlockArg(temp(), argsArray, argIndex, isSplat));
} else if (node instanceof LocalVariableTargetNode) {
LocalVariableTargetNode lvar = (LocalVariableTargetNode) node;
receiveBlockArg(getLocalVariable(lvar.name, lvar.depth), argsArray, argIndex, isSplat);
} else if (node instanceof ClassVariableTargetNode) {
addInstr(new PutClassVariableInstr(classVarDefinitionContainer(), ((ClassVariableTargetNode) node).name,
receiveBlockArg(temp(), argsArray, argIndex, isSplat)));
} else if (node instanceof ConstantTargetNode) {
putConstant(((ConstantTargetNode) node).name, receiveBlockArg(temp(), argsArray, argIndex, isSplat));
} else if (node instanceof GlobalVariableTargetNode) {
addInstr(new PutGlobalVarInstr(((GlobalVariableTargetNode) node).name,
receiveBlockArg(temp(), argsArray, argIndex, isSplat)));
} else if (node instanceof InstanceVariableTargetNode) {
addInstr(new PutFieldInstr(buildSelf(), ((InstanceVariableTargetNode) node).name,
receiveBlockArg(temp(), argsArray, argIndex, isSplat)));
} else if (node instanceof MultiTargetNode) {
MultiTargetNode multi = (MultiTargetNode) node;
for (int i = 0; i < multi.lefts.length; i++) {
buildBlockArgsAssignment(multi.lefts[i], null, i, false);
}
int postIndex = multi.lefts.length;
if (multi.rest != null) {
buildBlockArgsAssignment(multi.rest, null, postIndex, true);
postIndex++;
}
for (int i = 0; i < multi.rights.length; i++) {
buildBlockArgsAssignment(multi.rights[i], null, postIndex + i, false);
}
} else if (node instanceof SplatNode) {
// FIXME: we don't work in legacy either?
} else if (node instanceof ImplicitRestNode) {
} else {
throw notCompilable("Can't build assignment node", node);
}
}
private Operand buildBlockArgument(BlockArgumentNode node) {
if (node.expression instanceof SymbolNode && !scope.maybeUsingRefinements()) {
return new SymbolProc(symbol(((SymbolNode) node.expression)));
} else if (node.expression == null) {
return getYieldClosureVariable();
}
return build(node.expression);
}
private Operand buildCall(Variable resultArg, CallNode node, RubySymbol name) {
return buildCall(resultArg, node, name, null, null);
}
protected Operand buildLazyWithOrder(CallNode node, Label lazyLabel, Label endLabel, boolean preserveOrder) {
Operand value = buildCall(null, node, node.name, lazyLabel, endLabel);
// FIXME: missing !(value instanceof ImmutableLiteral) which will force more copy instr
// We need to preserve order in cases (like in presence of assignments) except that immutable
// literals can never change value so we can still emit these out of order.
return preserveOrder ? copy(value) : value;
}
// FIXME: This would be nice to combine in some form with AST side but it requires some pre-processing since prism merged all call types into a single node.
// We do name processing outside of this rather than from the node to support stripping '=' off of opelasgns
private Operand buildCall(Variable resultArg, CallNode node, RubySymbol name, Label lazyLabel, Label endLabel) {
Variable result = resultArg == null ? temp() : resultArg;
if (node.isVariableCall()) return _call(result, VARIABLE, buildSelf(), name);
CallType callType = determineCallType(node.receiver);
String id = name.idString();
if (node.isAttributeWrite()) return buildAttrAssign(result, node.receiver, node.arguments, node.block, node.name, node.isSafeNavigation(), containsVariableAssignment(node));
if (callType != CallType.FUNCTIONAL && Options.IR_STRING_FREEZE.load()) {
// Frozen string optimization: check for "string".freeze
if (node.receiver instanceof StringNode && (id.equals("freeze") || id.equals("-@"))) {
return new FrozenString(bytelistFrom((StringNode) node.receiver), CR_UNKNOWN, scope.getFile(), getLine(node.receiver));
}
}
if (node.isSafeNavigation()) {
if (lazyLabel == null) {
lazyLabel = getNewLabel();
endLabel = getNewLabel();
}
}
// The receiver has to be built *before* call arguments are built
// to preserve expected code execution order
// FIXME: this always builds with order (lacking containsVariableAssignment() in prism).
Operand receiver;
if (callType == CallType.FUNCTIONAL) {
receiver = buildSelf();
} else if (node.receiver instanceof CallNode && ((CallNode) node.receiver).isSafeNavigation()) {
receiver = buildLazyWithOrder((CallNode) node.receiver, lazyLabel, endLabel, true);
} else {
receiver = buildWithOrder(node.receiver, true);
}
if (node.isSafeNavigation()) addInstr(new BNilInstr(lazyLabel, receiver));
// FIXME: Missing arrayderef opti logic
createCall(result, receiver, callType, name, node.arguments, node.block, getLine(node), node.hasNewLineFlag());
if (node.isSafeNavigation()) {
addInstr(new JumpInstr(endLabel));
addInstr(new LabelInstr(lazyLabel));
addInstr(new CopyInstr(result, nil()));
addInstr(new LabelInstr(endLabel));
}
return result;
}
private Operand buildCallAndWrite(CallAndWriteNode node) {
return buildOpAsgn(node.receiver, node.value, node.read_name, node.write_name, symbol(AMPERSAND_AMPERSAND), node.isSafeNavigation());
}
@Override
protected Operand[] buildCallArgs(Node args, int[] flags) {
return buildCallArgsArray(((ArgumentsNode) args).arguments, flags);
}
protected Operand[] buildCallArgsArray(Node[] children, int[] flags) {
int numberOfArgs = children.length;
// FIXME: hack
if (numberOfArgs > 0 && children[numberOfArgs - 1] instanceof BlockArgumentNode) {
Node[] temp = children;
numberOfArgs--;
children = new Node[numberOfArgs];
System.arraycopy(temp, 0, children, 0, numberOfArgs);
}
Operand[] builtArgs = new Operand[numberOfArgs];
boolean hasAssignments = containsVariableAssignment(children);
for (int i = 0; i < numberOfArgs; i++) {
Node child = children[i];
if (child instanceof SplatNode) {
flags[0] |= CALL_SPLATS;
builtArgs[i] = new Splat(addResultInstr(new BuildSplatInstr(temp(), build(((SplatNode) child).expression), true)));
} else if (child instanceof KeywordHashNode && i == numberOfArgs - 1) {
builtArgs[i] = buildCallKeywordArguments((KeywordHashNode) children[i], flags); // FIXME: here and possibly AST make isKeywordsHash() method.
} else if (child instanceof ForwardingArgumentsNode) {
Operand rest = buildSplat(getLocalVariable(symbol(FWD_REST), scope.getStaticScope().isDefined("*")));
Operand kwRest = getLocalVariable(symbol(FWD_KWREST), scope.getStaticScope().isDefined("**"));
Variable check = addResultInstr(new RuntimeHelperCall(temp(), HASH_CHECK, new Operand[] { kwRest }));
Variable ary = addResultInstr(new BuildCompoundArrayInstr(temp(), rest, check, true, true));
builtArgs[i] = new Splat(buildSplat(ary));
flags[0] |= CALL_KEYWORD | CALL_KEYWORD_REST | CALL_SPLATS;
} else {
builtArgs[i] = buildWithOrder(children[i], hasAssignments);
}
}
return builtArgs;
}
protected Operand buildCallKeywordArguments(KeywordHashNode node, int[] flags) {
flags[0] |= CALL_KEYWORD;
if (hasOnlyRestKwargs(node.elements)) return buildRestKeywordArgs(node, flags);
return buildHash(node.elements, containsVariableAssignment(node.elements));
}
private Operand buildCallOrWrite(CallOrWriteNode node) {
return buildOpAsgn(node.receiver, node.value, node.read_name, node.write_name, symbol(OR_OR), node.isSafeNavigation());
}
private Operand buildCase(CaseNode node) {
return buildCase(node.predicate, node.conditions, node.consequent);
}
private Operand buildCaseMatch(CaseMatchNode node) {
return buildPatternCase(node.predicate, node.conditions, node.consequent);
}
private Operand buildClass(ClassNode node) {
return buildClass(determineBaseName(node.constant_path), node.superclass, node.constant_path,
node.body, createStaticScopeFrom(node.locals, StaticScope.Type.LOCAL), getLine(node), getEndLine(node));
}
private Operand buildClassVariableOperatorWrite(ClassVariableOperatorWriteNode node) {
Operand lhs = buildClassVar(temp(), node.name);
Operand rhs = build(node.value);
Variable value = call(temp(), lhs, node.binary_operator, rhs);
addInstr(new PutClassVariableInstr(classVarDefinitionContainer(), node.name, value));
return value;
}
private Operand buildInstanceVariableOperatorWrite(InstanceVariableOperatorWriteNode node) {
Operand lhs = buildInstVar(node.name);
Operand rhs = build(node.value);
Variable value = call(temp(), lhs, node.binary_operator, rhs);
addInstr(new PutFieldInstr(buildSelf(), node.name, value));
return value;
}
private Operand buildLocalVariableOperatorWrite(LocalVariableOperatorWriteNode node) {
int depth = staticScope.isDefined(node.name.idString()) >> 16;
Variable lhs = getLocalVariable(node.name, depth);
Operand rhs = build(node.value);
Variable value = call(lhs, lhs, node.binary_operator, rhs);
return value;
}
private Operand buildClassAndVariableWrite(ClassVariableAndWriteNode node) {
return buildOpAsgnAnd(() -> addResultInstr(new GetClassVariableInstr(temp(), classVarDefinitionContainer(), node.name)),
() -> (buildClassVarAsgn(node.name, node.value)));
}
private Operand buildClassOrVariableWrite(ClassVariableOrWriteNode node) {
return buildOpAsgnOrWithDefined(node,
(result) -> addInstr(new GetClassVariableInstr((Variable) result, classVarDefinitionContainer(), node.name)),
() -> (buildClassVarAsgn(node.name, node.value)));
}
private Operand buildClassVariableRead(Variable result, ClassVariableReadNode node) {
return buildClassVar(result, node.name);
}
private Operand buildClassVariableWrite(ClassVariableWriteNode node) {
return buildClassVarAsgn(node.name, node.value);
}
@Override
protected Operand buildColon2ForConstAsgnDeclNode(Node lhs, Variable valueResult, boolean constMissing) {
Variable leftModule = temp();
ConstantPathNode colon2Node = (ConstantPathNode) lhs;
RubySymbol name = symbol(determineBaseName(colon2Node));
Operand leftValue;
if (colon2Node.parent == null) {
leftValue = getManager().getObjectClass(); // ::Foo
} else {
leftValue = build(colon2Node.parent);
}
// FIXME: ::Foo should be able to eliminate this variable reference
copy(leftModule, leftValue);
addInstr(new SearchModuleForConstInstr(valueResult, leftModule, name, false, constMissing));
return leftModule;
}
private Operand buildConstantAndWrite(ConstantAndWriteNode node) {
return buildOpAsgnAnd(() -> addResultInstr(new SearchConstInstr(temp(), CurrentScope.INSTANCE, node.name, false)),
() -> (putConstant(node.name, build(node.value))));
}
private Operand buildConstantOperatorWrite(ConstantOperatorWriteNode node) {
Operand lhs = searchConst(temp(), node.name);
Operand rhs = build(node.value);
Variable value = call(temp(), lhs, node.binary_operator, rhs);
putConstant(buildSelf(), node.name, value);
return value;
}
private Operand buildConstantOrWrite(ConstantOrWriteNode node) {
return buildOpAsgnOrWithDefined(node,
(result) -> addInstr(new SearchConstInstr((Variable) result, CurrentScope.INSTANCE, node.name, false)),
() -> (putConstant(node.name, build(node.value))));
}
private Operand buildConstantOrWritePath(ConstantPathOrWriteNode node) {
// FIXME: unify with AST
RubySymbol name = ((ConstantPathNode) node.target).name;
Variable result = temp();
Label falseCheck = getNewLabel();
Label done = getNewLabel();
Label assign = getNewLabel();
// FIXME: this is semi-duplicated from buildConstantPath since we want out param of module and value returned to result
Operand module = node.target.parent == null ? getManager().getObjectClass() : build(node.target.parent);
searchModuleForConstNoFrills(result, module, name);
addInstr(BNEInstr.create(falseCheck, result, UndefinedValue.UNDEFINED));
addInstr(new JumpInstr(assign));
addInstr(new LabelInstr(falseCheck));
addInstr(BNEInstr.create(done, result, fals()));
addInstr(new LabelInstr(assign));
Operand rhsValue = build(node.value);
copy(result, rhsValue);
addInstr(new PutConstInstr(module, name, rhsValue));
addInstr(new LabelInstr(done));
return result;
}
private Operand buildConstantPath(Variable result, ConstantPathNode node) {
return buildConstantPath(result, node.name, node.parent);
}
private Operand buildConstantPath(Variable result, RubySymbol name, Node parent) {
Operand where = parent == null ? getManager().getObjectClass() : build(parent);
return searchModuleForConst(result, where, name);
}
private Operand buildConstantPathAndWrite(ConstantPathAndWriteNode node) {
return buildOpAsgnConstDeclAnd(node.target, node.value, symbol(determineBaseName(node.target)));
}
private Operand buildConstantPathOperatorWrite(ConstantPathOperatorWriteNode node) {
return buildOpAsgnConstDecl(node.target, node.value, node.binary_operator);
}
private Operand buildConstantPathOrWrite(ConstantPathOrWriteNode node) {
return buildOpAsgnConstDeclOr(node.target, node.value, symbol(determineBaseName(node.target)));
}
private Operand buildConstantRead(ConstantReadNode node) {
return addResultInstr(new SearchConstInstr(temp(), CurrentScope.INSTANCE, node.name, false));
}
private Operand buildConstantWrite(ConstantWriteNode node) {
return putConstant(node.name, build(node.value));
}
private Operand buildConstantWritePath(ConstantPathWriteNode node) {
return buildConstantWritePath(node.target, build(node.value));
}
// Multiple assignments provide the value otherwise it is grabbed from .value on the node.
private Operand buildConstantWritePath(ConstantPathNode path, Operand value) {
return putConstant(buildModuleParent(path.parent), path.name, value);
}
private Operand buildDef(DefNode node) {
// FIXME: due to how lazy methods work we need this set on method before we actually parse the method.
StaticScope staticScope = createStaticScopeFrom(node.locals, StaticScope.Type.LOCAL);
staticScope.setSignature(calculateSignature(node.parameters));
LazyMethodDefinition def = new LazyMethodDefinitionPrism(source, nodeSource, encoding, node);
if (node.receiver == null) {
return buildDefn(defineNewMethod(def, node.name.getBytes(), getLine(node), staticScope, true));
} else {
return buildDefs(node.receiver, defineNewMethod(def, node.name.getBytes(), getLine(node), staticScope, false));
}
}
private Operand buildDefined(DefinedNode node) {
return buildGetDefinition(node.value);
}
private Operand buildElse(ElseNode node) {
return buildStatements(node.statements);
}
private Operand buildFlipFlop(FlipFlopNode node) {
return buildFlip(node.left, node.right, node.isExcludeEnd());
}
// FIXME: Do we need warn or will YARP provide it.
private Operand buildFloat(FloatNode node) {
String number = bytelistFrom(node).toString();
// FIXME: This is very expensive but numeric values is still be decided in Prism.
IRubyObject fl = RubyKernel.new_float(getManager().getRuntime().getCurrentContext(),
getManager().getRuntime().newString(number), false);
return new Float(((RubyFloat) fl).getDoubleValue());
}
private Operand buildFor(ForNode node) {
return buildFor(node.collection, node.index, node.statements, scope.getStaticScope(),
calculateSignatureFor(node.index), getLine(node), getEndLine(node));
}
private Operand buildForwardingSuper(Variable result, ForwardingSuperNode node) {
return buildZSuper(result, node.block);
}
public Operand buildGetArgumentDefinition(final ArgumentsNode node, String type) {
if (node == null) return new MutableString(type);
Operand rv = new FrozenString(type);
boolean failPathReqd = false;
Label failLabel = getNewLabel();
for(Node arg: node.arguments) {
Operand def = buildGetDefinition(arg);
if (def == nil()) { // Optimization!
rv = nil();
break;
} else if (!def.hasKnownValue()) { // Optimization!
failPathReqd = true;
addInstr(createBranch(def, nil(), failLabel));
}
}
return failPathReqd ? buildDefnCheckIfThenPaths(failLabel, rv) : rv;
}
// FIXME: implementation of @@a ||= 1 uses getDefinition to determine it is defined but defined?(@@a ||= 1) is
// always defined as "assignment".
protected Operand buildGetDefinition2(Node node) {
if (node instanceof ClassVariableOrWriteNode) {
return buildClassVarGetDefinition(((ClassVariableOrWriteNode) node).name);
} else if (node instanceof GlobalVariableOrWriteNode) {
return buildGlobalVarGetDefinition(((GlobalVariableOrWriteNode) node).name);
} else if (node instanceof ConstantOrWriteNode) {
return buildConstantGetDefinition(((ConstantOrWriteNode) node).name);
}
return buildGetDefinition(node);
}
@Override
protected Operand buildGetDefinition(Node node) {
if (node == null) return new FrozenString("expression");
if (node instanceof ClassVariableWriteNode ||
node instanceof ClassVariableAndWriteNode ||
node instanceof ClassVariableOperatorWriteNode || node instanceof ClassVariableOrWriteNode ||
node instanceof ConstantAndWriteNode || node instanceof ConstantOrWriteNode ||
node instanceof ConstantPathWriteNode || node instanceof ConstantWriteNode ||
node instanceof InstanceVariableAndWriteNode || node instanceof InstanceVariableOrWriteNode ||
node instanceof InstanceVariableOperatorWriteNode ||
node instanceof LocalVariableWriteNode ||
node instanceof LocalVariableAndWriteNode || node instanceof LocalVariableOrWriteNode ||
node instanceof LocalVariableOperatorWriteNode || node instanceof ConstantOperatorWriteNode ||
node instanceof GlobalVariableOrWriteNode || node instanceof GlobalVariableAndWriteNode ||
node instanceof GlobalVariableWriteNode || node instanceof GlobalVariableOperatorWriteNode ||
node instanceof MultiWriteNode ||
node instanceof InstanceVariableWriteNode || node instanceof IndexAndWriteNode ||
node instanceof IndexOrWriteNode || node instanceof IndexOperatorWriteNode ||
node instanceof CallAndWriteNode || node instanceof CallOrWriteNode ||
node instanceof CallOperatorWriteNode) {
return new FrozenString(DefinedMessage.ASSIGNMENT.getText());
} else if (node instanceof OrNode || node instanceof AndNode ||
node instanceof InterpolatedRegularExpressionNode || node instanceof InterpolatedStringNode) {
return new FrozenString(DefinedMessage.EXPRESSION.getText());
} else if (node instanceof ParenthesesNode) {
if (((ParenthesesNode) node).body instanceof StatementsNode) {
StatementsNode statements = (StatementsNode) ((ParenthesesNode) node).body;
switch (statements.body.length) {
case 0: return nil();
case 1: return buildGetDefinition(statements.body[0]);
}
}
return new FrozenString(DefinedMessage.EXPRESSION.getText());
} else if (node instanceof FalseNode) {
return new FrozenString(DefinedMessage.FALSE.getText());
} else if (node instanceof LocalVariableReadNode) {
return new FrozenString(DefinedMessage.LOCAL_VARIABLE.getText());
} else if (node instanceof MatchPredicateNode || node instanceof MatchRequiredNode) {
return new FrozenString(DefinedMessage.METHOD.getText());
} else if (node instanceof NilNode) {
return new FrozenString(DefinedMessage.NIL.getText());
} else if (node instanceof SelfNode) {
return new FrozenString(DefinedMessage.SELF.getText());
} else if (node instanceof TrueNode) {
return new FrozenString(DefinedMessage.TRUE.getText());
} else if (node instanceof StatementsNode) {
Node[] array = ((StatementsNode) node).body;
Label undefLabel = getNewLabel();
Label doneLabel = getNewLabel();
Variable tmpVar = temp();
for (Node elt : array) {
Operand result = buildGetDefinition(elt);
addInstr(createBranch(result, nil(), undefLabel));
}
addInstr(new CopyInstr(tmpVar, new FrozenString(DefinedMessage.EXPRESSION.getText())));
addInstr(new JumpInstr(doneLabel));
addInstr(new LabelInstr(undefLabel));
addInstr(new CopyInstr(tmpVar, nil()));
addInstr(new LabelInstr(doneLabel));
return tmpVar;
} else if (node instanceof GlobalVariableReadNode) {
return buildGlobalVarGetDefinition(((GlobalVariableReadNode) node).name);
} else if (node instanceof BackReferenceReadNode) {
return addResultInstr(
new RuntimeHelperCall(
temp(),
IS_DEFINED_BACKREF,
new Operand[] {new FrozenString(DefinedMessage.GLOBAL_VARIABLE.getText())}
)
);
} else if (node instanceof NumberedReferenceReadNode) {
return addResultInstr(new RuntimeHelperCall(
temp(),
IS_DEFINED_NTH_REF,
new Operand[] {
fix(((NumberedReferenceReadNode) node).number),
new FrozenString(DefinedMessage.GLOBAL_VARIABLE.getText())
}
));
} else if (node instanceof InstanceVariableReadNode) {
return buildInstVarGetDefinition(((InstanceVariableReadNode) node).name);
} else if (node instanceof InstanceVariableOrWriteNode) {
return buildInstVarGetDefinition(((InstanceVariableOrWriteNode) node).name);
} else if (node instanceof ClassVariableReadNode) {
return buildClassVarGetDefinition(((ClassVariableReadNode) node).name);
} else if (node instanceof SuperNode) {
Label undefLabel = getNewLabel();
Variable tmpVar = addResultInstr(
new RuntimeHelperCall(
temp(),
IS_DEFINED_SUPER,
new Operand[]{
buildSelf(),
new FrozenString(DefinedMessage.SUPER.getText())
}
)
);
addInstr(createBranch(tmpVar, nil(), undefLabel));
Operand superDefnVal = buildGetArgumentDefinition(((SuperNode) node).arguments, DefinedMessage.SUPER.getText());
return buildDefnCheckIfThenPaths(undefLabel, superDefnVal);
} else if (node instanceof ForwardingSuperNode) {
return addResultInstr(
new RuntimeHelperCall(temp(), IS_DEFINED_SUPER,
new Operand[] { buildSelf(), new FrozenString(DefinedMessage.SUPER.getText()) }));
} else if (node instanceof CallNode) {
CallNode call = (CallNode) node;
if (call.receiver == null && call.arguments == null) { // VCALL
return addResultInstr(
new RuntimeHelperCall(temp(), IS_DEFINED_METHOD,
new Operand[]{ buildSelf(), new FrozenString(call.name), fals(), new FrozenString(DefinedMessage.METHOD.getText()) }));
}
String type = DefinedMessage.METHOD.getText();
if (call.receiver == null) { // FCALL
/* ------------------------------------------------------------------
* Generate IR for:
* r = self/receiver
* mc = r.metaclass
* return mc.methodBound(meth) ? buildGetArgumentDefn(..) : false
* ----------------------------------------------------------------- */
Label undefLabel = getNewLabel();
Variable tmpVar = addResultInstr(
new RuntimeHelperCall(
temp(),
IS_DEFINED_METHOD,
new Operand[]{
buildSelf(),
new Symbol(call.name),
fals(),
new FrozenString(DefinedMessage.METHOD.getText())
}
)
);
addInstr(createBranch(tmpVar, nil(), undefLabel));
Operand argsCheckDefn = buildGetArgumentDefinition(((CallNode) node).arguments, type);
return buildDefnCheckIfThenPaths(undefLabel, argsCheckDefn);
} else { // CALL
// protected main block
CodeBlock protectedCode = new CodeBlock() {
public Operand run() {
final Label undefLabel = getNewLabel();
Operand receiverDefn = buildGetDefinition(call.receiver);
addInstr(createBranch(receiverDefn, nil(), undefLabel));
Variable tmpVar = temp();
addInstr(new RuntimeHelperCall(tmpVar, IS_DEFINED_CALL,
new Operand[]{
build(call.receiver),
new Symbol(call.name),
new FrozenString(DefinedMessage.METHOD.getText())
}));
return buildDefnCheckIfThenPaths(undefLabel, tmpVar);
}
};
// Try verifying definition, and if we get an exception, throw it out, and return nil
return protectCodeWithRescue(protectedCode, () -> nil());
}
} else if (node instanceof YieldNode) {
return buildDefinitionCheck(new BlockGivenInstr(temp(), getYieldClosureVariable()), DefinedMessage.YIELD.getText());
} else if (node instanceof SuperNode) {
// FIXME: Current code missing way to tell zsuper from super
return addResultInstr(
new RuntimeHelperCall(temp(), IS_DEFINED_SUPER,
new Operand[]{buildSelf(), new FrozenString(DefinedMessage.SUPER.getText())}));
} else if (node instanceof ConstantReadNode) {
return buildConstantGetDefinition(((ConstantReadNode) node).name);
} else if (node instanceof ConstantPathNode) {
// SSS FIXME: Is there a reason to do this all with low-level IR?
// Can't this all be folded into a Java method that would be part
// of the runtime library, which then can be used by buildDefinitionCheck method above?
// This runtime library would be used both by the interpreter & the compiled code!
ConstantPathNode path = (ConstantPathNode) node;
final RubySymbol name = path.name;
final Variable errInfo = temp();
// store previous exception for restoration if we rescue something
addInstr(new GetErrorInfoInstr(errInfo));
CodeBlock protectedCode = () -> {
if (path.parent == null) { // colon3
return addResultInstr(
new RuntimeHelperCall(
temp(),
IS_DEFINED_CONSTANT_OR_METHOD,
new Operand[]{
getManager().getObjectClass(),
new FrozenString(name),
new FrozenString(DefinedMessage.CONSTANT.getText()),
new FrozenString(DefinedMessage.METHOD.getText())
}
)
);
}
Label bad = getNewLabel();
Label done = getNewLabel();
Variable result = temp();
Operand test = buildGetDefinition(path.parent);
addInstr(createBranch(test, nil(), bad));
Operand lhs = build(path.parent);
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, nil()));
addInstr(new LabelInstr(done));
return result;
};
// Try verifying definition, and if we get an JumpException exception, process it with the rescue block above
return protectCodeWithRescue(protectedCode, () -> {
addInstr(new RestoreErrorInfoInstr(errInfo)); // ignore and restore (we don't care about error)
return nil();
});
} else if (node instanceof ArrayNode) {
ArrayNode array = (ArrayNode) node;
Label undefLabel = getNewLabel();
Label doneLabel = getNewLabel();
Variable tmpVar = temp();
for (Node elt : array.elements) {
Operand result = buildGetDefinition(elt);
addInstr(createBranch(result, nil(), undefLabel));
}
addInstr(new CopyInstr(tmpVar, new FrozenString(DefinedMessage.EXPRESSION.getText())));
addInstr(new JumpInstr(doneLabel));
addInstr(new LabelInstr(undefLabel));
addInstr(new CopyInstr(tmpVar, nil()));
addInstr(new LabelInstr(doneLabel));
return tmpVar;
}
return new FrozenString("expression");
}
private Operand buildGlobalVariableRead(Variable result, GlobalVariableReadNode node) {
return buildGlobalVar(result, node.name);
}
private Operand buildGlobalVariableOperatorWrite(GlobalVariableOperatorWriteNode node) {
Operand lhs = buildGlobalVar(temp(), node.name);
Operand rhs = build(node.value);
Variable value = call(temp(), lhs, node.binary_operator, rhs);
addInstr(new PutGlobalVarInstr(node.name, value));
return value;
}
private Operand buildGlobalVariableAndWrite(GlobalVariableAndWriteNode node) {
return buildOpAsgnAnd(() -> buildGlobalVar(temp(), node.name),
() -> buildGlobalAsgn(node.name, node.value));
}
private Operand buildGlobalVariableOrWrite(GlobalVariableOrWriteNode node) {
return buildOpAsgnOrWithDefined(node,
(result) -> buildGlobalVar((Variable) result, node.name),
() -> buildGlobalAsgn(node.name, node.value));
}
private Operand buildGlobalVariableWrite(GlobalVariableWriteNode node) {
return buildGlobalAsgn(node.name, node.value);
}
private Operand buildHash(Node[] elements, boolean hasAssignments) {
List> args = new ArrayList<>();
Set
© 2015 - 2025 Weber Informatics LLC | Privacy Policy