private void visitExpression()

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();
        }
    }