private void generateExpression()

in rhino/src/main/java/org/mozilla/javascript/optimizer/BodyCodegen.java [1016:1775]


    private void generateExpression(Node node, Node parent) {
        int type = node.getType();
        Node child = node.getFirstChild();
        switch (type) {
            case Token.USE_STACK:
                break;

            case Token.FUNCTION:
                if (fnCurrent != null || parent.getType() != Token.SCRIPT) {
                    int fnIndex = node.getExistingIntProp(Node.FUNCTION_PROP);
                    OptFunctionNode ofn = OptFunctionNode.get(scriptOrFn, fnIndex);
                    int t = ofn.fnode.getFunctionType();
                    if (t != FunctionNode.FUNCTION_EXPRESSION && t != FunctionNode.ARROW_FUNCTION) {
                        throw Codegen.badTree();
                    }
                    visitFunction(ofn, t);
                }
                break;

            case Token.NAME:
                {
                    cfw.addALoad(variableObjectLocal);
                    cfw.addALoad(contextLocal);
                    addDynamicInvoke("NAME:GET:" + node.getString(), Signatures.NAME_GET);
                }
                break;

            case Token.CALL:
            case Token.NEW:
                {
                    int specialType = node.getIntProp(Node.SPECIALCALL_PROP, Node.NON_SPECIALCALL);
                    if (specialType == Node.NON_SPECIALCALL) {
                        OptFunctionNode target;
                        target = (OptFunctionNode) node.getProp(Node.DIRECTCALL_PROP);

                        if (target != null) {
                            visitOptimizedCall(node, target, type, child);
                        } else if (type == Token.CALL) {
                            visitStandardCall(node, child);
                        } else {
                            visitStandardNew(node, child);
                        }
                    } else {
                        visitSpecialCall(node, type, specialType, child);
                    }
                }
                break;

            case Token.REF_CALL:
                generateFunctionAndThisObj(child, node);
                pushThisFromLastScriptable();
                // stack: ... functionObj thisObj
                child = child.getNext();
                generateCallArgArray(node, child, false);
                cfw.addALoad(contextLocal);
                addScriptRuntimeInvoke(
                        "callRef",
                        "(Lorg/mozilla/javascript/Callable;"
                                + "Lorg/mozilla/javascript/Scriptable;"
                                + "[Ljava/lang/Object;"
                                + "Lorg/mozilla/javascript/Context;"
                                + ")Lorg/mozilla/javascript/Ref;");
                break;

            case Token.NUMBER:
                {
                    double num = node.getDouble();
                    if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) {
                        cfw.addPush(num);
                    } else {
                        codegen.pushNumberAsObject(cfw, num);
                    }
                }
                break;

            case Token.BIGINT:
                {
                    byte[] bytes = node.getBigInt().toByteArray();
                    cfw.add(ByteCode.NEW, "java/math/BigInteger");
                    cfw.add(ByteCode.DUP);
                    cfw.addPush(bytes.length);
                    cfw.add(ByteCode.NEWARRAY, ByteCode.T_BYTE);
                    for (int i = 0; i < bytes.length; i++) {
                        cfw.add(ByteCode.DUP);
                        cfw.addPush(i);
                        cfw.add(ByteCode.BIPUSH, bytes[i]);
                        cfw.add(ByteCode.BASTORE);
                    }
                    cfw.addInvoke(
                            ByteCode.INVOKESPECIAL, "java/math/BigInteger", "<init>", "([B)V");
                }
                break;

            case Token.STRING:
                cfw.addPush(node.getString());
                break;

            case Token.THIS:
                cfw.addALoad(thisObjLocal);
                break;

            case Token.SUPER:
                {
                    // See 9.1.1.3.5 GetSuperBase

                    // Read home object from activation, which we know we'll always have because we
                    // set "requiresActivation" to any function that mentions "super" in IRFactory
                    cfw.addALoad(variableObjectLocal);
                    cfw.add(ByteCode.CHECKCAST, "org/mozilla/javascript/NativeCall");
                    cfw.addInvoke(
                            ByteCode.INVOKEVIRTUAL,
                            "org/mozilla/javascript/NativeCall",
                            "getHomeObject",
                            "()Lorg/mozilla/javascript/Scriptable;");

                    // If null, then put undefined
                    int after = cfw.acquireLabel();
                    int putPrototype = cfw.acquireLabel();
                    cfw.add(ByteCode.DUP);
                    cfw.add(ByteCode.IFNONNULL, putPrototype);

                    cfw.add(ByteCode.POP);
                    cfw.add(
                            ByteCode.GETSTATIC,
                            "org/mozilla/javascript/Undefined",
                            "instance",
                            "Ljava/lang/Object;");
                    cfw.add(ByteCode.GOTO, after);

                    // Otherwise put the prototype
                    cfw.markLabel(putPrototype);
                    cfw.addInvoke(
                            ByteCode.INVOKEINTERFACE,
                            "org/mozilla/javascript/Scriptable",
                            "getPrototype",
                            "()Lorg/mozilla/javascript/Scriptable;");

                    cfw.markLabel(after);
                    break;
                }

            case Token.THISFN:
                cfw.add(ByteCode.ALOAD_0);
                break;

            case Token.NULL:
                cfw.add(ByteCode.ACONST_NULL);
                break;

            case Token.TRUE:
                cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean", "TRUE", "Ljava/lang/Boolean;");
                break;

            case Token.FALSE:
                cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean", "FALSE", "Ljava/lang/Boolean;");
                break;

            case Token.REGEXP:
                {
                    // Create a new wrapper around precompiled regexp
                    cfw.addALoad(contextLocal);
                    cfw.addALoad(variableObjectLocal);
                    int i = node.getExistingIntProp(Node.REGEXP_PROP);
                    cfw.add(
                            ByteCode.GETSTATIC,
                            codegen.mainClassName,
                            codegen.getCompiledRegexpName(scriptOrFn, i),
                            "Ljava/lang/Object;");
                    cfw.addInvoke(
                            ByteCode.INVOKESTATIC,
                            "org/mozilla/javascript/ScriptRuntime",
                            "wrapRegExp",
                            "(Lorg/mozilla/javascript/Context;"
                                    + "Lorg/mozilla/javascript/Scriptable;"
                                    + "Ljava/lang/Object;"
                                    + ")Lorg/mozilla/javascript/Scriptable;");
                }
                break;

            case Token.COMMA:
                {
                    Node next = child.getNext();
                    while (next != null) {
                        generateExpression(child, node);
                        cfw.add(ByteCode.POP);
                        child = next;
                        next = next.getNext();
                    }
                    generateExpression(child, node);
                    break;
                }

            case Token.ENUM_NEXT:
            case Token.ENUM_ID:
                {
                    int local = getLocalBlockRegister(node);
                    cfw.addALoad(local);
                    cfw.addALoad(contextLocal);
                    if (type == Token.ENUM_NEXT) {
                        addScriptRuntimeInvoke(
                                "enumNext",
                                "(Ljava/lang/Object;"
                                        + "Lorg/mozilla/javascript/Context;"
                                        + ")Ljava/lang/Boolean;");
                    } else {
                        addScriptRuntimeInvoke(
                                "enumId",
                                "(Ljava/lang/Object;"
                                        + "Lorg/mozilla/javascript/Context;"
                                        + ")Ljava/lang/Object;");
                    }
                    break;
                }

            case Token.ARRAYLIT:
                visitArrayLiteral(node, child, false);
                break;

            case Token.OBJECTLIT:
                visitObjectLiteral(node, child, false);
                break;

            case Token.NOT:
                {
                    int trueTarget = cfw.acquireLabel();
                    int falseTarget = cfw.acquireLabel();
                    int beyond = cfw.acquireLabel();
                    generateIfJump(child, node, trueTarget, falseTarget);

                    cfw.markLabel(trueTarget);
                    cfw.add(
                            ByteCode.GETSTATIC,
                            "java/lang/Boolean",
                            "FALSE",
                            "Ljava/lang/Boolean;");
                    cfw.add(ByteCode.GOTO, beyond);
                    cfw.markLabel(falseTarget);
                    cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean", "TRUE", "Ljava/lang/Boolean;");
                    cfw.markLabel(beyond);
                    cfw.adjustStackTop(-1);
                    break;
                }

            case Token.BITNOT:
                visitBitNot(node, child);
                break;

            case Token.VOID:
                generateExpression(child, node);
                cfw.add(ByteCode.POP);
                Codegen.pushUndefined(cfw);
                break;

            case Token.TYPEOF:
                generateExpression(child, node);
                addScriptRuntimeInvoke("typeof", "(Ljava/lang/Object;" + ")Ljava/lang/String;");
                break;

            case Token.TYPEOFNAME:
                visitTypeofname(node);
                break;

            case Token.INC:
            case Token.DEC:
                visitIncDec(node);
                break;

            case Token.OR:
            case Token.AND:
                {
                    generateExpression(child, node);
                    cfw.add(ByteCode.DUP);
                    addDynamicInvoke("MATH:TOBOOLEAN", Signatures.MATH_TO_BOOLEAN);
                    int falseTarget = cfw.acquireLabel();
                    if (type == Token.AND) cfw.add(ByteCode.IFEQ, falseTarget);
                    else cfw.add(ByteCode.IFNE, falseTarget);
                    cfw.add(ByteCode.POP);
                    generateExpression(child.getNext(), node);
                    cfw.markLabel(falseTarget);
                }
                break;

            case Token.HOOK:
                {
                    Node ifThen = child.getNext();
                    Node ifElse = ifThen.getNext();
                    generateExpression(child, node);
                    addDynamicInvoke("MATH:TOBOOLEAN", Signatures.MATH_TO_BOOLEAN);
                    int elseTarget = cfw.acquireLabel();
                    cfw.add(ByteCode.IFEQ, elseTarget);
                    int stack = cfw.getStackTop();
                    generateExpression(ifThen, node);
                    int afterHook = cfw.acquireLabel();
                    cfw.add(ByteCode.GOTO, afterHook);
                    cfw.markLabel(elseTarget, stack);
                    generateExpression(ifElse, node);
                    cfw.markLabel(afterHook);
                }
                break;

            case Token.ADD:
                {
                    generateExpression(child, node);
                    generateExpression(child.getNext(), node);
                    switch (node.getIntProp(Node.ISNUMBER_PROP, -1)) {
                        case Node.BOTH:
                            cfw.add(ByteCode.DADD);
                            break;
                        case Node.LEFT:
                            cfw.addALoad(contextLocal);
                            addOptRuntimeInvoke(
                                    "add",
                                    "(DLjava/lang/Object;Lorg/mozilla/javascript/Context;)Ljava/lang/Object;");
                            break;
                        case Node.RIGHT:
                            cfw.addALoad(contextLocal);
                            addOptRuntimeInvoke(
                                    "add",
                                    "(Ljava/lang/Object;DLorg/mozilla/javascript/Context;)Ljava/lang/Object;");
                            break;
                        default:
                            cfw.addALoad(contextLocal);
                            addDynamicInvoke("MATH:ADD", Signatures.MATH_ADD);
                    }
                }
                break;

            case Token.SUB:
            case Token.MUL:
            case Token.DIV:
            case Token.MOD:
                visitArithmetic(node, type, child, parent);
                break;

            case Token.EXP:
                visitExponentiation(node, child, parent);
                break;

            case Token.BITOR:
            case Token.BITXOR:
            case Token.BITAND:
            case Token.LSH:
            case Token.RSH:
            case Token.URSH:
                visitBitOp(node, type, child);
                break;

            case Token.POS:
                {
                    int childNumberFlag = node.getIntProp(Node.ISNUMBER_PROP, -1);
                    generateExpression(child, node);
                    if (childNumberFlag == -1) {
                        addObjectToDouble();
                        addDoubleWrap();
                    }
                    break;
                }

            case Token.NEG:
                {
                    int childNumberFlag = node.getIntProp(Node.ISNUMBER_PROP, -1);
                    generateExpression(child, node);
                    if (childNumberFlag == -1) {
                        addObjectToNumeric();
                        addScriptRuntimeInvoke(
                                "negate", "(Ljava/lang/Number;" + ")Ljava/lang/Number;");
                    } else {
                        cfw.add(ByteCode.DNEG);
                    }
                    break;
                }

            case Token.TO_DOUBLE:
                // cnvt to double (not Double)
                generateExpression(child, node);
                addObjectToDouble();
                break;

            case Token.TO_OBJECT:
                {
                    // convert from double
                    int prop = -1;
                    if (child.getType() == Token.NUMBER) {
                        prop = child.getIntProp(Node.ISNUMBER_PROP, -1);
                    }
                    if (prop != -1) {
                        child.removeProp(Node.ISNUMBER_PROP);
                        generateExpression(child, node);
                        child.putIntProp(Node.ISNUMBER_PROP, prop);
                    } else {
                        generateExpression(child, node);
                        addDoubleWrap();
                    }
                    break;
                }

            case Token.IN:
            case Token.INSTANCEOF:
            case Token.LE:
            case Token.LT:
            case Token.GE:
            case Token.GT:
                {
                    int trueGOTO = cfw.acquireLabel();
                    int falseGOTO = cfw.acquireLabel();
                    visitIfJumpRelOp(node, child, trueGOTO, falseGOTO);
                    addJumpedBooleanWrap(trueGOTO, falseGOTO);
                    break;
                }

            case Token.EQ:
            case Token.NE:
            case Token.SHEQ:
            case Token.SHNE:
                {
                    int trueGOTO = cfw.acquireLabel();
                    int falseGOTO = cfw.acquireLabel();
                    visitIfJumpEqOp(node, child, trueGOTO, falseGOTO);
                    addJumpedBooleanWrap(trueGOTO, falseGOTO);
                    break;
                }

            case Token.GETPROP:
            case Token.GETPROPNOWARN:
                visitGetProp(node, child);
                break;

            case Token.GETELEM:
                generateExpression(child, node); // object
                if (node.getIntProp(Node.OPTIONAL_CHAINING, 0) == 1) {
                    int getElem = cfw.acquireLabel();
                    int after = cfw.acquireLabel();

                    cfw.add(ByteCode.DUP);
                    addOptRuntimeInvoke("isNullOrUndefined", "(Ljava/lang/Object;)Z");
                    cfw.add(ByteCode.IFEQ, getElem);

                    cfw.add(ByteCode.POP);
                    cfw.add(
                            ByteCode.GETSTATIC,
                            "org/mozilla/javascript/Undefined",
                            "instance",
                            "Ljava/lang/Object;");
                    cfw.add(ByteCode.GOTO, after);

                    cfw.markLabel(getElem);
                    finishGetElemGeneration(node, child);
                    cfw.markLabel(after);
                } else {
                    finishGetElemGeneration(node, child);
                }
                break;

            case Token.GET_REF:
                generateExpression(child, node); // reference
                if (node.getIntProp(Node.OPTIONAL_CHAINING, 0) == 1) {
                    int getRef = cfw.acquireLabel();
                    int after = cfw.acquireLabel();

                    cfw.add(ByteCode.DUP);

                    addOptRuntimeInvoke("isNullOrUndefined", "(Ljava/lang/Object;)Z");
                    cfw.add(ByteCode.IFEQ, getRef);
                    cfw.add(ByteCode.GOTO, after);

                    cfw.markLabel(getRef);
                    cfw.add(ByteCode.CHECKCAST, "org/mozilla/javascript/Ref");
                    finishGetRefGeneration();
                    cfw.markLabel(after);
                } else {
                    finishGetRefGeneration();
                }
                break;

            case Token.GETVAR:
                visitGetVar(node);
                break;

            case Token.SETVAR:
                visitSetVar(node, child, true);
                break;

            case Token.SETNAME:
                visitSetName(node, child);
                break;

            case Token.STRICT_SETNAME:
                visitStrictSetName(node, child);
                break;

            case Token.SETCONST:
                visitSetConst(node, child);
                break;

            case Token.SETCONSTVAR:
                visitSetConstVar(node, child, true);
                break;

            case Token.SETPROP:
            case Token.SETPROP_OP:
                visitSetProp(type, node, child);
                break;

            case Token.SETELEM:
            case Token.SETELEM_OP:
                visitSetElem(type, node, child);
                break;

            case Token.SET_REF:
            case Token.SET_REF_OP:
                {
                    generateExpression(child, node);
                    child = child.getNext();
                    if (type == Token.SET_REF_OP) {
                        cfw.add(ByteCode.DUP);
                        cfw.addALoad(contextLocal);
                        addScriptRuntimeInvoke(
                                "refGet",
                                "(Lorg/mozilla/javascript/Ref;"
                                        + "Lorg/mozilla/javascript/Context;"
                                        + ")Ljava/lang/Object;");
                    }
                    generateExpression(child, node);
                    cfw.addALoad(contextLocal);
                    cfw.addALoad(variableObjectLocal);
                    addScriptRuntimeInvoke(
                            "refSet",
                            "(Lorg/mozilla/javascript/Ref;"
                                    + "Ljava/lang/Object;"
                                    + "Lorg/mozilla/javascript/Context;"
                                    + "Lorg/mozilla/javascript/Scriptable;"
                                    + ")Ljava/lang/Object;");
                }
                break;

            case Token.DEL_REF:
                generateExpression(child, node);
                cfw.addALoad(contextLocal);
                addScriptRuntimeInvoke(
                        "refDel",
                        "(Lorg/mozilla/javascript/Ref;"
                                + "Lorg/mozilla/javascript/Context;"
                                + ")Ljava/lang/Object;");
                break;

            case Token.DELPROP:
                boolean isName = child.getType() == Token.BINDNAME;
                generateExpression(child, node);
                child = child.getNext();
                generateExpression(child, node);
                if (node.getIntProp(Node.SUPER_PROPERTY_ACCESS, 0) == 1) {
                    // We have pushed `super` and the expression, but we need to remove them because
                    // we actually are just going to throw an error. However, delete is supposed to
                    // put a boolean on the stack and the class file writer would complain if we
                    // don't have only popped here. So we pop and the push 0 (false). Anyway, this
                    // is code that will always fail, so honestly no one will ever write something
                    // like this (delete super[foo]), so... even if this is not the most efficient
                    // bytecode, it's fine.
                    cfw.add(ByteCode.POP2);
                    cfw.addLoadConstant(0);
                    addScriptRuntimeInvoke("throwDeleteOnSuperPropertyNotAllowed", "()V");
                } else {
                    cfw.addALoad(contextLocal);
                    cfw.addPush(isName);
                    addScriptRuntimeInvoke(
                            "delete",
                            "(Ljava/lang/Object;"
                                    + "Ljava/lang/Object;"
                                    + "Lorg/mozilla/javascript/Context;"
                                    + "Z)Ljava/lang/Object;");
                }
                break;

            case Token.BINDNAME:
                {
                    while (child != null) {
                        generateExpression(child, node);
                        child = child.getNext();
                    }
                    // Generate code for "ScriptRuntime.bind(varObj, "s")"
                    cfw.addALoad(variableObjectLocal);
                    cfw.addALoad(contextLocal);
                    addDynamicInvoke("NAME:BIND:" + node.getString(), Signatures.NAME_BIND);
                }
                break;

            case Token.LOCAL_LOAD:
                cfw.addALoad(getLocalBlockRegister(node));
                break;

            case Token.REF_SPECIAL:
                {
                    generateExpression(child, node);
                    if (node.getIntProp(Node.OPTIONAL_CHAINING, 0) == 1) {
                        int getExpr = cfw.acquireLabel();
                        int after = cfw.acquireLabel();

                        cfw.add(ByteCode.DUP);

                        addOptRuntimeInvoke("isNullOrUndefined", "(Ljava/lang/Object;)Z");
                        cfw.add(ByteCode.IFEQ, getExpr);

                        cfw.add(ByteCode.POP);
                        cfw.add(
                                ByteCode.GETSTATIC,
                                "org/mozilla/javascript/Undefined",
                                "instance",
                                "Ljava/lang/Object;");
                        cfw.add(ByteCode.GOTO, after);

                        cfw.markLabel(getExpr);
                        finishRefSpecialGeneration(node);
                        cfw.markLabel(after);
                    } else {
                        finishRefSpecialGeneration(node);
                    }
                }
                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
                    do {
                        generateExpression(child, node);
                        child = child.getNext();
                    } while (child != null);
                    cfw.addALoad(contextLocal);
                    String methodName, signature;
                    switch (type) {
                        case Token.REF_MEMBER:
                            methodName = "memberRef";
                            signature =
                                    "(Ljava/lang/Object;"
                                            + "Ljava/lang/Object;"
                                            + "Lorg/mozilla/javascript/Context;"
                                            + "I"
                                            + ")Lorg/mozilla/javascript/Ref;";
                            break;
                        case Token.REF_NS_MEMBER:
                            methodName = "memberRef";
                            signature =
                                    "(Ljava/lang/Object;"
                                            + "Ljava/lang/Object;"
                                            + "Ljava/lang/Object;"
                                            + "Lorg/mozilla/javascript/Context;"
                                            + "I"
                                            + ")Lorg/mozilla/javascript/Ref;";
                            break;
                        case Token.REF_NAME:
                            methodName = "nameRef";
                            signature =
                                    "(Ljava/lang/Object;"
                                            + "Lorg/mozilla/javascript/Context;"
                                            + "Lorg/mozilla/javascript/Scriptable;"
                                            + "I"
                                            + ")Lorg/mozilla/javascript/Ref;";
                            cfw.addALoad(variableObjectLocal);
                            break;
                        case Token.REF_NS_NAME:
                            methodName = "nameRef";
                            signature =
                                    "(Ljava/lang/Object;"
                                            + "Ljava/lang/Object;"
                                            + "Lorg/mozilla/javascript/Context;"
                                            + "Lorg/mozilla/javascript/Scriptable;"
                                            + "I"
                                            + ")Lorg/mozilla/javascript/Ref;";
                            cfw.addALoad(variableObjectLocal);
                            break;
                        default:
                            throw Kit.codeBug();
                    }
                    cfw.addPush(memberTypeFlags);
                    addScriptRuntimeInvoke(methodName, signature);
                }
                break;

            case Token.DOTQUERY:
                visitDotQuery(node, child);
                break;

            case Token.ESCXMLATTR:
                generateExpression(child, node);
                cfw.addALoad(contextLocal);
                addScriptRuntimeInvoke(
                        "escapeAttributeValue",
                        "(Ljava/lang/Object;"
                                + "Lorg/mozilla/javascript/Context;"
                                + ")Ljava/lang/String;");
                break;

            case Token.ESCXMLTEXT:
                generateExpression(child, node);
                cfw.addALoad(contextLocal);
                addScriptRuntimeInvoke(
                        "escapeTextValue",
                        "(Ljava/lang/Object;"
                                + "Lorg/mozilla/javascript/Context;"
                                + ")Ljava/lang/String;");
                break;

            case Token.DEFAULTNAMESPACE:
                generateExpression(child, node);
                cfw.addALoad(contextLocal);
                addScriptRuntimeInvoke(
                        "setDefaultNamespace",
                        "(Ljava/lang/Object;"
                                + "Lorg/mozilla/javascript/Context;"
                                + ")Ljava/lang/Object;");
                break;

            case Token.YIELD:
            case Token.YIELD_STAR:
                generateYieldPoint(node, true);
                break;

            case Token.WITHEXPR:
                {
                    Node with = child.getNext();
                    Node leaveWith = with.getNext();
                    generateStatement(child);
                    generateExpression(with.getFirstChild(), with);
                    generateStatement(leaveWith);
                    break;
                }

            case Token.ARRAYCOMP:
                {
                    Node expr = child.getNext();
                    generateStatement(child);
                    generateExpression(expr, node);
                    break;
                }

            case Token.TEMPLATE_LITERAL:
                visitTemplateLiteral(node);
                break;

            case Token.NULLISH_COALESCING:
                {
                    generateExpression(child, node); // left-hand side
                    int end = cfw.acquireLabel();

                    cfw.add(ByteCode.DUP);
                    addOptRuntimeInvoke("isNullOrUndefined", "(Ljava/lang/Object;)Z");
                    cfw.add(ByteCode.IFEQ, end);

                    cfw.add(ByteCode.POP);
                    generateExpression(child.getNext(), node); // right-hand side
                    cfw.markLabel(end);
                    break;
                }

            default:
                throw new RuntimeException("Unexpected node type " + type);
        }
    }