in rhino/src/main/java/org/mozilla/javascript/optimizer/BodyCodegen.java [698:986]
private void generateStatement(Node node) {
updateLineNumber(node);
int type = node.getType();
Node child = node.getFirstChild();
switch (type) {
case Token.LOOP:
case Token.LABEL:
case Token.WITH:
case Token.SCRIPT:
case Token.BLOCK:
case Token.EMPTY:
// no-ops.
if (compilerEnv.isGenerateObserverCount()) {
// Need to add instruction count even for no-ops to catch
// cases like while (1) {}
addInstructionCount(1);
}
while (child != null) {
generateStatement(child);
child = child.getNext();
}
break;
case Token.LOCAL_BLOCK:
{
boolean prevLocal = inLocalBlock;
inLocalBlock = true;
int local = getNewWordLocal();
if (isGenerator) {
cfw.add(ByteCode.ACONST_NULL);
cfw.addAStore(local);
}
node.putIntProp(Node.LOCAL_PROP, local);
while (child != null) {
generateStatement(child);
child = child.getNext();
}
releaseWordLocal((short) local);
node.removeProp(Node.LOCAL_PROP);
inLocalBlock = prevLocal;
break;
}
case Token.FUNCTION:
{
int fnIndex = node.getExistingIntProp(Node.FUNCTION_PROP);
OptFunctionNode ofn = OptFunctionNode.get(scriptOrFn, fnIndex);
int t = ofn.fnode.getFunctionType();
if (t == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) {
visitFunction(ofn, t);
} else {
if (t != FunctionNode.FUNCTION_STATEMENT) {
throw Codegen.badTree();
}
}
break;
}
case Token.TRY:
visitTryCatchFinally((Jump) node, child);
break;
case Token.CATCH_SCOPE:
{
// nothing stays on the stack on entry into a catch scope
cfw.setStackTop((short) 0);
int local = getLocalBlockRegister(node);
int scopeIndex = node.getExistingIntProp(Node.CATCH_SCOPE_PROP);
String name = null;
if (child.getType() == Token.NAME) {
name = child.getString(); // name of exception
}
child = child.getNext();
generateExpression(child, node); // load expression object
if (scopeIndex == 0) {
cfw.add(ByteCode.ACONST_NULL);
} else {
// Load previous catch scope object
cfw.addALoad(local);
}
if (name != null) {
cfw.addPush(name);
} else {
cfw.add(ByteCode.ACONST_NULL);
}
cfw.addALoad(contextLocal);
cfw.addALoad(variableObjectLocal);
addScriptRuntimeInvoke(
"newCatchScope",
"(Ljava/lang/Throwable;"
+ "Lorg/mozilla/javascript/Scriptable;"
+ "Ljava/lang/String;"
+ "Lorg/mozilla/javascript/Context;"
+ "Lorg/mozilla/javascript/Scriptable;"
+ ")Lorg/mozilla/javascript/Scriptable;");
cfw.addAStore(local);
}
break;
case Token.THROW:
generateExpression(child, node);
if (compilerEnv.isGenerateObserverCount()) addInstructionCount();
generateThrowJavaScriptException();
break;
case Token.RETHROW:
if (compilerEnv.isGenerateObserverCount()) addInstructionCount();
cfw.addALoad(getLocalBlockRegister(node));
cfw.add(ByteCode.ATHROW);
break;
case Token.RETURN_RESULT:
case Token.RETURN:
if (child != null) {
generateExpression(child, node);
} else if (type == Token.RETURN) {
Codegen.pushUndefined(cfw);
} else {
if (popvLocal < 0) throw Codegen.badTree();
cfw.addALoad(popvLocal);
}
if (isGenerator) {
// Stash away the return value for use in the epilogue.
generateSetGeneratorReturnValue();
}
if (compilerEnv.isGenerateObserverCount()) addInstructionCount();
if (epilogueLabel == -1) {
if (!hasVarsInRegs) throw Codegen.badTree();
epilogueLabel = cfw.acquireLabel();
}
cfw.add(ByteCode.GOTO, epilogueLabel);
break;
case Token.SWITCH:
if (compilerEnv.isGenerateObserverCount()) addInstructionCount();
visitSwitch((Jump) node, child);
break;
case Token.ENTERWITH:
generateExpression(child, node);
cfw.addALoad(contextLocal);
cfw.addALoad(variableObjectLocal);
addScriptRuntimeInvoke(
"enterWith",
"(Ljava/lang/Object;"
+ "Lorg/mozilla/javascript/Context;"
+ "Lorg/mozilla/javascript/Scriptable;"
+ ")Lorg/mozilla/javascript/Scriptable;");
cfw.addAStore(variableObjectLocal);
incReferenceWordLocal(variableObjectLocal);
break;
case Token.LEAVEWITH:
cfw.addALoad(variableObjectLocal);
addScriptRuntimeInvoke(
"leaveWith",
"(Lorg/mozilla/javascript/Scriptable;"
+ ")Lorg/mozilla/javascript/Scriptable;");
cfw.addAStore(variableObjectLocal);
decReferenceWordLocal(variableObjectLocal);
break;
case Token.ENUM_INIT_KEYS:
case Token.ENUM_INIT_VALUES:
case Token.ENUM_INIT_ARRAY:
case Token.ENUM_INIT_VALUES_IN_ORDER:
generateExpression(child, node);
cfw.addALoad(contextLocal);
cfw.addALoad(variableObjectLocal);
int enumType =
type == Token.ENUM_INIT_KEYS
? ScriptRuntime.ENUMERATE_KEYS
: type == Token.ENUM_INIT_VALUES
? ScriptRuntime.ENUMERATE_VALUES
: type == Token.ENUM_INIT_VALUES_IN_ORDER
? ScriptRuntime.ENUMERATE_VALUES_IN_ORDER
: ScriptRuntime.ENUMERATE_ARRAY;
cfw.addPush(enumType);
addScriptRuntimeInvoke(
"enumInit",
"(Ljava/lang/Object;"
+ "Lorg/mozilla/javascript/Context;"
+ "Lorg/mozilla/javascript/Scriptable;"
+ "I"
+ ")Ljava/lang/Object;");
cfw.addAStore(getLocalBlockRegister(node));
break;
case Token.EXPR_VOID:
if (child.getType() == Token.SETVAR) {
/* special case this so as to avoid unnecessary
load's & pop's */
visitSetVar(child, child.getFirstChild(), false);
} else if (child.getType() == Token.SETCONSTVAR) {
/* special case this so as to avoid unnecessary
load's & pop's */
visitSetConstVar(child, child.getFirstChild(), false);
} else if ((child.getType() == Token.YIELD)
|| (child.getType() == Token.YIELD_STAR)) {
generateYieldPoint(child, false);
} else {
generateExpression(child, node);
if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) cfw.add(ByteCode.POP2);
else cfw.add(ByteCode.POP);
}
break;
case Token.EXPR_RESULT:
generateExpression(child, node);
if (popvLocal < 0) {
popvLocal = getNewWordLocal();
}
cfw.addAStore(popvLocal);
break;
case Token.TARGET:
{
if (compilerEnv.isGenerateObserverCount()) addInstructionCount();
int label = getTargetLabel(node);
cfw.markLabel(label);
if (compilerEnv.isGenerateObserverCount()) saveCurrentCodeOffset();
}
break;
case Token.JSR:
case Token.GOTO:
case Token.IFEQ:
case Token.IFNE:
if (compilerEnv.isGenerateObserverCount()) addInstructionCount();
visitGoto((Jump) node, type, child);
break;
case Token.FINALLY:
{
// This is the non-exception case for a finally block. In
// other words, since we inline finally blocks wherever
// jsr was previously used, and jsr is only used when the
// function is not a generator, we don't need to generate
// this case if the function isn't a generator.
if (!isGenerator) {
break;
}
if (compilerEnv.isGenerateObserverCount()) saveCurrentCodeOffset();
// there is exactly one value on the stack when enterring
// finally blocks: the return address (or its int encoding)
cfw.setStackTop((short) 1);
// Save return address in a new local
int finallyRegister = getNewWordLocal();
int finallyStart = cfw.acquireLabel();
int finallyEnd = cfw.acquireLabel();
cfw.markLabel(finallyStart);
generateIntegerWrap();
cfw.addAStore(finallyRegister);
while (child != null) {
generateStatement(child);
child = child.getNext();
}
cfw.addALoad(finallyRegister);
cfw.add(ByteCode.CHECKCAST, "java/lang/Integer");
generateIntegerUnwrap();
FinallyReturnPoint ret = finallys.get(node);
ret.tableLabel = cfw.acquireLabel();
cfw.add(ByteCode.GOTO, ret.tableLabel);
// After this GOTO we expect stack to be empty again!
cfw.setStackTop((short) 0);
releaseWordLocal((short) finallyRegister);
cfw.markLabel(finallyEnd);
}
break;
case Token.DEBUGGER:
break;
default:
throw Codegen.badTree();
}
}