optimizer.ClassCompiler provides new simple API for generating of class files from scripts that does not require to create Context instances.

The jsc tool is updated to use the new interface instead of using ClassNamehelper and friends.
This commit is contained in:
igor%mir2.org 2004-01-17 21:02:32 +00:00
Родитель 0dad9eb69c
Коммит 9ab50b8455
11 изменённых файлов: 630 добавлений и 328 удалений

Просмотреть файл

@ -38,11 +38,27 @@
package org.mozilla.javascript;
import java.util.Hashtable;
public class CompilerEnvirons
{
public void initFromContext(Context cx, ErrorReporter syntaxErrorReporter)
public CompilerEnvirons()
{
setSyntaxErrorReporter(syntaxErrorReporter);
this.errorReporter = DefaultErrorReporter.instance;
this.syntaxErrorCount = 0;
this.fromEval = false;
this.languageVersion = Context.VERSION_DEFAULT;
this.generateDebugInfo = true;
this.useDynamicScope = false;
this.reservedKeywordAsIdentifier = false;
this.allowMemberExprAsFunctionName = false;
this.optimizationLevel = 0;
this.generatingSource = true;
}
public void initFromContext(Context cx)
{
setErrorReporter(cx.getErrorReporter());
this.languageVersion = cx.getLanguageVersion();
useDynamicScope = cx.hasCompileFunctionsWithDynamicScope();
generateDebugInfo = (!cx.isGeneratingDebugChanged()
@ -55,6 +71,7 @@ public class CompilerEnvirons
this.optimizationLevel = cx.getOptimizationLevel();
this.generatingSource = cx.isGeneratingSource();
this.activationNames = cx.activationNames;
}
public final int getSyntaxErrorCount()
@ -62,38 +79,41 @@ public class CompilerEnvirons
return syntaxErrorCount;
}
public void setSyntaxErrorReporter(ErrorReporter syntaxErrorReporter)
public final ErrorReporter getErrorReporter()
{
this.syntaxErrorReporter = syntaxErrorReporter;
return errorReporter;
}
public final void reportSyntaxError(boolean isError,
String message,
public void setErrorReporter(ErrorReporter errorReporter)
{
if (errorReporter == null) throw new IllegalArgumentException();
this.errorReporter = errorReporter;
}
public final void reportSyntaxError(String message,
String sourceName, int lineno,
String lineText, int lineOffset)
{
if (isError) {
++syntaxErrorCount;
if (fromEval) {
// We're probably in an eval. Need to throw an exception.
throw ScriptRuntime.constructError(
"SyntaxError", message, sourceName,
lineno, lineText, lineOffset);
} else if (syntaxErrorReporter != null) {
syntaxErrorReporter.error(message, sourceName, lineno,
lineText, lineOffset);
} else {
throw new EvaluatorException(message, sourceName, lineno,
lineText, lineOffset);
}
++syntaxErrorCount;
if (fromEval) {
// We're probably in an eval. Need to throw an exception.
throw ScriptRuntime.constructError(
"SyntaxError", message, sourceName,
lineno, lineText, lineOffset);
} else {
if (syntaxErrorReporter != null) {
syntaxErrorReporter.warning(message, sourceName,
lineno, lineText, lineOffset);
}
getErrorReporter().error(message, sourceName, lineno,
lineText, lineOffset);
}
}
public final void reportSyntaxWarning(String message,
String sourceName, int lineno,
String lineText, int lineOffset)
{
getErrorReporter().warning(message, sourceName,
lineno, lineText, lineOffset);
}
public final boolean isFromEval()
{
return fromEval;
@ -159,7 +179,6 @@ public class CompilerEnvirons
* the body of the function.
* Note that code generated without source is not fully ECMA
* conformant.
* @since 1.3
*/
public void setGeneratingSource(boolean generatingSource)
{
@ -167,17 +186,18 @@ public class CompilerEnvirons
}
private ErrorReporter syntaxErrorReporter;
private ErrorReporter errorReporter;
private int syntaxErrorCount;
private boolean fromEval;
int languageVersion = Context.VERSION_DEFAULT;
boolean generateDebugInfo;
boolean generateDebugInfo = true;
boolean useDynamicScope;
boolean reservedKeywordAsIdentifier;
boolean allowMemberExprAsFunctionName;
private int optimizationLevel;
private boolean generatingSource;
private int optimizationLevel = 0;
private boolean generatingSource = true;
Hashtable activationNames;
}

Просмотреть файл

@ -521,9 +521,10 @@ public class Context
*
* @see org.mozilla.javascript.ErrorReporter
*/
public ErrorReporter getErrorReporter() {
public final ErrorReporter getErrorReporter()
{
if (errorReporter == null) {
errorReporter = new DefaultErrorReporter();
return DefaultErrorReporter.instance;
}
return errorReporter;
}
@ -534,15 +535,20 @@ public class Context
* @return the previous error reporter
* @see org.mozilla.javascript.ErrorReporter
*/
public ErrorReporter setErrorReporter(ErrorReporter reporter) {
ErrorReporter result = errorReporter;
Object listeners = propertyListeners;
if (listeners != null && errorReporter != reporter) {
firePropertyChangeImpl(listeners, errorReporterProperty,
errorReporter, reporter);
public ErrorReporter setErrorReporter(ErrorReporter reporter)
{
if (reporter == null) throw new IllegalArgumentException();
ErrorReporter old = getErrorReporter();
if (reporter == old) {
return old;
}
errorReporter = reporter;
return result;
Object listeners = propertyListeners;
if (listeners != null) {
firePropertyChangeImpl(listeners, errorReporterProperty,
old, reporter);
}
this.errorReporter = reporter;
return old;
}
/**
@ -707,10 +713,10 @@ public class Context
* @see org.mozilla.javascript.ErrorReporter
*/
public static EvaluatorException reportRuntimeError(String message,
String sourceName,
int lineno,
String lineSource,
int lineOffset)
String sourceName,
int lineno,
String lineSource,
int lineOffset)
{
Context cx = getCurrentContext();
if (cx != null) {
@ -996,25 +1002,21 @@ public class Context
{
boolean errorseen = false;
CompilerEnvirons compilerEnv = new CompilerEnvirons();
compilerEnv.initFromContext(this, null);
compilerEnv.initFromContext(this);
compilerEnv.setErrorReporter(DefaultErrorReporter.instance);
// no source name or source text manager, because we're just
// going to throw away the result.
TokenStream ts = new TokenStream(compilerEnv,
null, source, null, 1);
Parser p = new Parser();
Decompiler decompiler = new Decompiler();
compilerEnv.setGeneratingSource(false);
Parser p = new Parser(compilerEnv);
try {
p.parse(ts, decompiler);
} catch (IOException ioe) {
errorseen = true;
p.parse(source, null, 1);
} catch (EvaluatorException ee) {
errorseen = true;
}
// Return false only if an error occurred as a result of reading past
// the end of the file, i.e. if the source could be fixed by
// appending more source.
if (errorseen && ts.eof())
if (errorseen && p.eof())
return false;
else
return true;
@ -2069,8 +2071,7 @@ public class Context
if (!(scope == null ^ returnFunction)) Kit.codeBug();
CompilerEnvirons compilerEnv = new CompilerEnvirons();
ErrorReporter reporter = getErrorReporter();
compilerEnv.initFromContext(this, reporter);
compilerEnv.initFromContext(this);
compilerEnv.setFromEval(fromEval);
if (debugger != null) {
@ -2080,12 +2081,13 @@ public class Context
}
}
TokenStream ts = new TokenStream(compilerEnv,
sourceReader, sourceString,
sourceName, lineno);
Parser p = new Parser();
Decompiler decompiler = new Decompiler();
ScriptOrFnNode tree = p.parse(ts, decompiler);
Parser p = new Parser(compilerEnv);
ScriptOrFnNode tree;
if (sourceString != null) {
tree = p.parse(sourceString, sourceName, lineno);
} else {
tree = p.parse(sourceReader, sourceName, lineno);
}
int syntaxErrorCount = compilerEnv.getSyntaxErrorCount();
if (syntaxErrorCount == 0) {
Interpreter compiler = createCompiler();
@ -2096,11 +2098,7 @@ public class Context
securityDomain = null;
}
String encodedSource = null;
if (compilerEnv.isGeneratingSource()) {
encodedSource = decompiler.getEncodedSource();
}
decompiler = null; // It helps GC
String encodedSource = p.getEncodedSource();
Object result = compiler.compile(this, scope, compilerEnv,
tree, encodedSource,
@ -2119,11 +2117,12 @@ public class Context
}
String msg = Context.getMessage1("msg.got.syntax.errors",
String.valueOf(syntaxErrorCount));
throw reporter.runtimeError(msg, sourceName, lineno, null, 0);
throw compilerEnv.getErrorReporter().
runtimeError(msg, sourceName, lineno, null, 0);
}
private static Class codegenClass = Kit.classOrNull(
"org.mozilla.javascript.optimizer.ClassCompiler");
"org.mozilla.javascript.optimizer.Codegen");
private Interpreter createCompiler() {
Interpreter result = null;
@ -2237,8 +2236,6 @@ public class Context
* @return true if an function activation object is needed.
*/
public boolean isActivationNeeded(String name) {
if ("arguments".equals(name))
return true;
return activationNames != null && activationNames.containsKey(name);
}
@ -2330,7 +2327,7 @@ public class Context
* This is the list of names of objects forcing the creation of
* function activation records.
*/
private Hashtable activationNames;
Hashtable activationNames;
// For the interpreter to indicate line/source for error reports.
int interpreterLineIndex;

Просмотреть файл

@ -40,7 +40,9 @@ package org.mozilla.javascript;
*
* @author Norris Boyd
*/
class DefaultErrorReporter implements ErrorReporter {
class DefaultErrorReporter implements ErrorReporter
{
static final DefaultErrorReporter instance = new DefaultErrorReporter();
public void warning(String message, String sourceName, int line,
String lineSource, int lineOffset)

Просмотреть файл

@ -396,8 +396,7 @@ public class NodeTransformer
if (bind == null || bind.getType() != Token.BINDNAME)
break;
String name = bind.getString();
Context cx = Context.getCurrentContext();
if (cx != null && cx.isActivationNeeded(name)) {
if (isActivationNeeded(name)) {
// use of "arguments" requires an activation object.
((FunctionNode) tree).setRequiresActivation(true);
}
@ -418,10 +417,10 @@ public class NodeTransformer
if (inFunction) {
Node n = node.getFirstChild().getNext();
String name = n == null ? "" : n.getString();
Context cx = Context.getCurrentContext();
if ((cx != null && cx.isActivationNeeded(name)) ||
(name.equals("length") &&
cx.getLanguageVersion() == Context.VERSION_1_2))
if (isActivationNeeded(name)
|| (name.equals("length")
&& compilerEnv.getLanguageVersion()
== Context.VERSION_1_2))
{
// Use of "arguments" or "length" in 1.2 requires
// an activation object.
@ -435,8 +434,7 @@ public class NodeTransformer
if (!inFunction || inWithStatement())
break;
String name = node.getString();
Context cx = Context.getCurrentContext();
if (cx != null && cx.isActivationNeeded(name)) {
if (isActivationNeeded(name)) {
// Use of "arguments" requires an activation object.
((FunctionNode) tree).setRequiresActivation(true);
}
@ -530,8 +528,19 @@ public class NodeTransformer
{
int lineno = stmt.getLineno();
String sourceName = tree.getSourceName();
compilerEnv.reportSyntaxError(
true, message, sourceName, lineno, null, 0);
compilerEnv.reportSyntaxError(message, sourceName, lineno, null, 0);
}
private boolean isActivationNeeded(String name)
{
if ("arguments".equals(name))
return true;
if (compilerEnv.activationNames != null
&& compilerEnv.activationNames.containsKey(name))
{
return true;
}
return false;
}
private ObjArray loops;

Просмотреть файл

@ -37,6 +37,7 @@
package org.mozilla.javascript;
import java.io.Reader;
import java.io.IOException;
/**
@ -51,7 +52,56 @@ import java.io.IOException;
* @author Brendan Eich
*/
class Parser {
public class Parser
{
public Parser(CompilerEnvirons compilerEnv)
{
this.compilerEnv = compilerEnv;
}
protected Decompiler createDecompiler(CompilerEnvirons compilerEnv)
{
return new Decompiler();
}
/*
* Build a parse tree from the given sourceString.
*
* @return an Object representing the parsed
* program. If the parse fails, null will be returned. (The
* parse failure will result in a call to the ErrorReporter from
* CompilerEnvirons.)
*/
public ScriptOrFnNode parse(String sourceString,
String sourceLocation, int lineno)
{
this.ts = new TokenStream(compilerEnv, null, sourceString,
sourceLocation, lineno);
try {
return parse();
} catch (IOException ex) {
// Should never happen
throw new IllegalStateException(ex.getMessage());
}
}
/*
* Build a parse tree from the given sourceString.
*
* @return an Object representing the parsed
* program. If the parse fails, null will be returned. (The
* parse failure will result in a call to the ErrorReporter from
* CompilerEnvirons.)
*/
public ScriptOrFnNode parse(Reader sourceReader,
String sourceLocation, int lineno)
throws IOException
{
this.ts = new TokenStream(compilerEnv, sourceReader, null,
sourceLocation, lineno);
return parse();
}
private void mustMatchToken(int toMatch, String messageId)
throws IOException, ParserException
@ -73,26 +123,15 @@ class Parser {
throw new ParserException();
}
/*
* Build a parse tree from the given TokenStream.
*
* @param ts the TokenStream to parse
* @param nf the node factory to use to build parse nodes
*
* @return an Object representing the parsed
* program. If the parse fails, null will be returned. (The
* parse failure will result in a call to the current Context's
* ErrorReporter.)
*/
public ScriptOrFnNode parse(TokenStream ts, Decompiler decompiler)
private ScriptOrFnNode parse()
throws IOException
{
this.compilerEnv = ts.compilerEnv;
this.ts = ts;
this.decompiler = createDecompiler(compilerEnv);
this.nf = new IRFactory(this);
currentScriptOrFn = nf.createScript();
this.decompiler = decompiler;
int sourceStartOffset = decompiler.getCurrentOffset();
this.encodedSource = null;
decompiler.addToken(Token.SCRIPT);
this.ok = true;
@ -148,9 +187,24 @@ class Parser {
nf.initScript(currentScriptOrFn, pn);
if (compilerEnv.isGeneratingSource()) {
encodedSource = decompiler.getEncodedSource();
}
this.decompiler = null; // It helps GC
return currentScriptOrFn;
}
public String getEncodedSource()
{
return encodedSource;
}
public boolean eof()
{
return ts.eof();
}
/*
* The C version of this function takes an argument list,
* which doesn't seem to be needed for tree generation...
@ -1473,7 +1527,7 @@ class Parser {
}
CompilerEnvirons compilerEnv;
TokenStream ts;
private TokenStream ts;
private IRFactory nf;
@ -1483,7 +1537,9 @@ class Parser {
private int nestingOfWith;
Decompiler decompiler;
private Decompiler decompiler;
private String encodedSource;
}
// Exception to unwind

Просмотреть файл

@ -2078,7 +2078,9 @@ public class ScriptRuntime {
// get the command line arguments and define "arguments"
// array in the top-level object
Scriptable argsObj = cx.newArray(global, args);
Object[] argsCopy = new Object[args.length];
System.arraycopy(args, 0, argsCopy, 0, args.length);
Scriptable argsObj = cx.newArray(global, argsCopy);
global.defineProperty("arguments", argsObj,
ScriptableObject.DONTENUM);
script.exec(cx, global);

Просмотреть файл

@ -294,16 +294,14 @@ public class TokenStream
public final void reportCurrentLineError(String message)
{
compilerEnv.reportSyntaxError(true, message,
getSourceName(), getLineno(),
compilerEnv.reportSyntaxError(message, getSourceName(), getLineno(),
getLine(), getOffset());
}
public final void reportCurrentLineWarning(String message)
{
compilerEnv.reportSyntaxError(false, message,
getSourceName(), getLineno(),
getLine(), getOffset());
compilerEnv.reportSyntaxWarning(message, getSourceName(), getLineno(),
getLine(), getOffset());
}
public final String getSourceName() { return sourceName; }

Просмотреть файл

@ -40,49 +40,94 @@ import java.lang.reflect.Constructor;
import org.mozilla.javascript.*;
public class ClassCompiler extends Interpreter
public class ClassCompiler
{
public Object compile(Context cx, Scriptable scope,
CompilerEnvirons compilerEnv,
ScriptOrFnNode scriptOrFn,
String encodedSource,
boolean returnFunction,
SecurityController securityController,
Object securityDomain)
public ClassCompiler(CompilerEnvirons compilerEnv)
{
OptClassNameHelper
nameHelper = (OptClassNameHelper)ClassNameHelper.get(cx);
Class[] interfaces = nameHelper.getTargetImplements();
Class superClass = nameHelper.getTargetExtends();
boolean isPrimary = (interfaces == null && superClass == null);
String mainClassName = nameHelper.getScriptClassName(isPrimary);
if (compilerEnv == null) throw new IllegalArgumentException();
this.compilerEnv = compilerEnv;
}
Codegen codegen = new Codegen();
public CompilerEnvirons getCompilerEnv()
{
return compilerEnv;
}
byte[] mainClassBytes
= codegen.compileToClassFile(compilerEnv, mainClassName,
scriptOrFn, encodedSource,
returnFunction);
public Class getTargetExtends()
{
return targetExtends;
}
boolean onlySave = false;
ClassRepository repository = nameHelper.getClassRepository();
if (repository != null) {
try {
if (!repository.storeClass(mainClassName, mainClassBytes,
true))
{
onlySave = true;
}
} catch (IOException iox) {
throw Context.throwAsScriptRuntimeEx(iox);
public void setTargetExtends(Class extendsClass)
{
targetExtends = extendsClass;
}
public Class[] getTargetImplements()
{
return targetImplements;
}
public void setTargetImplements(Class[] implementsClasses)
{
targetImplements = implementsClasses;
}
protected String makeAuxiliaryClassName(String mainClassName,
String auxMarker)
{
return mainClassName+auxMarker;
}
/**
* 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 followinf odd index gives class file body as byte[]
* array. The initial elemnt 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);
ScriptOrFnNode tree = p.parse(source, sourceLocation, lineno);
int syntaxErrorCount = compilerEnv.getSyntaxErrorCount();
if (syntaxErrorCount == 0) {
String encodedSource = p.getEncodedSource();
Class superClass = getTargetExtends();
Class[] interfaces = getTargetImplements();
String scriptClassName;
boolean isPrimary = (interfaces == null && superClass == null);
if (isPrimary) {
scriptClassName = mainClassName;
} else {
scriptClassName = makeAuxiliaryClassName(mainClassName, "1");
}
if (!isPrimary) {
String adapterClassName = nameHelper.getScriptClassName(true);
int functionCount = scriptOrFn.getFunctionCount();
Codegen codegen = new Codegen();
byte[] scriptClassBytes
= codegen.compileToClassFile(compilerEnv, scriptClassName,
tree, encodedSource,
false);
syntaxErrorCount = compilerEnv.getSyntaxErrorCount();
if (syntaxErrorCount == 0) {
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 = scriptOrFn.getFunctionNode(i);
FunctionNode ofn = tree.getFunctionNode(i);
String name = ofn.getFunctionName();
if (name != null && name.length() != 0) {
functionNames.put(name, ofn.getParamCount());
@ -91,76 +136,24 @@ public class ClassCompiler extends Interpreter
if (superClass == null) {
superClass = ScriptRuntime.ObjectClass;
}
byte[] classFile = JavaAdapter.createAdapterCode(
functionNames, adapterClassName,
superClass, interfaces,
mainClassName);
try {
if (!repository.storeClass(adapterClassName, classFile,
true))
{
onlySave = true;
}
} catch (IOException iox) {
throw Context.throwAsScriptRuntimeEx(iox);
}
byte[] mainClassBytes
= JavaAdapter.createAdapterCode(
functionNames, mainClassName,
superClass, interfaces, scriptClassName);
return new Object[] { mainClassName, mainClassBytes,
scriptClassName, scriptClassBytes };
}
}
if (onlySave) { return null; }
Exception e = null;
Class result = null;
ClassLoader parentLoader = cx.getApplicationClassLoader();
GeneratedClassLoader loader;
if (securityController == null) {
loader = cx.createClassLoader(parentLoader);
} else {
loader = securityController.createClassLoader(parentLoader,
securityDomain);
}
try {
result = loader.defineClass(mainClassName, mainClassBytes);
loader.linkClass(result);
} catch (SecurityException x) {
e = x;
} catch (IllegalArgumentException x) {
e = x;
}
if (e != null)
throw new RuntimeException("Malformed optimizer package " + e);
if (scriptOrFn.getType() == Token.FUNCTION) {
NativeFunction f;
try {
Constructor ctor = result.getConstructors()[0];
Object[] initArgs = { scope, cx, new Integer(0) };
f = (NativeFunction)ctor.newInstance(initArgs);
} catch (Exception ex) {
throw new RuntimeException
("Unable to instantiate compiled class:"+ex.toString());
}
int ftype = ((FunctionNode)scriptOrFn).getFunctionType();
OptRuntime.initFunction(f, ftype, scope, cx);
return f;
} else {
Script script;
try {
script = (Script) result.newInstance();
} catch (Exception ex) {
throw new RuntimeException
("Unable to instantiate compiled class:"+ex.toString());
}
return script;
}
String msg = ScriptRuntime.getMessage1(
"msg.got.syntax.errors", String.valueOf(syntaxErrorCount));
throw compilerEnv.getErrorReporter().
runtimeError(msg, sourceLocation, lineno, null, 0);
}
public void notifyDebuggerCompilationDone(Context cx,
ScriptOrFnNode scriptOrFn,
String debugSource)
{
// Not supported
}
private CompilerEnvirons compilerEnv;
private Class targetExtends;
private Class[] targetImplements;
}

Просмотреть файл

@ -52,8 +52,126 @@ import java.lang.reflect.Constructor;
* @author Roger Lawrence
*/
class Codegen
public class Codegen extends Interpreter
{
public Object compile(Context cx, Scriptable scope,
CompilerEnvirons compilerEnv,
ScriptOrFnNode scriptOrFn,
String encodedSource,
boolean returnFunction,
SecurityController securityController,
Object securityDomain)
{
OptClassNameHelper
nameHelper = (OptClassNameHelper)ClassNameHelper.get(cx);
Class[] interfaces = nameHelper.getTargetImplements();
Class superClass = nameHelper.getTargetExtends();
boolean isPrimary = (interfaces == null && superClass == null);
String mainClassName = nameHelper.getScriptClassName(isPrimary);
byte[] mainClassBytes = compileToClassFile(compilerEnv, mainClassName,
scriptOrFn, encodedSource,
returnFunction);
boolean onlySave = false;
ClassRepository repository = nameHelper.getClassRepository();
if (repository != null) {
try {
if (!repository.storeClass(mainClassName, mainClassBytes,
true))
{
onlySave = true;
}
} catch (IOException iox) {
throw Context.throwAsScriptRuntimeEx(iox);
}
if (!isPrimary) {
String adapterClassName = nameHelper.getScriptClassName(true);
int functionCount = scriptOrFn.getFunctionCount();
ObjToIntMap functionNames = new ObjToIntMap(functionCount);
for (int i = 0; i != functionCount; ++i) {
FunctionNode ofn = scriptOrFn.getFunctionNode(i);
String name = ofn.getFunctionName();
if (name != null && name.length() != 0) {
functionNames.put(name, ofn.getParamCount());
}
}
if (superClass == null) {
superClass = ScriptRuntime.ObjectClass;
}
byte[] classFile = JavaAdapter.createAdapterCode(
functionNames, adapterClassName,
superClass, interfaces,
mainClassName);
try {
if (!repository.storeClass(adapterClassName, classFile,
true))
{
onlySave = true;
}
} catch (IOException iox) {
throw Context.throwAsScriptRuntimeEx(iox);
}
}
}
if (onlySave) { return null; }
Exception e = null;
Class result = null;
ClassLoader parentLoader = cx.getApplicationClassLoader();
GeneratedClassLoader loader;
if (securityController == null) {
loader = cx.createClassLoader(parentLoader);
} else {
loader = securityController.createClassLoader(parentLoader,
securityDomain);
}
try {
result = loader.defineClass(mainClassName, mainClassBytes);
loader.linkClass(result);
} catch (SecurityException x) {
e = x;
} catch (IllegalArgumentException x) {
e = x;
}
if (e != null)
throw new RuntimeException("Malformed optimizer package " + e);
if (scriptOrFn.getType() == Token.FUNCTION) {
NativeFunction f;
try {
Constructor ctor = result.getConstructors()[0];
Object[] initArgs = { scope, cx, new Integer(0) };
f = (NativeFunction)ctor.newInstance(initArgs);
} catch (Exception ex) {
throw new RuntimeException
("Unable to instantiate compiled class:"+ex.toString());
}
int ftype = ((FunctionNode)scriptOrFn).getFunctionType();
OptRuntime.initFunction(f, ftype, scope, cx);
return f;
} else {
Script script;
try {
script = (Script) result.newInstance();
} catch (Exception ex) {
throw new RuntimeException
("Unable to instantiate compiled class:"+ex.toString());
}
return script;
}
}
public void notifyDebuggerCompilationDone(Context cx,
ScriptOrFnNode scriptOrFn,
String debugSource)
{
// Not supported
}
byte[] compileToClassFile(CompilerEnvirons compilerEnv,
String mainClassName,
ScriptOrFnNode scriptOrFn,

Просмотреть файл

@ -41,6 +41,7 @@ import java.util.*;
import java.lang.reflect.*;
import java.text.MessageFormat;
import org.mozilla.javascript.*;
import org.mozilla.javascript.optimizer.ClassCompiler;
import org.mozilla.javascript.tools.ToolErrorReporter;
/**
@ -51,129 +52,150 @@ public class Main {
/**
* Main entry point.
*
* Process arguments as would a normal Java program. Also
* create a new Context and associate it with the current thread.
* Process arguments as would a normal Java program.
* Then set up the execution environment and begin to
* compile scripts.
*/
public static void main(String args[]) {
Context cx = Context.enter();
public static void main(String args[])
{
Main main = new Main();
args = main.processOptions(args);
if (args == null) {
if (main.printHelp) {
System.out.println(ToolErrorReporter.getMessage(
"msg.jsc.usage", Main.class.getName()));
System.exit(0);
}
System.exit(1);
}
if (!main.reporter.hasReportedError()) {
main.processSource(args);
}
}
public Main()
{
reporter = new ToolErrorReporter(true);
cx.setErrorReporter(reporter);
args = processOptions(cx, args);
if ( ! reporter.hasReportedError())
processSource(cx, args);
cx.exit();
compilerEnv = new CompilerEnvirons();
compilerEnv.setErrorReporter(reporter);
compiler = new ClassCompiler(compilerEnv);
}
/**
* Parse arguments.
*
*/
public static String[] processOptions(Context cx, String args[]) {
ClassNameHelper nameHelper = ClassNameHelper.get(cx);
nameHelper.setTargetPackage(""); // default to no package
cx.setGeneratingDebug(false); // default to no symbols
public String[] processOptions(String args[])
{
targetPackage = ""; // default to no package
compilerEnv.setGenerateDebugInfo(false); // default to no symbols
for (int i=0; i < args.length; i++) {
String arg = args[i];
if (!arg.startsWith("-")) {
String[] result = new String[args.length - i];
for (int j=i; j < args.length; j++)
result[j-i] = args[j];
int tail = args.length - i;
if (targetName != null && tail > 1) {
addError("msg.multiple.js.to.file", targetName);
return null;
}
String[] result = new String[tail];
for (int j = 0; j != tail; ++j) {
result[j] = args[i + j];
}
return result;
}
if (arg.equals("-help") || arg.equals("-h")
|| arg.equals("--help"))
{
printHelp = true;
return null;
}
try {
if (arg.equals("-version") && ++i < args.length) {
int version = Integer.parseInt(args[i]);
cx.setLanguageVersion(version);
compilerEnv.setLanguageVersion(version);
continue;
}
if ((arg.equals("-opt") || arg.equals("-O")) &&
++i < args.length)
{
int optLevel = Integer.parseInt(args[i]);
cx.setOptimizationLevel(optLevel);
compilerEnv.setOptimizationLevel(optLevel);
continue;
}
}
catch (NumberFormatException e) {
cx.reportError( ToolErrorReporter.getMessage("msg.jsc.usage",
args[i] ));
continue;
badUsage(args[i]);
return null;
}
if (arg.equals("-nosource")) {
cx.setGeneratingSource(false);
compilerEnv.setGeneratingSource(false);
continue;
}
if (arg.equals("-debug") || arg.equals("-g")) {
cx.setGeneratingDebug(true);
compilerEnv.setGenerateDebugInfo(true);
continue;
}
if (arg.equals("-o") && ++i < args.length) {
String outFile = args[i];
if (!Character.isJavaIdentifierStart(outFile.charAt(0))) {
cx.reportError(ToolErrorReporter.getMessage(
"msg.invalid.classfile.name",
outFile));
String name = args[i];
int end = name.length();
if (end == 0
|| !Character.isJavaIdentifierStart(name.charAt(0)))
{
addError("msg.invalid.classfile.name", name);
continue;
}
for ( int j = 1; j < outFile.length(); j++ ) {
if ( (!Character.isJavaIdentifierPart(outFile.charAt(j)) &&
outFile.charAt(j) != '.') || (outFile.charAt(j) == '.' &&
(!outFile.endsWith(".class") || j != outFile.length()-6 )) )
{
cx.reportError(ToolErrorReporter.getMessage(
"msg.invalid.classfile.name",
outFile ));
for (int j = 1; j < end; j++) {
char c = name.charAt(j);
if (!Character.isJavaIdentifierPart(c)) {
if (c == '.') {
// check if it is the dot in .class
if (j == end - 6 && name.endsWith(".class")) {
name = name.substring(0, j);
break;
}
}
addError("msg.invalid.classfile.name", name);
break;
}
}
nameHelper.setTargetClassFileName(outFile);
hasOutOption = true;
targetName = name;
continue;
}
if (arg.equals("-package") && ++i < args.length) {
String targetPackage = args[i];
for ( int j = 0; j < targetPackage.length(); j++ ) {
if ( !Character.isJavaIdentifierStart(targetPackage.charAt(j))) {
cx.reportError(ToolErrorReporter.getMessage(
"msg.package.name",
targetPackage));
continue;
}
for ( int k = ++j; k< targetPackage.length(); k++, j++ ) {
if (targetPackage.charAt(k) == '.' &&
targetPackage.charAt(k-1) != '.' &&
k != targetPackage.length()-1)
{
String pkg = args[i];
int end = pkg.length();
for (int j = 0; j != end; ++j) {
char c = pkg.charAt(j);
if (Character.isJavaIdentifierStart(c)) {
for (++j; j != end; ++j) {
c = pkg.charAt(j);
if (!Character.isJavaIdentifierPart(c)) {
break;
}
}
if (j == end) {
break;
}
if (c == '.' && j != end - 1) {
continue;
}
if (!Character.isJavaIdentifierPart(
targetPackage.charAt(k)))
{
cx.reportError(ToolErrorReporter.getMessage(
"msg.package.name",
targetPackage));
continue;
}
continue;
}
addError("msg.package.name", targetPackage);
return null;
}
nameHelper.setTargetPackage(targetPackage);
targetPackage = pkg;
continue;
}
if (arg.equals("-extends") && ++i < args.length) {
String targetExtends = args[i];
Class superClass;
try {
nameHelper.setTargetExtends(Class.forName(targetExtends));
superClass = Class.forName(targetExtends);
} catch (ClassNotFoundException e) {
throw new Error(e.toString()); // TODO: better error
}
compiler.setTargetExtends(superClass);
continue;
}
if (arg.equals("-implements") && ++i < args.length) {
@ -192,85 +214,131 @@ public class Main {
}
Class[] implementsClasses = new Class[v.size()];
v.copyInto(implementsClasses);
nameHelper.setTargetImplements(implementsClasses);
compiler.setTargetImplements(implementsClasses);
continue;
}
usage(arg);
if (arg.equals("-d") && ++i < args.length) {
destinationDir = args[i];
continue;
}
badUsage(arg);
return null;
}
// no file name
p(ToolErrorReporter.getMessage("msg.no.file"));
System.exit(1);
return null;
}
/**
* Print a usage message.
*/
public static void usage(String s) {
p(ToolErrorReporter.getMessage("msg.jsc.usage", s));
System.exit(1);
private static void badUsage(String s) {
System.err.println(ToolErrorReporter.getMessage(
"msg.jsc.bad.usage", Main.class.getName(), s));
}
/**
* Compile JavaScript source.
*
*/
public static void processSource(Context cx, String[] filenames) {
ClassNameHelper nameHelper = ClassNameHelper.get(cx);
if (hasOutOption && filenames.length > 1) {
cx.reportError(ToolErrorReporter.getMessage(
"msg.multiple.js.to.file",
nameHelper.getTargetClassFileName()));
}
for (int i=0; i < filenames.length; i++) {
public void processSource(String[] filenames)
{
for (int i = 0; i != filenames.length; ++i) {
String filename = filenames[i];
File f = new File(filename);
if (!f.exists()) {
cx.reportError(ToolErrorReporter.getMessage(
"msg.jsfile.not.found",
filename));
return;
}
if (!filename.endsWith(".js")) {
cx.reportError(ToolErrorReporter.getMessage(
"msg.extension.not.js",
filename));
addError("msg.extension.not.js", filename);
return;
}
if (!hasOutOption) {
File f = new File(filename);
String source = readSource(f);
if (source == null) return;
String mainClassName = targetName;
if (mainClassName == null) {
String name = f.getName();
String nojs = name.substring(0, name.length() - 3);
String className = getClassName(nojs) + ".class";
String out = f.getParent() == null ? className : f.getParent() +
File.separator + className;
nameHelper.setTargetClassFileName(out);
mainClassName = getClassName(nojs);
}
if (nameHelper.getTargetClassFileName() == null) {
cx.reportError(ToolErrorReporter.getMessage("msg.no-opt"));
if (targetPackage.length() != 0) {
mainClassName = targetPackage+"."+mainClassName;
}
try {
Reader in = new FileReader(filename);
cx.compileReader(in, filename, 1, null);
}
catch (FileNotFoundException ex) {
cx.reportError(ToolErrorReporter.getMessage(
"msg.couldnt.open",
filename));
Object[] compiled
= compiler.compileToClassFiles(source, filename, 1,
mainClassName);
if (compiled == null || compiled.length == 0) {
return;
}
catch (IOException ioe) {
cx.reportError(ioe.toString());
File targetTopDir = null;
if (destinationDir != null) {
targetTopDir = new File(destinationDir);
} else {
String parent = f.getParent();
if (parent != null) {
targetTopDir = new File(parent);
}
}
for (int j = 0; j != compiled.length; j += 2) {
String className = (String)compiled[j];
byte[] bytes = (byte[])compiled[j + 1];
File outfile = getOutputFile(targetTopDir, className);
try {
FileOutputStream os = new FileOutputStream(outfile);
try {
os.write(bytes);
} finally {
os.close();
}
} catch (IOException ioe) {
addFormatedError(ioe.toString());
}
}
}
}
private String readSource(File f)
{
if (!f.exists()) {
addError("msg.jsfile.not.found", f.getAbsolutePath());
return null;
}
try {
Reader in = new FileReader(f);
try {
return Kit.readReader(in);
} finally {
in.close();
}
} catch (FileNotFoundException ex) {
addError("msg.couldnt.open", f.getAbsolutePath());
} catch (IOException ioe) {
addFormatedError(ioe.toString());
}
return null;
}
private File getOutputFile(File parentDir, String className)
{
String path = className.replace('.', File.separatorChar);
path = path.concat(".class");
File f = new File(parentDir, path);
String dirPath = f.getParent();
if (dirPath != null) {
File dir = new File(dirPath);
if (!dir.exists()) {
dir.mkdirs();
}
}
return f;
}
/**
* Verify that class file names are legal Java identifiers. Substitute
* illegal characters with underscores, and prepend the name with an
* underscore if the file name does not begin with a JavaLetter.
*/
static String getClassName(String name) {
String getClassName(String name) {
char[] s = new char[name.length()+1];
char c;
int j = 0;
@ -293,7 +361,28 @@ public class Main {
System.out.println(s);
}
private static boolean hasOutOption = false;
private static ToolErrorReporter reporter;
private void addError(String messageId, String arg)
{
String msg;
if (arg == null) {
msg = ToolErrorReporter.getMessage(messageId);
} else {
msg = ToolErrorReporter.getMessage(messageId, arg);
}
addFormatedError(msg);
}
private void addFormatedError(String message)
{
reporter.error(message, null, -1, null, -1);
}
private boolean printHelp;
private ToolErrorReporter reporter;
private CompilerEnvirons compilerEnv;
private ClassCompiler compiler;
private String targetName;
private String targetPackage;
private String destinationDir;
}

Просмотреть файл

@ -94,17 +94,35 @@ msg.format3 =\
msg.uncaughtJSException =\
uncaught JavaScript exception: {0}
msg.jsc.bad.usage =\
Didn''t understand "{1}". \n\
For more information, try java {0} -h
msg.jsc.usage =\
Didn''t understand "{0}". \n\
Valid arguments are: \n\
\ -version 100|110|120|130|140|150 \n\
\ -opt [1-9] \n\
\ -debug \n\
\ -nosource \n\
\ -o outfile.class \n\
\ -package packageName \n\
\ -extends extendsClassName \n\
\ -implements implementsClassName
Usage: java {0} [OPTION]... SOURCE...\n\
Valid options are: \n\
\ -version VERSION Use the specified language version.\n\
\ VERSION should be one of 100|110|120|130|140|150.\n\
\ -opt LEVEL Use optimization with the specified level.\n\
\ LEVEL should be one of 0..9.\n\
\ -debug, -g Include debug information.\n\
\ -nosource Do not include source to function objects.\n\
\ It makes f.toString() useless and violates ECMAScript\n\
\ standard but makes generated classes smaller and\n\
\ saves memory.\n\
\ -o CLASSNAME Use specified name as the last component of the main\n\
\ generated class name. When specified, only one script\n\
\ SOURCE is allowed. If omitted, it defaults to source\n\
\ name with stripped .js suffix.\n\
\ -package PACKAHGE Place generated classes in the specified package.\n\
\ -d DIRECTORY Use DIRECTORY as destination directory for generated\n\
\ classes. If omitted, it defaults to parent directory\n\
\ of SOURCE.\n\
\ -extends CLASS The main generated class will extend the specified\n\
\ class CLASS.\n\
\ -implements INTERFACE1,INTERFACE2,... The main generated class will\n\
\ implement the specified list of interfaces.\n\
\ -help, --help, -h Print this help and exit.\n\
msg.no.file =\