in rhino/src/main/java/org/mozilla/javascript/CodeGenerator.java [555:1178]
private void visitExpression(Node node, int contextFlags) {
int type = node.getType();
Node child = node.getFirstChild();
int savedStackDepth = stackDepth;
switch (type) {
case Token.FUNCTION:
{
int fnIndex = node.getExistingIntProp(Node.FUNCTION_PROP);
FunctionNode fn = scriptOrFn.getFunctionNode(fnIndex);
// See comments in visitStatement for Token.FUNCTION case
if (fn.getFunctionType() != FunctionNode.FUNCTION_EXPRESSION
&& fn.getFunctionType() != FunctionNode.ARROW_FUNCTION) {
throw Kit.codeBug();
}
addIndexOp(Icode_CLOSURE_EXPR, fnIndex);
if (fn.isMethodDefinition()) {
addIcode(ICode_FN_STORE_HOME_OBJECT);
}
stackChange(1);
}
break;
case Token.LOCAL_LOAD:
{
int localIndex = getLocalBlockRef(node);
addIndexOp(Token.LOCAL_LOAD, localIndex);
stackChange(1);
}
break;
case Token.COMMA:
{
Node lastChild = node.getLastChild();
while (child != lastChild) {
visitExpression(child, 0);
addIcode(Icode_POP);
stackChange(-1);
child = child.getNext();
}
// Preserve tail context flag if any
visitExpression(child, contextFlags & ECF_TAIL);
}
break;
case Token.USE_STACK:
// Indicates that stack was modified externally,
// like placed catch object
stackChange(1);
break;
case Token.REF_CALL:
case Token.CALL:
case Token.NEW:
{
boolean isOptionalChainingCall =
node.getIntProp(Node.OPTIONAL_CHAINING, 0) == 1;
CompleteOptionalCallJump completeOptionalCallJump = null;
if (type == Token.NEW) {
visitExpression(child, 0);
} else {
completeOptionalCallJump =
generateCallFunAndThis(child, isOptionalChainingCall);
if (completeOptionalCallJump != null) {
resolveForwardGoto(completeOptionalCallJump.putArgsAndDoCallLabel);
}
}
int argCount = 0;
while ((child = child.getNext()) != null) {
visitExpression(child, 0);
++argCount;
}
int callType = node.getIntProp(Node.SPECIALCALL_PROP, Node.NON_SPECIALCALL);
if (type != Token.REF_CALL && callType != Node.NON_SPECIALCALL) {
// embed line number and source filename
addIndexOp(Icode_CALLSPECIAL, argCount);
addUint8(callType);
addUint8(type == Token.NEW ? 1 : 0);
addUint16(lineNumber & 0xFFFF);
} else if (node.getIntProp(Node.SUPER_PROPERTY_ACCESS, 0) == 1) {
addIndexOp(Icode_CALL_ON_SUPER, argCount);
} else {
// Only use the tail call optimization if we're not in a try
// or we're not generating debug info (since the
// optimization will confuse the debugger)
if (type == Token.CALL
&& (contextFlags & ECF_TAIL) != 0
&& !compilerEnv.isGenerateDebugInfo()
&& !itsInTryFlag) {
type = Icode_TAIL_CALL;
}
addIndexOp(type, argCount);
}
// adjust stack
if (type == Token.NEW) {
// new: f, args -> result
stackChange(-argCount);
} else {
// call: f, thisObj, args -> result
// ref_call: f, thisObj, args -> ref
stackChange(-1 - argCount);
}
if (argCount > itsData.itsMaxCalleeArgs) {
itsData.itsMaxCalleeArgs = argCount;
}
if (completeOptionalCallJump != null) {
resolveForwardGoto(completeOptionalCallJump.afterLabel);
}
}
break;
case Token.AND:
case Token.OR:
{
visitExpression(child, 0);
addIcode(Icode_DUP);
stackChange(1);
int afterSecondJumpStart = iCodeTop;
int jump = (type == Token.AND) ? Token.IFNE : Token.IFEQ;
addGotoOp(jump);
stackChange(-1);
addIcode(Icode_POP);
stackChange(-1);
child = child.getNext();
// Preserve tail context flag if any
visitExpression(child, contextFlags & ECF_TAIL);
resolveForwardGoto(afterSecondJumpStart);
}
break;
case Token.HOOK:
{
Node ifThen = child.getNext();
Node ifElse = ifThen.getNext();
visitExpression(child, 0);
int elseJumpStart = iCodeTop;
addGotoOp(Token.IFNE);
stackChange(-1);
// Preserve tail context flag if any
visitExpression(ifThen, contextFlags & ECF_TAIL);
int afterElseJumpStart = iCodeTop;
addGotoOp(Token.GOTO);
resolveForwardGoto(elseJumpStart);
stackDepth = savedStackDepth;
// Preserve tail context flag if any
visitExpression(ifElse, contextFlags & ECF_TAIL);
resolveForwardGoto(afterElseJumpStart);
}
break;
case Token.GETPROP:
case Token.GETPROPNOWARN:
visitExpression(child, 0);
child = child.getNext();
if (node.getIntProp(Node.OPTIONAL_CHAINING, 0) == 1) {
// Jump if null or undefined
addIcode(Icode_DUP);
stackChange(1);
int putUndefinedLabel = iCodeTop;
addGotoOp(Icode.Icode_IF_NULL_UNDEF);
stackChange(-1);
// Access property
addStringOp(type, child.getString());
int afterLabel = iCodeTop;
addGotoOp(Token.GOTO);
// Put undefined
resolveForwardGoto(putUndefinedLabel);
addIcode(Icode_POP);
addStringOp(Token.NAME, "undefined");
resolveForwardGoto(afterLabel);
} else if (node.getIntProp(Node.SUPER_PROPERTY_ACCESS, 0) == 1) {
addStringOp(
type == Token.GETPROP ? Token.GETPROP_SUPER : Token.GETPROPNOWARN_SUPER,
child.getString());
} else {
addStringOp(type, child.getString());
}
break;
case Token.DELPROP:
boolean isName = child.getType() == Token.BINDNAME;
visitExpression(child, 0);
child = child.getNext();
visitExpression(child, 0);
if (node.getIntProp(Node.SUPER_PROPERTY_ACCESS, 0) == 1) {
addIcode(Icode_DELPROP_SUPER);
} else if (isName) {
// special handling for delete name
addIcode(Icode_DELNAME);
} else {
addToken(Token.DELPROP);
}
stackChange(-1);
break;
case Token.GETELEM:
visitExpression(child, 0);
child = child.getNext();
if (node.getIntProp(Node.OPTIONAL_CHAINING, 0) == 1) {
addIcode(Icode_DUP);
stackChange(1);
int putUndefinedLabel = iCodeTop;
addGotoOp(Icode.Icode_IF_NULL_UNDEF);
stackChange(-1);
// Infix op
finishGetElemGeneration(child);
int afterLabel = iCodeTop;
addGotoOp(Token.GOTO);
// Put undefined
resolveForwardGoto(putUndefinedLabel);
addIcode(Icode_POP);
addStringOp(Token.NAME, "undefined");
resolveForwardGoto(afterLabel);
} else if (node.getIntProp(Node.SUPER_PROPERTY_ACCESS, 0) == 1) {
visitExpression(child, 0);
addToken(Token.GETELEM_SUPER);
stackChange(-1);
} else {
finishGetElemGeneration(child);
}
break;
case Token.BITAND:
case Token.BITOR:
case Token.BITXOR:
case Token.LSH:
case Token.RSH:
case Token.URSH:
case Token.ADD:
case Token.SUB:
case Token.MOD:
case Token.DIV:
case Token.MUL:
case Token.EXP:
case Token.EQ:
case Token.NE:
case Token.SHEQ:
case Token.SHNE:
case Token.IN:
case Token.INSTANCEOF:
case Token.LE:
case Token.LT:
case Token.GE:
case Token.GT:
visitExpression(child, 0);
child = child.getNext();
visitExpression(child, 0);
addToken(type);
stackChange(-1);
break;
case Token.POS:
case Token.NEG:
case Token.NOT:
case Token.BITNOT:
case Token.TYPEOF:
case Token.VOID:
visitExpression(child, 0);
if (type == Token.VOID) {
addIcode(Icode_POP);
addIcode(Icode_UNDEF);
} else {
addToken(type);
}
break;
case Token.GET_REF:
case Token.DEL_REF:
visitExpression(child, 0);
if (node.getIntProp(Node.OPTIONAL_CHAINING, 0) == 1) {
// On the stack we'll have either the Ref or undefined
addIcode(Icode_DUP);
stackChange(1);
// If it's null or undefined, just jump ahead
int afterLabel = iCodeTop;
addGotoOp(Icode.Icode_IF_NULL_UNDEF);
stackChange(-1);
// Otherwise do the GET_REF
addToken(type);
resolveForwardGoto(afterLabel);
} else {
addToken(type);
}
break;
case Token.SETPROP:
case Token.SETPROP_OP:
{
visitExpression(child, 0);
child = child.getNext();
String property = child.getString();
child = child.getNext();
if (type == Token.SETPROP_OP) {
addIcode(Icode_DUP);
stackChange(1);
addStringOp(Token.GETPROP, property);
// Compensate for the following USE_STACK
stackChange(-1);
}
visitExpression(child, 0);
addStringOp(
node.getIntProp(Node.SUPER_PROPERTY_ACCESS, 0) == 1
? Token.SETPROP_SUPER
: Token.SETPROP,
property);
stackChange(-1);
}
break;
case Token.SETELEM:
case Token.SETELEM_OP:
visitExpression(child, 0);
child = child.getNext();
visitExpression(child, 0);
child = child.getNext();
if (type == Token.SETELEM_OP) {
addIcode(Icode_DUP2);
stackChange(2);
addToken(Token.GETELEM);
stackChange(-1);
// Compensate for the following USE_STACK
stackChange(-1);
}
visitExpression(child, 0);
addToken(
node.getIntProp(Node.SUPER_PROPERTY_ACCESS, 0) == 1
? Token.SETELEM_SUPER
: Token.SETELEM);
stackChange(-2);
break;
case Token.SET_REF:
case Token.SET_REF_OP:
visitExpression(child, 0);
child = child.getNext();
if (type == Token.SET_REF_OP) {
addIcode(Icode_DUP);
stackChange(1);
addToken(Token.GET_REF);
// Compensate for the following USE_STACK
stackChange(-1);
}
visitExpression(child, 0);
addToken(Token.SET_REF);
stackChange(-1);
break;
case Token.STRICT_SETNAME:
case Token.SETNAME:
{
String name = child.getString();
visitExpression(child, 0);
child = child.getNext();
visitExpression(child, 0);
addStringOp(type, name);
stackChange(-1);
}
break;
case Token.SETCONST:
{
String name = child.getString();
visitExpression(child, 0);
child = child.getNext();
visitExpression(child, 0);
addStringOp(Icode_SETCONST, name);
stackChange(-1);
}
break;
case Token.TYPEOFNAME:
{
int index = -1;
// use typeofname if an activation frame exists
// since the vars all exist there instead of in jregs
if (itsInFunctionFlag && !itsData.itsNeedsActivation)
index = scriptOrFn.getIndexForNameNode(node);
if (index == -1) {
addStringOp(Icode_TYPEOFNAME, node.getString());
stackChange(1);
} else {
addVarOp(Token.GETVAR, index);
stackChange(1);
addToken(Token.TYPEOF);
}
}
break;
case Token.BINDNAME:
case Token.NAME:
case Token.STRING:
addStringOp(type, node.getString());
stackChange(1);
break;
case Token.INC:
case Token.DEC:
visitIncDec(node, child);
break;
case Token.NUMBER:
{
double num = node.getDouble();
int inum = (int) num;
if (inum == num) {
if (inum == 0) {
addIcode(Icode_ZERO);
// Check for negative zero
if (1.0 / num < 0.0) {
addToken(Token.NEG);
}
} else if (inum == 1) {
addIcode(Icode_ONE);
} else if ((short) inum == inum) {
addIcode(Icode_SHORTNUMBER);
// write short as uin16 bit pattern
addUint16(inum & 0xFFFF);
} else {
addIcode(Icode_INTNUMBER);
addInt(inum);
}
} else {
int index = getDoubleIndex(num);
addIndexOp(Token.NUMBER, index);
}
stackChange(1);
}
break;
case Token.GETVAR:
{
if (itsData.itsNeedsActivation) Kit.codeBug();
int index = scriptOrFn.getIndexForNameNode(node);
addVarOp(Token.GETVAR, index);
stackChange(1);
}
break;
case Token.SETVAR:
{
if (itsData.itsNeedsActivation) Kit.codeBug();
int index = scriptOrFn.getIndexForNameNode(child);
child = child.getNext();
visitExpression(child, 0);
addVarOp(Token.SETVAR, index);
}
break;
case Token.SETCONSTVAR:
{
if (itsData.itsNeedsActivation) Kit.codeBug();
int index = scriptOrFn.getIndexForNameNode(child);
child = child.getNext();
visitExpression(child, 0);
addVarOp(Token.SETCONSTVAR, index);
}
break;
case Token.NULL:
case Token.THIS:
case Token.SUPER:
case Token.THISFN:
case Token.FALSE:
case Token.TRUE:
addToken(type);
stackChange(1);
break;
case Token.ENUM_NEXT:
case Token.ENUM_ID:
addIndexOp(type, getLocalBlockRef(node));
stackChange(1);
break;
case Token.BIGINT:
addBigInt(node.getBigInt());
stackChange(1);
break;
case Token.REGEXP:
{
int index = node.getExistingIntProp(Node.REGEXP_PROP);
addIndexOp(Token.REGEXP, index);
stackChange(1);
}
break;
case Token.ARRAYLIT:
case Token.OBJECTLIT:
visitLiteral(node, child);
break;
case Token.ARRAYCOMP:
visitArrayComprehension(node, child, child.getNext());
break;
case Token.REF_SPECIAL:
visitExpression(child, 0);
if (node.getIntProp(Node.OPTIONAL_CHAINING, 0) == 1) {
// Jump if null or undefined
addIcode(Icode_DUP);
stackChange(1);
int putUndefinedLabel = iCodeTop;
addGotoOp(Icode.Icode_IF_NULL_UNDEF);
stackChange(-1);
// Access property
addStringOp(type, (String) node.getProp(Node.NAME_PROP));
int afterLabel = iCodeTop;
addGotoOp(Token.GOTO);
// Put undefined
resolveForwardGoto(putUndefinedLabel);
addIcode(Icode_POP);
addStringOp(Token.NAME, "undefined");
resolveForwardGoto(afterLabel);
} else {
addStringOp(type, (String) node.getProp(Node.NAME_PROP));
}
break;
case Token.REF_MEMBER:
case Token.REF_NS_MEMBER:
case Token.REF_NAME:
case Token.REF_NS_NAME:
{
int memberTypeFlags = node.getIntProp(Node.MEMBER_TYPE_PROP, 0);
// generate possible target, possible namespace and member
int childCount = 0;
do {
visitExpression(child, 0);
++childCount;
child = child.getNext();
} while (child != null);
addIndexOp(type, memberTypeFlags);
stackChange(1 - childCount);
}
break;
case Token.DOTQUERY:
{
int queryPC;
updateLineNumber(node);
visitExpression(child, 0);
addIcode(Icode_ENTERDQ);
stackChange(-1);
queryPC = iCodeTop;
visitExpression(child.getNext(), 0);
addBackwardGoto(Icode_LEAVEDQ, queryPC);
}
break;
case Token.DEFAULTNAMESPACE:
case Token.ESCXMLATTR:
case Token.ESCXMLTEXT:
visitExpression(child, 0);
addToken(type);
break;
case Token.YIELD:
case Token.YIELD_STAR:
if (child != null) {
visitExpression(child, 0);
} else {
addIcode(Icode_UNDEF);
stackChange(1);
}
if (type == Token.YIELD) {
addToken(Token.YIELD);
} else {
addIcode(Icode_YIELD_STAR);
}
addUint16(node.getLineno() & 0xFFFF);
break;
case Token.WITHEXPR:
{
Node enterWith = node.getFirstChild();
Node with = enterWith.getNext();
visitExpression(enterWith.getFirstChild(), 0);
addToken(Token.ENTERWITH);
stackChange(-1);
visitExpression(with.getFirstChild(), 0);
addToken(Token.LEAVEWITH);
break;
}
case Token.TEMPLATE_LITERAL:
visitTemplateLiteral(node);
break;
case Token.NULLISH_COALESCING:
{
visitExpression(child, 0);
child = child.getNext();
addIcode(Icode_DUP);
stackChange(1);
int end = iCodeTop;
addGotoOp(Icode.Icode_IF_NOT_NULL_UNDEF);
stackChange(-1);
addIcode(Icode_POP);
visitExpression(child, 0);
stackChange(-1);
resolveForwardGoto(end);
break;
}
default:
throw badTree(node);
}
if (savedStackDepth + 1 != stackDepth) {
Kit.codeBug();
}
}