in rhino/src/main/java/org/mozilla/javascript/optimizer/BodyCodegen.java [225:554]
private void generatePrologue() {
if (inDirectCallFunction) {
int directParameterCount = scriptOrFn.getParamCount();
// 0 is reserved for function Object 'this'
// 1 is reserved for context
// 2 is reserved for parentScope
// 3 is reserved for script 'this'
if (firstFreeLocal != 4) Kit.codeBug();
for (int i = 0; i != directParameterCount; ++i) {
varRegisters[i] = firstFreeLocal;
// 3 is 1 for Object parm and 2 for double parm
firstFreeLocal += 3;
}
if (!fnCurrent.getParameterNumberContext()) {
// make sure that all parameters are objects
itsForcedObjectParameters = true;
for (int i = 0; i != directParameterCount; ++i) {
int reg = varRegisters[i];
cfw.addALoad(reg);
cfw.add(ByteCode.GETSTATIC, "java/lang/Void", "TYPE", "Ljava/lang/Class;");
int isObjectLabel = cfw.acquireLabel();
cfw.add(ByteCode.IF_ACMPNE, isObjectLabel);
cfw.addDLoad(reg + 1);
addDoubleWrap();
cfw.addAStore(reg);
cfw.markLabel(isObjectLabel);
}
}
}
if (fnCurrent != null) {
// Use the enclosing scope of the function as our variable object.
cfw.addALoad(funObjLocal);
cfw.addInvoke(
ByteCode.INVOKEINTERFACE,
"org/mozilla/javascript/Scriptable",
"getParentScope",
"()Lorg/mozilla/javascript/Scriptable;");
cfw.addAStore(variableObjectLocal);
}
// reserve 'args[]'
argsLocal = firstFreeLocal++;
localsMax = firstFreeLocal;
// Generate Generator specific prelude
if (isGenerator) {
// reserve 'args[]'
operationLocal = firstFreeLocal++;
localsMax = firstFreeLocal;
// Local 3 is a reference to a GeneratorState object. The rest
// of codegen expects local 3 to be a reference to the thisObj.
// So move the value in local 3 to generatorStateLocal, and load
// the saved thisObj from the GeneratorState object.
cfw.addALoad(thisObjLocal);
generatorStateLocal = firstFreeLocal++;
localsMax = firstFreeLocal;
cfw.add(ByteCode.CHECKCAST, OptRuntime.GeneratorState.CLASS_NAME);
cfw.add(ByteCode.DUP);
cfw.addAStore(generatorStateLocal);
cfw.add(
ByteCode.GETFIELD,
OptRuntime.GeneratorState.CLASS_NAME,
OptRuntime.GeneratorState.thisObj_NAME,
OptRuntime.GeneratorState.thisObj_TYPE);
cfw.addAStore(thisObjLocal);
if (epilogueLabel == -1) {
epilogueLabel = cfw.acquireLabel();
}
List<Node> targets = ((FunctionNode) scriptOrFn).getResumptionPoints();
if (targets != null) {
// get resumption point
generateGetGeneratorResumptionPoint();
// generate dispatch table
generatorSwitch = cfw.addTableSwitch(0, targets.size() + GENERATOR_START);
generateCheckForThrowOrClose(-1, false, GENERATOR_START);
}
}
// Compile RegExp and template literals if this is a script. For functions
// this is performed during instantiation in functionInit
if (fnCurrent == null) {
if (scriptOrFn.getRegexpCount() != 0) {
cfw.addALoad(contextLocal);
cfw.addInvoke(
ByteCode.INVOKESTATIC,
codegen.mainClassName,
Codegen.REGEXP_INIT_METHOD_NAME,
Codegen.REGEXP_INIT_METHOD_SIGNATURE);
}
if (scriptOrFn.getTemplateLiteralCount() != 0) {
cfw.addInvoke(
ByteCode.INVOKESTATIC,
codegen.mainClassName,
Codegen.TEMPLATE_LITERAL_INIT_METHOD_NAME,
Codegen.TEMPLATE_LITERAL_INIT_METHOD_SIGNATURE);
}
}
if (compilerEnv.isGenerateObserverCount()) saveCurrentCodeOffset();
// skip creating activation object or loading args for the body of a generator. The
// activation record required by a generator has already been created
// in generateGenerator().
if (isGenerator) return;
if (hasVarsInRegs) {
// No need to create activation. Pad arguments if need be.
int parmCount = scriptOrFn.getParamCount();
if (parmCount > 0 && !inDirectCallFunction) {
// Set up args array
if (scriptOrFn.hasRestParameter()) {
cfw.addALoad(contextLocal);
cfw.addALoad(variableObjectLocal);
cfw.addALoad(argsLocal);
cfw.addPush(parmCount);
addScriptRuntimeInvoke(
"padAndRestArguments",
"("
+ "Lorg/mozilla/javascript/Context;"
+ "Lorg/mozilla/javascript/Scriptable;"
+ "[Ljava/lang/Object;"
+ "I"
+ ")[Ljava/lang/Object;");
cfw.addAStore(argsLocal);
} else {
// check length of arguments, pad if need be
cfw.addALoad(argsLocal);
cfw.add(ByteCode.ARRAYLENGTH);
cfw.addPush(parmCount);
int label = cfw.acquireLabel();
cfw.add(ByteCode.IF_ICMPGE, label);
cfw.addALoad(argsLocal);
cfw.addPush(parmCount);
addScriptRuntimeInvoke(
"padArguments", "([Ljava/lang/Object;I)[Ljava/lang/Object;");
cfw.addAStore(argsLocal);
cfw.markLabel(label);
}
}
int paramCount = fnCurrent.fnode.getParamCount();
int varCount = fnCurrent.fnode.getParamAndVarCount();
boolean[] constDeclarations = fnCurrent.fnode.getParamAndVarConst();
// REMIND - only need to initialize the vars that don't get a value
// before the next call and are used in the function
int firstUndefVar = -1;
for (int i = 0; i != varCount; ++i) {
int reg = -1;
if (i < paramCount) {
if (!inDirectCallFunction) {
reg = getNewWordLocal();
cfw.addALoad(argsLocal);
cfw.addPush(i);
cfw.add(ByteCode.AALOAD);
cfw.addAStore(reg);
}
} else if (fnCurrent.isNumberVar(i)) {
reg = getNewWordPairLocal(constDeclarations[i]);
cfw.addPush(0.0);
cfw.addDStore(reg);
} else {
reg = getNewWordLocal(constDeclarations[i]);
if (firstUndefVar == -1) {
Codegen.pushUndefined(cfw);
firstUndefVar = reg;
} else {
cfw.addALoad(firstUndefVar);
}
cfw.addAStore(reg);
}
if (reg >= 0) {
if (constDeclarations[i]) {
cfw.addPush(0);
cfw.addIStore(reg + (fnCurrent.isNumberVar(i) ? 2 : 1));
}
varRegisters[i] = reg;
}
// Add debug table entry if we're generating debug info
if (compilerEnv.isGenerateDebugInfo()) {
String name = fnCurrent.fnode.getParamOrVarName(i);
String type = fnCurrent.isNumberVar(i) ? "D" : "Ljava/lang/Object;";
int startPC = cfw.getCurrentCodeOffset();
if (reg < 0) {
reg = varRegisters[i];
}
cfw.addVariableDescriptor(name, type, startPC, reg);
}
}
// Skip creating activation object.
return;
}
String debugVariableName;
boolean isArrow = false;
if (scriptOrFn instanceof FunctionNode) {
isArrow = ((FunctionNode) scriptOrFn).getFunctionType() == FunctionNode.ARROW_FUNCTION;
}
if (fnCurrent != null) {
debugVariableName = "activation";
cfw.addALoad(funObjLocal);
cfw.addALoad(contextLocal);
cfw.addALoad(variableObjectLocal);
cfw.addALoad(argsLocal);
cfw.addPush(scriptOrFn.isInStrictMode());
cfw.addPush(scriptOrFn.hasRestParameter());
cfw.addPush(
!(scriptOrFn instanceof FunctionNode)
|| ((FunctionNode) scriptOrFn).requiresArgumentObject());
if (!isArrow) {
// Just pass the home object of the function
cfw.addLoadThis();
cfw.addInvoke(
ByteCode.INVOKEVIRTUAL,
"org/mozilla/javascript/BaseFunction",
"getHomeObject",
"()Lorg/mozilla/javascript/Scriptable;");
} else {
// Propagate the home object from the activation scope (i.e. the NativeCall)
int putNull = cfw.acquireLabel();
int after = cfw.acquireLabel();
cfw.addALoad(variableObjectLocal);
cfw.add(ByteCode.INSTANCEOF, "org/mozilla/javascript/NativeCall");
cfw.add(ByteCode.IFEQ, putNull);
cfw.addALoad(variableObjectLocal);
cfw.add(ByteCode.CHECKCAST, "org/mozilla/javascript/NativeCall");
cfw.addInvoke(
ByteCode.INVOKEVIRTUAL,
"org/mozilla/javascript/NativeCall",
"getHomeObject",
"()Lorg/mozilla/javascript/Scriptable;");
cfw.add(ByteCode.GOTO, after);
cfw.markLabel(putNull);
cfw.add(ByteCode.ACONST_NULL);
cfw.markLabel(after);
}
String methodName =
isArrow ? "createArrowFunctionActivation" : "createFunctionActivation";
addScriptRuntimeInvoke(
methodName,
"(Lorg/mozilla/javascript/NativeFunction;"
+ "Lorg/mozilla/javascript/Context;"
+ "Lorg/mozilla/javascript/Scriptable;"
+ "[Ljava/lang/Object;"
+ "Z"
+ "Z"
+ "Z"
+ "Lorg/mozilla/javascript/Scriptable;"
+ ")Lorg/mozilla/javascript/Scriptable;");
cfw.addAStore(variableObjectLocal);
cfw.addALoad(contextLocal);
cfw.addALoad(variableObjectLocal);
addScriptRuntimeInvoke(
"enterActivationFunction",
"(Lorg/mozilla/javascript/Context;"
+ "Lorg/mozilla/javascript/Scriptable;"
+ ")V");
} else {
debugVariableName = "global";
cfw.addALoad(funObjLocal);
cfw.addALoad(thisObjLocal);
cfw.addALoad(contextLocal);
cfw.addALoad(variableObjectLocal);
cfw.addPush(0); // false to indicate it is not eval script
addScriptRuntimeInvoke(
"initScript",
"(Lorg/mozilla/javascript/NativeFunction;"
+ "Lorg/mozilla/javascript/Scriptable;"
+ "Lorg/mozilla/javascript/Context;"
+ "Lorg/mozilla/javascript/Scriptable;"
+ "Z"
+ ")V");
}
enterAreaStartLabel = cfw.acquireLabel();
epilogueLabel = cfw.acquireLabel();
cfw.markLabel(enterAreaStartLabel);
generateNestedFunctionInits();
// default is to generate debug info
if (compilerEnv.isGenerateDebugInfo()) {
cfw.addVariableDescriptor(
debugVariableName,
"Lorg/mozilla/javascript/Scriptable;",
cfw.getCurrentCodeOffset(),
variableObjectLocal);
}
if (fnCurrent == null) {
// OPT: use dataflow to prove that this assignment is dead
popvLocal = getNewWordLocal();
Codegen.pushUndefined(cfw);
cfw.addAStore(popvLocal);
int linenum = scriptOrFn.getEndLineno();
if (linenum != -1) cfw.addLineNumberEntry((short) linenum);
} else {
if (fnCurrent.itsContainsCalls0) {
itsZeroArgArray = getNewWordLocal();
cfw.add(
ByteCode.GETSTATIC,
"org/mozilla/javascript/ScriptRuntime",
"emptyArgs",
"[Ljava/lang/Object;");
cfw.addAStore(itsZeroArgArray);
}
if (fnCurrent.itsContainsCalls1) {
itsOneArgArray = getNewWordLocal();
cfw.addPush(1);
cfw.add(ByteCode.ANEWARRAY, "java/lang/Object");
cfw.addAStore(itsOneArgArray);
}
}
}