use of org.mozilla.javascript.ast.ScriptNode in project HL4A by HL4A.
the class BodyCodegen method generateCode.
private byte[] generateCode(String encodedSource) {
boolean hasScript = (scriptOrFnNodes[0].getType() == Token.SCRIPT);
boolean hasFunctions = (scriptOrFnNodes.length > 1 || !hasScript);
boolean isStrictMode = scriptOrFnNodes[0].isInStrictMode();
String sourceFile = null;
if (compilerEnv.isGenerateDebugInfo()) {
sourceFile = scriptOrFnNodes[0].getSourceName();
}
ClassFileWriter cfw = new ClassFileWriter(mainClassName, SUPER_CLASS_NAME, sourceFile);
cfw.addField(ID_FIELD_NAME, "I", ACC_PRIVATE);
if (hasFunctions) {
generateFunctionConstructor(cfw);
}
if (hasScript) {
cfw.addInterface("org/mozilla/javascript/Script");
generateScriptCtor(cfw);
generateMain(cfw);
generateExecute(cfw);
}
generateCallMethod(cfw, isStrictMode);
generateResumeGenerator(cfw);
generateNativeFunctionOverrides(cfw, encodedSource);
int count = scriptOrFnNodes.length;
for (int i = 0; i != count; ++i) {
ScriptNode n = scriptOrFnNodes[i];
BodyCodegen bodygen = new BodyCodegen();
bodygen.cfw = cfw;
bodygen.codegen = this;
bodygen.compilerEnv = compilerEnv;
bodygen.scriptOrFn = n;
bodygen.scriptOrFnIndex = i;
try {
bodygen.generateBodyCode();
} catch (ClassFileWriter.ClassFileFormatException e) {
throw reportClassFileFormatException(n, e.getMessage());
}
if (n.getType() == Token.FUNCTION) {
OptFunctionNode ofn = OptFunctionNode.get(n);
generateFunctionInit(cfw, ofn);
if (ofn.isTargetOfDirectCall()) {
emitDirectConstructor(cfw, ofn);
}
}
}
emitRegExpInit(cfw);
emitConstantDudeInitializers(cfw);
return cfw.toByteArray();
}
use of org.mozilla.javascript.ast.ScriptNode in project HL4A by HL4A.
the class BodyCodegen method generateCallMethod.
private void generateCallMethod(ClassFileWriter cfw, boolean isStrictMode) {
cfw.startMethod("call", "(Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + "Lorg/mozilla/javascript/Scriptable;" + "[Ljava/lang/Object;)Ljava/lang/Object;", (short) (ACC_PUBLIC | ACC_FINAL));
// Generate code for:
// if (!ScriptRuntime.hasTopCall(cx)) {
// return ScriptRuntime.doTopCall(this, cx, scope, thisObj, args);
// }
int nonTopCallLabel = cfw.acquireLabel();
// cx
cfw.addALoad(1);
cfw.addInvoke(ByteCode.INVOKESTATIC, "org/mozilla/javascript/ScriptRuntime", "hasTopCall", "(Lorg/mozilla/javascript/Context;" + ")Z");
cfw.add(ByteCode.IFNE, nonTopCallLabel);
cfw.addALoad(0);
cfw.addALoad(1);
cfw.addALoad(2);
cfw.addALoad(3);
cfw.addALoad(4);
cfw.addPush(isStrictMode);
cfw.addInvoke(ByteCode.INVOKESTATIC, "org/mozilla/javascript/ScriptRuntime", "doTopCall", "(Lorg/mozilla/javascript/Callable;" + "Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + "Lorg/mozilla/javascript/Scriptable;" + "[Ljava/lang/Object;" + "Z" + ")Ljava/lang/Object;");
cfw.add(ByteCode.ARETURN);
cfw.markLabel(nonTopCallLabel);
// Now generate switch to call the real methods
cfw.addALoad(0);
cfw.addALoad(1);
cfw.addALoad(2);
cfw.addALoad(3);
cfw.addALoad(4);
int end = scriptOrFnNodes.length;
boolean generateSwitch = (2 <= end);
int switchStart = 0;
int switchStackTop = 0;
if (generateSwitch) {
cfw.addLoadThis();
cfw.add(ByteCode.GETFIELD, cfw.getClassName(), ID_FIELD_NAME, "I");
// do switch from (1, end - 1) mapping 0 to
// the default case
switchStart = cfw.addTableSwitch(1, end - 1);
}
for (int i = 0; i != end; ++i) {
ScriptNode n = scriptOrFnNodes[i];
if (generateSwitch) {
if (i == 0) {
cfw.markTableSwitchDefault(switchStart);
switchStackTop = cfw.getStackTop();
} else {
cfw.markTableSwitchCase(switchStart, i - 1, switchStackTop);
}
}
if (n.getType() == Token.FUNCTION) {
OptFunctionNode ofn = OptFunctionNode.get(n);
if (ofn.isTargetOfDirectCall()) {
int pcount = ofn.fnode.getParamCount();
if (pcount != 0) {
// stack top == arguments array from addALoad4()
for (int p = 0; p != pcount; ++p) {
cfw.add(ByteCode.ARRAYLENGTH);
cfw.addPush(p);
int undefArg = cfw.acquireLabel();
int beyond = cfw.acquireLabel();
cfw.add(ByteCode.IF_ICMPLE, undefArg);
// get array[p]
cfw.addALoad(4);
cfw.addPush(p);
cfw.add(ByteCode.AALOAD);
cfw.add(ByteCode.GOTO, beyond);
cfw.markLabel(undefArg);
pushUndefined(cfw);
cfw.markLabel(beyond);
// Only one push
cfw.adjustStackTop(-1);
cfw.addPush(0.0);
// restore invariant
cfw.addALoad(4);
}
}
}
}
cfw.addInvoke(ByteCode.INVOKESTATIC, mainClassName, getBodyMethodName(n), getBodyMethodSignature(n));
cfw.add(ByteCode.ARETURN);
}
cfw.stopMethod((short) 5);
// 5: this, cx, scope, js this, args[]
}
use of org.mozilla.javascript.ast.ScriptNode in project HL4A by HL4A.
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", 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;", ACC_PUBLIC);
break;
case Do_getParamCount:
// Only this
methodLocals = 1;
cfw.startMethod("getParamCount", "()I", ACC_PUBLIC);
break;
case Do_getParamAndVarCount:
// Only this
methodLocals = 1;
cfw.startMethod("getParamAndVarCount", "()I", ACC_PUBLIC);
break;
case Do_getParamOrVarName:
// this + paramOrVarIndex
methodLocals = 1 + 1;
cfw.startMethod("getParamOrVarName", "(I)Ljava/lang/String;", ACC_PUBLIC);
break;
case Do_getParamOrVarConst:
// this + paramOrVarName
methodLocals = 1 + 1 + 1;
cfw.startMethod("getParamOrVarConst", "(I)Z", ACC_PUBLIC);
break;
case Do_getEncodedSource:
// Only this
methodLocals = 1;
cfw.startMethod("getEncodedSource", "()Ljava/lang/String;", 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.ScriptNode in project HL4A by HL4A.
the class ClassCompiler method compileToClassFiles.
/**
* Compile JavaScript source into one or more Java class files.
* The first compiled class will have name mainClassName.
* If the results of {@link #getTargetExtends()} or
* {@link #getTargetImplements()} are not null, then the first compiled
* class will extend the specified super class and implement
* specified interfaces.
*
* @return array where elements with even indexes specifies class name
* and the following odd index gives class file body as byte[]
* array. The initial element of the array always holds
* mainClassName and array[1] holds its byte code.
*/
public Object[] compileToClassFiles(String source, String sourceLocation, int lineno, String mainClassName) {
Parser p = new Parser(compilerEnv);
AstRoot ast = p.parse(source, sourceLocation, lineno);
IRFactory irf = new IRFactory(compilerEnv);
ScriptNode tree = irf.transformTree(ast);
// release reference to original parse tree & parser
irf = null;
ast = null;
p = null;
Class<?> superClass = getTargetExtends();
Class<?>[] interfaces = getTargetImplements();
String scriptClassName;
boolean isPrimary = (interfaces == null && superClass == null);
if (isPrimary) {
scriptClassName = mainClassName;
} else {
scriptClassName = makeAuxiliaryClassName(mainClassName, "1");
}
Codegen codegen = new Codegen();
codegen.setMainMethodClass(mainMethodClassName);
byte[] scriptClassBytes = codegen.compileToClassFile(compilerEnv, scriptClassName, tree, tree.getEncodedSource(), false);
if (isPrimary) {
return new Object[] { scriptClassName, scriptClassBytes };
}
int functionCount = tree.getFunctionCount();
ObjToIntMap functionNames = new ObjToIntMap(functionCount);
for (int i = 0; i != functionCount; ++i) {
FunctionNode ofn = tree.getFunctionNode(i);
String name = ofn.getName();
if (name != null && name.length() != 0) {
functionNames.put(name, ofn.getParamCount());
}
}
if (superClass == null) {
superClass = ScriptRuntime.ObjectClass;
}
byte[] mainClassBytes = JavaAdapter.createAdapterCode(functionNames, mainClassName, superClass, interfaces, scriptClassName);
return new Object[] { mainClassName, mainClassBytes, scriptClassName, scriptClassBytes };
}
use of org.mozilla.javascript.ast.ScriptNode in project HL4A by HL4A.
the class Context method compileImpl.
private Object compileImpl(Scriptable scope, Reader sourceReader, String sourceString, String sourceName, int lineno, Object securityDomain, boolean returnFunction, Evaluator compiler, ErrorReporter compilationErrorReporter) throws IOException {
if (sourceName == null) {
sourceName = "未命名 代码";
}
if (securityDomain != null && getSecurityController() == null) {
throw new IllegalArgumentException("securityDomain should be null if setSecurityController() was never called");
}
// One of sourceReader or sourceString has to be null
if (!(sourceReader == null ^ sourceString == null))
Kit.codeBug();
// scope should be given if and only if compiling function
if (!(scope == null ^ returnFunction))
Kit.codeBug();
CompilerEnvirons compilerEnv = new CompilerEnvirons();
compilerEnv.initFromContext(this);
if (compilationErrorReporter == null) {
compilationErrorReporter = compilerEnv.getErrorReporter();
}
if (debugger != null) {
if (sourceReader != null) {
sourceString = Kit.readReader(sourceReader);
sourceReader = null;
}
}
Parser p = new Parser(compilerEnv, compilationErrorReporter);
if (returnFunction) {
p.calledByCompileFunction = true;
}
if (isStrictMode()) {
p.setDefaultUseStrictDirective(true);
}
AstRoot ast;
if (sourceString != null) {
ast = p.parse(sourceString, sourceName, lineno);
} else {
ast = p.parse(sourceReader, sourceName, lineno);
}
if (returnFunction) {
// parser no longer adds function to script node
if (!(ast.getFirstChild() != null && ast.getFirstChild().getType() == Token.FUNCTION)) {
// with sources like function() {};;;
throw new IllegalArgumentException("compileFunction only accepts source with single JS function: " + sourceString);
}
}
IRFactory irf = new IRFactory(compilerEnv, compilationErrorReporter);
ScriptNode tree = irf.transformTree(ast);
// discard everything but the IR tree
p = null;
ast = null;
irf = null;
if (compiler == null) {
compiler = createCompiler();
}
Object bytecode = compiler.compile(compilerEnv, tree, tree.getEncodedSource(), returnFunction);
if (debugger != null) {
if (sourceString == null)
Kit.codeBug();
if (bytecode instanceof DebuggableScript) {
DebuggableScript dscript = (DebuggableScript) bytecode;
notifyDebugger_r(this, dscript, sourceString);
} else {
throw new RuntimeException("NOT SUPPORTED");
}
}
Object result;
if (returnFunction) {
result = compiler.createFunctionObject(this, scope, bytecode, securityDomain);
} else {
result = compiler.createScriptObject(bytecode, securityDomain);
}
return result;
}
Aggregations