private void visitStandardCall()

in rhino/src/main/java/org/mozilla/javascript/optimizer/BodyCodegen.java [2504:2682]


    private void visitStandardCall(Node node, Node child) {
        if (node.getType() != Token.CALL) throw Codegen.badTree();

        Node firstArgChild = child.getNext();
        int childType = child.getType();
        boolean isOptionalChainingCall = node.getIntProp(Node.OPTIONAL_CHAINING, 0) == 1;

        String methodName;
        String signature;
        Integer afterLabel = null;

        if (node.getIntProp(Node.SUPER_PROPERTY_ACCESS, 0) == 1) {
            // Just one general case, non optimized depending on the number of arguments

            int argCount = countArguments(firstArgChild);
            generateFunctionAndThisObj(child, node);

            // stack: ... functionObj, superObj is stored on scratch last scriptable
            // We discard the last scriptable, and then push the thisObj. So we have resolved the
            // function on the super object, but we invoke it with the current this.
            cfw.addALoad(contextLocal);
            cfw.addInvoke(
                    ByteCode.INVOKESTATIC,
                    "org/mozilla/javascript/ScriptRuntime",
                    "discardLastStoredScriptable",
                    "(Lorg/mozilla/javascript/Context;)V");
            cfw.addALoad(thisObjLocal);

            if (argCount == 0) {
                methodName = "call0";
                signature = SIGNATURE_CALL0;
            } else if (argCount == 1) {
                generateExpression(firstArgChild, node);
                methodName = "call1";
                signature = SIGNATURE_CALL1;
            } else {
                if (argCount == 2) {
                    generateExpression(firstArgChild, node);
                    generateExpression(firstArgChild.getNext(), node);
                    methodName = "call2";
                    signature = SIGNATURE_CALL2;
                } else {
                    generateCallArgArray(node, firstArgChild, false);
                    methodName = "callN";
                    signature = SIGNATURE_CALLN;
                }
            }
        } else if (firstArgChild == null) {
            if (childType == Token.NAME) {
                // name() call
                String name = child.getString();
                cfw.addPush(name);
                methodName = isOptionalChainingCall ? "callName0Optional" : "callName0";
                signature =
                        "(Ljava/lang/String;"
                                + "Lorg/mozilla/javascript/Context;"
                                + "Lorg/mozilla/javascript/Scriptable;"
                                + ")Ljava/lang/Object;";
            } else if (childType == Token.GETPROP) {
                // x.name() call
                Node propTarget = child.getFirstChild();
                generateExpression(propTarget, node);
                Node id = propTarget.getNext();
                String property = id.getString();
                cfw.addPush(property);
                methodName = isOptionalChainingCall ? "callProp0Optional" : "callProp0";
                signature =
                        "(Ljava/lang/Object;"
                                + "Ljava/lang/String;"
                                + "Lorg/mozilla/javascript/Context;"
                                + "Lorg/mozilla/javascript/Scriptable;"
                                + ")Ljava/lang/Object;";
            } else if (childType == Token.GETPROPNOWARN) {
                throw Kit.codeBug();
            } else {
                generateFunctionAndThisObj(child, node);
                pushThisFromLastScriptable();
                methodName = isOptionalChainingCall ? "call0Optional" : "call0";
                signature = SIGNATURE_CALL0;
            }

        } else if (childType == Token.NAME) {
            // XXX: this optimization is only possible if name
            // resolution
            // is not affected by arguments evaluation and currently
            // there are no checks for it
            String name = child.getString();
            if (isOptionalChainingCall) { // name?.()
                // eval name and this and put name in dynamic signature just like
                // with "GETWITHTHIS".
                cfw.addALoad(variableObjectLocal);
                cfw.addALoad(contextLocal);
                addDynamicInvoke("NAME:GETWITHTHISOPTIONAL:" + name, Signatures.NAME_GET_THIS);

                // jump to afterLabel is name is not null and not undefined
                afterLabel = cfw.acquireLabel();
                int doCallLabel = cfw.acquireLabel();
                cfw.add(ByteCode.DUP);
                addOptRuntimeInvoke("isNullOrUndefined", "(Ljava/lang/Object;)Z");
                cfw.add(ByteCode.IFEQ, doCallLabel);

                // push undefined and jump to end
                cfw.add(ByteCode.POP);
                cfw.add(
                        ByteCode.GETSTATIC,
                        "org/mozilla/javascript/Undefined",
                        "instance",
                        "Ljava/lang/Object;");
                cfw.add(ByteCode.GOTO, afterLabel);

                // push this, arguments, and do call
                cfw.markLabel(doCallLabel);
                pushThisFromLastScriptable();
                methodName = "callN";
                generateCallArgArray(node, firstArgChild, false);
                signature = SIGNATURE_CALLN;
            } else {
                generateCallArgArray(node, firstArgChild, false);
                cfw.addPush(name);
                methodName = "callName";
                signature =
                        "([Ljava/lang/Object;"
                                + "Ljava/lang/String;"
                                + "Lorg/mozilla/javascript/Context;"
                                + "Lorg/mozilla/javascript/Scriptable;"
                                + ")Ljava/lang/Object;";
            }
        } else {
            int argCount = countArguments(firstArgChild);
            generateFunctionAndThisObj(child, node);
            // stack: ... functionObj, thisObj is stored on scratch last scriptable

            if (isOptionalChainingCall) {
                // jump to afterLabel is name is not null and not undefined
                afterLabel = cfw.acquireLabel();
                int doCallLabel = cfw.acquireLabel();
                cfw.add(ByteCode.DUP);
                addOptRuntimeInvoke("isNullOrUndefined", "(Ljava/lang/Object;)Z");
                cfw.add(ByteCode.IFEQ, doCallLabel);

                // push undefined and jump to end
                cfw.add(ByteCode.POP);
                cfw.add(
                        ByteCode.GETSTATIC,
                        "org/mozilla/javascript/Undefined",
                        "instance",
                        "Ljava/lang/Object;");
                cfw.add(ByteCode.GOTO, afterLabel);

                cfw.markLabel(doCallLabel);
                cfw.add(ByteCode.CHECKCAST, "org/mozilla/javascript/Callable");
            }

            pushThisFromLastScriptable();
            if (argCount == 1) {
                generateExpression(firstArgChild, node);
                methodName = "call1";
                signature = SIGNATURE_CALL1;
            } else {
                if (argCount == 2) {
                    generateExpression(firstArgChild, node);
                    generateExpression(firstArgChild.getNext(), node);
                    methodName = "call2";
                    signature = SIGNATURE_CALL2;
                } else {
                    generateCallArgArray(node, firstArgChild, false);
                    methodName = "callN";
                    signature = SIGNATURE_CALLN;
                }
            }
        }

        cfw.addALoad(contextLocal);
        cfw.addALoad(variableObjectLocal);
        addOptRuntimeInvoke(methodName, signature);
        if (afterLabel != null) {
            cfw.markLabel(afterLabel);
        }
    }