use of org.mozilla.javascript.ast.FunctionNode in project HL4A by HL4A.
the class NodeTransformer method transform.
public final void transform(ScriptNode tree, boolean inStrictMode, CompilerEnvirons env) {
boolean useStrictMode = inStrictMode;
// many existing scripts.
if ((env.getLanguageVersion() >= Context.VERSION_ES6) && tree.isInStrictMode()) {
useStrictMode = true;
}
transformCompilationUnit(tree, useStrictMode);
for (int i = 0; i != tree.getFunctionCount(); ++i) {
FunctionNode fn = tree.getFunctionNode(i);
transform(fn, useStrictMode, env);
}
}
use of org.mozilla.javascript.ast.FunctionNode in project HL4A by HL4A.
the class CodeGenerator method visitExpression.
private void visitExpression(Node node, int contextFlags) {
int type = node.getType();
Node child = node.getFirstChild();
int savedStackDepth = stackDepth;
switch(type) {
case Token.FUNCTION:
{
int fnIndex = node.getExistingIntProp(Node.FUNCTION_PROP);
FunctionNode fn = scriptOrFn.getFunctionNode(fnIndex);
// See comments in visitStatement for Token.FUNCTION case
if (fn.getFunctionType() != FunctionNode.FUNCTION_EXPRESSION && fn.getFunctionType() != FunctionNode.ARROW_FUNCTION) {
throw Kit.codeBug();
}
addIndexOp(Icode_CLOSURE_EXPR, fnIndex);
stackChange(1);
}
break;
case Token.LOCAL_LOAD:
{
int localIndex = getLocalBlockRef(node);
addIndexOp(Token.LOCAL_LOAD, localIndex);
stackChange(1);
}
break;
case Token.COMMA:
{
Node lastChild = node.getLastChild();
while (child != lastChild) {
visitExpression(child, 0);
addIcode(Icode_POP);
stackChange(-1);
child = child.getNext();
}
// Preserve tail context flag if any
visitExpression(child, contextFlags & ECF_TAIL);
}
break;
case Token.USE_STACK:
// Indicates that stack was modified externally,
// like placed catch object
stackChange(1);
break;
case Token.REF_CALL:
case Token.CALL:
case Token.NEW:
{
if (type == Token.NEW) {
visitExpression(child, 0);
} else {
generateCallFunAndThis(child);
}
int argCount = 0;
while ((child = child.getNext()) != null) {
visitExpression(child, 0);
++argCount;
}
int callType = node.getIntProp(Node.SPECIALCALL_PROP, Node.NON_SPECIALCALL);
if (type != Token.REF_CALL && callType != Node.NON_SPECIALCALL) {
// embed line number and source filename
addIndexOp(Icode_CALLSPECIAL, argCount);
addUint8(callType);
addUint8(type == Token.NEW ? 1 : 0);
addUint16(lineNumber & 0xFFFF);
} else {
// optimization will confuse the debugger)
if (type == Token.CALL && (contextFlags & ECF_TAIL) != 0 && !compilerEnv.isGenerateDebugInfo() && !itsInTryFlag) {
type = Icode_TAIL_CALL;
}
addIndexOp(type, argCount);
}
// adjust stack
if (type == Token.NEW) {
// new: f, args -> result
stackChange(-argCount);
} else {
// call: f, thisObj, args -> result
// ref_call: f, thisObj, args -> ref
stackChange(-1 - argCount);
}
if (argCount > itsData.itsMaxCalleeArgs) {
itsData.itsMaxCalleeArgs = argCount;
}
}
break;
case Token.AND:
case Token.OR:
{
visitExpression(child, 0);
addIcode(Icode_DUP);
stackChange(1);
int afterSecondJumpStart = iCodeTop;
int jump = (type == Token.AND) ? Token.IFNE : Token.IFEQ;
addGotoOp(jump);
stackChange(-1);
addIcode(Icode_POP);
stackChange(-1);
child = child.getNext();
// Preserve tail context flag if any
visitExpression(child, contextFlags & ECF_TAIL);
resolveForwardGoto(afterSecondJumpStart);
}
break;
case Token.HOOK:
{
Node ifThen = child.getNext();
Node ifElse = ifThen.getNext();
visitExpression(child, 0);
int elseJumpStart = iCodeTop;
addGotoOp(Token.IFNE);
stackChange(-1);
// Preserve tail context flag if any
visitExpression(ifThen, contextFlags & ECF_TAIL);
int afterElseJumpStart = iCodeTop;
addGotoOp(Token.GOTO);
resolveForwardGoto(elseJumpStart);
stackDepth = savedStackDepth;
// Preserve tail context flag if any
visitExpression(ifElse, contextFlags & ECF_TAIL);
resolveForwardGoto(afterElseJumpStart);
}
break;
case Token.GETPROP:
case Token.GETPROPNOWARN:
visitExpression(child, 0);
child = child.getNext();
addStringOp(type, child.getString());
break;
case Token.DELPROP:
boolean isName = child.getType() == Token.BINDNAME;
visitExpression(child, 0);
child = child.getNext();
visitExpression(child, 0);
if (isName) {
// special handling for delete name
addIcode(Icode_DELNAME);
} else {
addToken(Token.DELPROP);
}
stackChange(-1);
break;
case Token.GETELEM:
case Token.BITAND:
case Token.BITOR:
case Token.BITXOR:
case Token.LSH:
case Token.RSH:
case Token.URSH:
case Token.ADD:
case Token.SUB:
case Token.MOD:
case Token.DIV:
case Token.MUL:
case Token.EQ:
case Token.NE:
case Token.SHEQ:
case Token.SHNE:
case Token.IN:
case Token.INSTANCEOF:
case Token.LE:
case Token.LT:
case Token.GE:
case Token.GT:
visitExpression(child, 0);
child = child.getNext();
visitExpression(child, 0);
addToken(type);
stackChange(-1);
break;
case Token.POS:
case Token.NEG:
case Token.NOT:
case Token.BITNOT:
case Token.TYPEOF:
case Token.VOID:
visitExpression(child, 0);
if (type == Token.VOID) {
addIcode(Icode_POP);
addIcode(Icode_UNDEF);
} else {
addToken(type);
}
break;
case Token.GET_REF:
case Token.DEL_REF:
visitExpression(child, 0);
addToken(type);
break;
case Token.SETPROP:
case Token.SETPROP_OP:
{
visitExpression(child, 0);
child = child.getNext();
String property = child.getString();
child = child.getNext();
if (type == Token.SETPROP_OP) {
addIcode(Icode_DUP);
stackChange(1);
addStringOp(Token.GETPROP, property);
// Compensate for the following USE_STACK
stackChange(-1);
}
visitExpression(child, 0);
addStringOp(Token.SETPROP, property);
stackChange(-1);
}
break;
case Token.SETELEM:
case Token.SETELEM_OP:
visitExpression(child, 0);
child = child.getNext();
visitExpression(child, 0);
child = child.getNext();
if (type == Token.SETELEM_OP) {
addIcode(Icode_DUP2);
stackChange(2);
addToken(Token.GETELEM);
stackChange(-1);
// Compensate for the following USE_STACK
stackChange(-1);
}
visitExpression(child, 0);
addToken(Token.SETELEM);
stackChange(-2);
break;
case Token.SET_REF:
case Token.SET_REF_OP:
visitExpression(child, 0);
child = child.getNext();
if (type == Token.SET_REF_OP) {
addIcode(Icode_DUP);
stackChange(1);
addToken(Token.GET_REF);
// Compensate for the following USE_STACK
stackChange(-1);
}
visitExpression(child, 0);
addToken(Token.SET_REF);
stackChange(-1);
break;
case Token.STRICT_SETNAME:
case Token.SETNAME:
{
String name = child.getString();
visitExpression(child, 0);
child = child.getNext();
visitExpression(child, 0);
addStringOp(type, name);
stackChange(-1);
}
break;
case Token.SETCONST:
{
String name = child.getString();
visitExpression(child, 0);
child = child.getNext();
visitExpression(child, 0);
addStringOp(Icode_SETCONST, name);
stackChange(-1);
}
break;
case Token.TYPEOFNAME:
{
int index = -1;
// since the vars all exist there instead of in jregs
if (itsInFunctionFlag && !itsData.itsNeedsActivation)
index = scriptOrFn.getIndexForNameNode(node);
if (index == -1) {
addStringOp(Icode_TYPEOFNAME, node.getString());
stackChange(1);
} else {
addVarOp(Token.GETVAR, index);
stackChange(1);
addToken(Token.TYPEOF);
}
}
break;
case Token.BINDNAME:
case Token.NAME:
case Token.STRING:
addStringOp(type, node.getString());
stackChange(1);
break;
case Token.INC:
case Token.DEC:
visitIncDec(node, child);
break;
case Token.NUMBER:
{
double num = node.getDouble();
int inum = (int) num;
if (inum == num) {
if (inum == 0) {
addIcode(Icode_ZERO);
// Check for negative zero
if (1.0 / num < 0.0) {
addToken(Token.NEG);
}
} else if (inum == 1) {
addIcode(Icode_ONE);
} else if ((short) inum == inum) {
addIcode(Icode_SHORTNUMBER);
// write short as uin16 bit pattern
addUint16(inum & 0xFFFF);
} else {
addIcode(Icode_INTNUMBER);
addInt(inum);
}
} else {
int index = getDoubleIndex(num);
addIndexOp(Token.NUMBER, index);
}
stackChange(1);
}
break;
case Token.GETVAR:
{
if (itsData.itsNeedsActivation)
Kit.codeBug();
int index = scriptOrFn.getIndexForNameNode(node);
addVarOp(Token.GETVAR, index);
stackChange(1);
}
break;
case Token.SETVAR:
{
if (itsData.itsNeedsActivation)
Kit.codeBug();
int index = scriptOrFn.getIndexForNameNode(child);
child = child.getNext();
visitExpression(child, 0);
addVarOp(Token.SETVAR, index);
}
break;
case Token.SETCONSTVAR:
{
if (itsData.itsNeedsActivation)
Kit.codeBug();
int index = scriptOrFn.getIndexForNameNode(child);
child = child.getNext();
visitExpression(child, 0);
addVarOp(Token.SETCONSTVAR, index);
}
break;
case Token.NULL:
case Token.THIS:
case Token.THISFN:
case Token.FALSE:
case Token.TRUE:
addToken(type);
stackChange(1);
break;
case Token.ENUM_NEXT:
case Token.ENUM_ID:
addIndexOp(type, getLocalBlockRef(node));
stackChange(1);
break;
case Token.REGEXP:
{
int index = node.getExistingIntProp(Node.REGEXP_PROP);
addIndexOp(Token.REGEXP, index);
stackChange(1);
}
break;
case Token.ARRAYLIT:
case Token.OBJECTLIT:
visitLiteral(node, child);
break;
case Token.ARRAYCOMP:
visitArrayComprehension(node, child, child.getNext());
break;
case Token.REF_SPECIAL:
visitExpression(child, 0);
addStringOp(type, (String) node.getProp(Node.NAME_PROP));
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
int childCount = 0;
do {
visitExpression(child, 0);
++childCount;
child = child.getNext();
} while (child != null);
addIndexOp(type, memberTypeFlags);
stackChange(1 - childCount);
}
break;
case Token.DOTQUERY:
{
int queryPC;
updateLineNumber(node);
visitExpression(child, 0);
addIcode(Icode_ENTERDQ);
stackChange(-1);
queryPC = iCodeTop;
visitExpression(child.getNext(), 0);
addBackwardGoto(Icode_LEAVEDQ, queryPC);
}
break;
case Token.DEFAULTNAMESPACE:
case Token.ESCXMLATTR:
case Token.ESCXMLTEXT:
visitExpression(child, 0);
addToken(type);
break;
case Token.YIELD:
if (child != null) {
visitExpression(child, 0);
} else {
addIcode(Icode_UNDEF);
stackChange(1);
}
addToken(Token.YIELD);
addUint16(node.getLineno() & 0xFFFF);
break;
case Token.WITHEXPR:
{
Node enterWith = node.getFirstChild();
Node with = enterWith.getNext();
visitExpression(enterWith.getFirstChild(), 0);
addToken(Token.ENTERWITH);
stackChange(-1);
visitExpression(with.getFirstChild(), 0);
addToken(Token.LEAVEWITH);
break;
}
default:
throw badTree(node);
}
if (savedStackDepth + 1 != stackDepth) {
Kit.codeBug();
}
}
use of org.mozilla.javascript.ast.FunctionNode in project hackpad by dropbox.
the class BodyCodegen method initOptFunctions_r.
private static void initOptFunctions_r(ScriptNode scriptOrFn) {
for (int i = 0, N = scriptOrFn.getFunctionCount(); i != N; ++i) {
FunctionNode fn = scriptOrFn.getFunctionNode(i);
new OptFunctionNode(fn);
initOptFunctions_r(fn);
}
}
use of org.mozilla.javascript.ast.FunctionNode in project hackpad by dropbox.
the class BodyCodegen method generateNativeFunctionOverrides.
private void generateNativeFunctionOverrides(ClassFileWriter cfw, String encodedSource) {
// Override NativeFunction.getLanguageVersion() with
// public int getLanguageVersion() { return <version-constant>; }
cfw.startMethod("getLanguageVersion", "()I", ClassFileWriter.ACC_PUBLIC);
cfw.addPush(compilerEnv.getLanguageVersion());
cfw.add(ByteCode.IRETURN);
// 1: this and no argument or locals
cfw.stopMethod((short) 1);
// The rest of NativeFunction overrides require specific code for each
// script/function id
final int Do_getFunctionName = 0;
final int Do_getParamCount = 1;
final int Do_getParamAndVarCount = 2;
final int Do_getParamOrVarName = 3;
final int Do_getEncodedSource = 4;
final int Do_getParamOrVarConst = 5;
final int SWITCH_COUNT = 6;
for (int methodIndex = 0; methodIndex != SWITCH_COUNT; ++methodIndex) {
if (methodIndex == Do_getEncodedSource && encodedSource == null) {
continue;
}
// Generate:
// prologue;
// switch over function id to implement function-specific action
// epilogue
short methodLocals;
switch(methodIndex) {
case Do_getFunctionName:
// Only this
methodLocals = 1;
cfw.startMethod("getFunctionName", "()Ljava/lang/String;", ClassFileWriter.ACC_PUBLIC);
break;
case Do_getParamCount:
// Only this
methodLocals = 1;
cfw.startMethod("getParamCount", "()I", ClassFileWriter.ACC_PUBLIC);
break;
case Do_getParamAndVarCount:
// Only this
methodLocals = 1;
cfw.startMethod("getParamAndVarCount", "()I", ClassFileWriter.ACC_PUBLIC);
break;
case Do_getParamOrVarName:
// this + paramOrVarIndex
methodLocals = 1 + 1;
cfw.startMethod("getParamOrVarName", "(I)Ljava/lang/String;", ClassFileWriter.ACC_PUBLIC);
break;
case Do_getParamOrVarConst:
// this + paramOrVarName
methodLocals = 1 + 1 + 1;
cfw.startMethod("getParamOrVarConst", "(I)Z", ClassFileWriter.ACC_PUBLIC);
break;
case Do_getEncodedSource:
// Only this
methodLocals = 1;
cfw.startMethod("getEncodedSource", "()Ljava/lang/String;", ClassFileWriter.ACC_PUBLIC);
cfw.addPush(encodedSource);
break;
default:
throw Kit.codeBug();
}
int count = scriptOrFnNodes.length;
int switchStart = 0;
int switchStackTop = 0;
if (count > 1) {
// Generate switch but only if there is more then one
// script/function
cfw.addLoadThis();
cfw.add(ByteCode.GETFIELD, cfw.getClassName(), ID_FIELD_NAME, "I");
// do switch from 1 .. count - 1 mapping 0 to the default case
switchStart = cfw.addTableSwitch(1, count - 1);
}
for (int i = 0; i != count; ++i) {
ScriptNode n = scriptOrFnNodes[i];
if (i == 0) {
if (count > 1) {
cfw.markTableSwitchDefault(switchStart);
switchStackTop = cfw.getStackTop();
}
} else {
cfw.markTableSwitchCase(switchStart, i - 1, switchStackTop);
}
// Impelemnet method-specific switch code
switch(methodIndex) {
case Do_getFunctionName:
// Push function name
if (n.getType() == Token.SCRIPT) {
cfw.addPush("");
} else {
String name = ((FunctionNode) n).getName();
cfw.addPush(name);
}
cfw.add(ByteCode.ARETURN);
break;
case Do_getParamCount:
// Push number of defined parameters
cfw.addPush(n.getParamCount());
cfw.add(ByteCode.IRETURN);
break;
case Do_getParamAndVarCount:
// Push number of defined parameters and declared variables
cfw.addPush(n.getParamAndVarCount());
cfw.add(ByteCode.IRETURN);
break;
case Do_getParamOrVarName:
// Push name of parameter using another switch
// over paramAndVarCount
int paramAndVarCount = n.getParamAndVarCount();
if (paramAndVarCount == 0) {
// The runtime should never call the method in this
// case but to make bytecode verifier happy return null
// as throwing execption takes more code
cfw.add(ByteCode.ACONST_NULL);
cfw.add(ByteCode.ARETURN);
} else if (paramAndVarCount == 1) {
// As above do not check for valid index but always
// return the name of the first param
cfw.addPush(n.getParamOrVarName(0));
cfw.add(ByteCode.ARETURN);
} else {
// Do switch over getParamOrVarName
// param or var index
cfw.addILoad(1);
// do switch from 1 .. paramAndVarCount - 1 mapping 0
// to the default case
int paramSwitchStart = cfw.addTableSwitch(1, paramAndVarCount - 1);
for (int j = 0; j != paramAndVarCount; ++j) {
if (cfw.getStackTop() != 0)
Kit.codeBug();
String s = n.getParamOrVarName(j);
if (j == 0) {
cfw.markTableSwitchDefault(paramSwitchStart);
} else {
cfw.markTableSwitchCase(paramSwitchStart, j - 1, 0);
}
cfw.addPush(s);
cfw.add(ByteCode.ARETURN);
}
}
break;
case Do_getParamOrVarConst:
// Push name of parameter using another switch
// over paramAndVarCount
paramAndVarCount = n.getParamAndVarCount();
boolean[] constness = n.getParamAndVarConst();
if (paramAndVarCount == 0) {
// The runtime should never call the method in this
// case but to make bytecode verifier happy return null
// as throwing execption takes more code
cfw.add(ByteCode.ICONST_0);
cfw.add(ByteCode.IRETURN);
} else if (paramAndVarCount == 1) {
// As above do not check for valid index but always
// return the name of the first param
cfw.addPush(constness[0]);
cfw.add(ByteCode.IRETURN);
} else {
// Do switch over getParamOrVarName
// param or var index
cfw.addILoad(1);
// do switch from 1 .. paramAndVarCount - 1 mapping 0
// to the default case
int paramSwitchStart = cfw.addTableSwitch(1, paramAndVarCount - 1);
for (int j = 0; j != paramAndVarCount; ++j) {
if (cfw.getStackTop() != 0)
Kit.codeBug();
if (j == 0) {
cfw.markTableSwitchDefault(paramSwitchStart);
} else {
cfw.markTableSwitchCase(paramSwitchStart, j - 1, 0);
}
cfw.addPush(constness[j]);
cfw.add(ByteCode.IRETURN);
}
}
break;
case Do_getEncodedSource:
// Push number encoded source start and end
// to prepare for encodedSource.substring(start, end)
cfw.addPush(n.getEncodedSourceStart());
cfw.addPush(n.getEncodedSourceEnd());
cfw.addInvoke(ByteCode.INVOKEVIRTUAL, "java/lang/String", "substring", "(II)Ljava/lang/String;");
cfw.add(ByteCode.ARETURN);
break;
default:
throw Kit.codeBug();
}
}
cfw.stopMethod(methodLocals);
}
}
use of org.mozilla.javascript.ast.FunctionNode in project hackpad by dropbox.
the class BodyCodegen method generateEpilogue.
private void generateEpilogue() {
if (compilerEnv.isGenerateObserverCount())
addInstructionCount();
if (isGenerator) {
// generate locals initialization
Map<Node, int[]> liveLocals = ((FunctionNode) scriptOrFn).getLiveLocals();
if (liveLocals != null) {
List<Node> nodes = ((FunctionNode) scriptOrFn).getResumptionPoints();
for (int i = 0; i < nodes.size(); i++) {
Node node = nodes.get(i);
int[] live = liveLocals.get(node);
if (live != null) {
cfw.markTableSwitchCase(generatorSwitch, getNextGeneratorState(node));
generateGetGeneratorLocalsState();
for (int j = 0; j < live.length; j++) {
cfw.add(ByteCode.DUP);
cfw.addLoadConstant(j);
cfw.add(ByteCode.AALOAD);
cfw.addAStore(live[j]);
}
cfw.add(ByteCode.POP);
cfw.add(ByteCode.GOTO, getTargetLabel(node));
}
}
}
// generate dispatch tables for finally
if (finallys != null) {
for (Node n : finallys.keySet()) {
if (n.getType() == Token.FINALLY) {
FinallyReturnPoint ret = finallys.get(n);
// the finally will jump here
cfw.markLabel(ret.tableLabel, (short) 1);
// start generating a dispatch table
int startSwitch = cfw.addTableSwitch(0, ret.jsrPoints.size() - 1);
int c = 0;
cfw.markTableSwitchDefault(startSwitch);
for (int i = 0; i < ret.jsrPoints.size(); i++) {
// generate gotos back to the JSR location
cfw.markTableSwitchCase(startSwitch, c);
cfw.add(ByteCode.GOTO, ret.jsrPoints.get(i).intValue());
c++;
}
}
}
}
}
if (epilogueLabel != -1) {
cfw.markLabel(epilogueLabel);
}
if (hasVarsInRegs) {
cfw.add(ByteCode.ARETURN);
return;
} else if (isGenerator) {
if (((FunctionNode) scriptOrFn).getResumptionPoints() != null) {
cfw.markTableSwitchDefault(generatorSwitch);
}
// change state for re-entry
generateSetGeneratorResumptionPoint(GENERATOR_TERMINATE);
// throw StopIteration
cfw.addALoad(variableObjectLocal);
addOptRuntimeInvoke("throwStopIteration", "(Ljava/lang/Object;)V");
Codegen.pushUndefined(cfw);
cfw.add(ByteCode.ARETURN);
} else if (fnCurrent == null) {
cfw.addALoad(popvLocal);
cfw.add(ByteCode.ARETURN);
} else {
generateActivationExit();
cfw.add(ByteCode.ARETURN);
// Generate catch block to catch all and rethrow to call exit code
// under exception propagation as well.
int finallyHandler = cfw.acquireLabel();
cfw.markHandler(finallyHandler);
short exceptionObject = getNewWordLocal();
cfw.addAStore(exceptionObject);
// Duplicate generateActivationExit() in the catch block since it
// takes less space then full-featured ByteCode.JSR/ByteCode.RET
generateActivationExit();
cfw.addALoad(exceptionObject);
releaseWordLocal(exceptionObject);
// rethrow
cfw.add(ByteCode.ATHROW);
// mark the handler
cfw.addExceptionHandler(enterAreaStartLabel, epilogueLabel, finallyHandler, // catch any
null);
}
}
Aggregations