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