Decompling parsing from code generation: compiler instances are created only when parsing tree is build.

This commit is contained in:
igor%mir2.org 2003-12-27 21:32:35 +00:00
parent ea68f99149
commit 3cab5b3e26
11 changed files with 352 additions and 247 deletions

View File

@ -0,0 +1,128 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is representation of compiler options for Rhino.
*
* The Initial Developer of the Original Code is
* RUnit Software AS.
* Portions created by the Initial Developer are Copyright (C) 2003
* the Initial Developer. All Rights Reserved.
*
* Contributor(s): Igor Bukanov, igor@fastmail.fm
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
package org.mozilla.javascript;
public class CompilerEnvirons
{
public void initFromContext(Context cx, ErrorReporter syntaxErrorReporter)
{
setSyntaxErrorReporter(syntaxErrorReporter);
this.languageVersion = cx.getLanguageVersion();
useDynamicScope = cx.hasCompileFunctionsWithDynamicScope();
generateDebugInfo = (!cx.isGeneratingDebugChanged()
|| cx.isGeneratingDebug());
this.reservedKeywordAsIdentifier
= cx.hasFeature(Context.FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER);
this.allowMemberExprAsFunctionName
= cx.hasFeature(Context.FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME);
}
public final int getSyntaxErrorCount()
{
return syntaxErrorCount;
}
public void setSyntaxErrorReporter(ErrorReporter syntaxErrorReporter)
{
if (syntaxErrorReporter == null) Kit.argBug();
this.syntaxErrorReporter = syntaxErrorReporter;
}
public final void reportSyntaxError(boolean isError,
String messageProperty, Object[] args,
String sourceName, int lineno,
String line, int lineOffset)
{
String message = Context.getMessage(messageProperty, args);
if (isError) {
++syntaxErrorCount;
if (fromEval) {
// We're probably in an eval. Need to throw an exception.
throw ScriptRuntime.constructError(
"SyntaxError", message, sourceName,
lineno, line, lineOffset);
} else {
syntaxErrorReporter.error(message, sourceName,
lineno, line, lineOffset);
}
} else {
syntaxErrorReporter.warning(message, sourceName,
lineno, line, lineOffset);
}
}
public final boolean isFromEval()
{
return fromEval;
}
public void setFromEval(boolean fromEval)
{
this.fromEval = fromEval;
}
public final int getLanguageVersion()
{
return languageVersion;
}
public final boolean isGenerateDebugInfo()
{
return generateDebugInfo;
}
public final boolean isUseDynamicScope()
{
return useDynamicScope;
}
private ErrorReporter syntaxErrorReporter;
private int syntaxErrorCount;
private boolean fromEval;
int languageVersion = Context.VERSION_DEFAULT;
boolean generateDebugInfo;
boolean useDynamicScope;
boolean reservedKeywordAsIdentifier;
boolean allowMemberExprAsFunctionName;
}

View File

@ -977,17 +977,17 @@ public class Context
public boolean stringIsCompilableUnit(String source)
{
boolean errorseen = false;
Interpreter compiler = new Interpreter();
compiler.setSyntaxErrorReporter(new DefaultErrorReporter(), false);
CompilerEnvirons compilerEnv = new CompilerEnvirons();
compilerEnv.initFromContext(this, new DefaultErrorReporter());
// no source name or source text manager, because we're just
// going to throw away the result.
TokenStream ts = new TokenStream(this, compiler, null, source, null, 1);
TokenStream ts = new TokenStream(compilerEnv,
null, source, null, 1);
IRFactory irf = new IRFactory(compiler, ts);
Parser p = createParser();
Parser p = new Parser(compilerEnv);
Decompiler decompiler = new Decompiler();
try {
p.parse(ts, irf, decompiler);
p.parse(ts, decompiler);
} catch (IOException ioe) {
errorseen = true;
} catch (EvaluatorException ee) {
@ -2014,16 +2014,10 @@ public class Context
// scope should be given if and only if compiling function
if (!(scope == null ^ returnFunction)) Kit.codeBug();
Interpreter compiler = createCompiler();
CompilerEnvirons compilerEnv = new CompilerEnvirons();
ErrorReporter reporter = getErrorReporter();
compiler.setSyntaxErrorReporter(reporter, fromEval);
if (securityController != null) {
securityDomain = securityController.
getDynamicSecurityDomain(securityDomain);
} else {
securityDomain = null;
}
compilerEnv.initFromContext(this, reporter);
compilerEnv.setFromEval(fromEval);
if (debugger != null) {
if (sourceReader != null) {
@ -2032,15 +2026,23 @@ public class Context
}
}
TokenStream ts = new TokenStream(this, compiler,
TokenStream ts = new TokenStream(compilerEnv,
sourceReader, sourceString,
sourceName, lineno);
Parser p = createParser();
IRFactory irf = new IRFactory(compiler, ts);
Parser p = new Parser(compilerEnv);
Decompiler decompiler = new Decompiler();
ScriptOrFnNode tree = p.parse(ts, irf, decompiler);
if (compiler.syntaxErrorCount == 0) {
ScriptOrFnNode tree = p.parse(ts, decompiler);
int syntaxErrorCount = compilerEnv.getSyntaxErrorCount();
if (syntaxErrorCount == 0) {
Interpreter compiler = createCompiler();
compiler.compilerEnv = compilerEnv;
if (securityController != null) {
securityDomain = securityController.
getDynamicSecurityDomain(securityDomain);
} else {
securityDomain = null;
}
String encodedSource = null;
if (isGeneratingSource()) {
encodedSource = decompiler.getEncodedSource();
@ -2063,7 +2065,8 @@ public class Context
Object result = compiler.compile(this, scope, tree,
securityController, securityDomain,
encodedSource);
if (compiler.syntaxErrorCount == 0) {
syntaxErrorCount = compilerEnv.getSyntaxErrorCount();
if (syntaxErrorCount == 0) {
if (debugger != null) {
if (sourceString == null) Kit.codeBug();
compiler.notifyDebuggerCompilationDone(this, result,
@ -2073,7 +2076,7 @@ public class Context
}
}
String msg = Context.getMessage1("msg.got.syntax.errors",
String.valueOf(compiler.syntaxErrorCount));
String.valueOf(syntaxErrorCount));
throw reporter.runtimeError(msg, sourceName, lineno, null, 0);
}
@ -2091,14 +2094,6 @@ public class Context
return result;
}
private Parser createParser() {
Parser parser = new Parser();
parser.setLanguageVersion(getLanguageVersion());
parser.setAllowMemberExprAsFunctionName(
hasFeature(Context.FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME));
return parser;
}
static String getSourcePositionFromStack(int[] linep) {
Context cx = getCurrentContext();
if (cx == null)

View File

@ -43,21 +43,22 @@ package org.mozilla.javascript;
* @author Mike McCabe
* @author Norris Boyd
*/
public class IRFactory {
public IRFactory(Interpreter compiler, TokenStream ts) {
this.compiler = compiler;
class IRFactory
{
IRFactory(TokenStream ts)
{
this.ts = ts;
}
public ScriptOrFnNode createScript() {
ScriptOrFnNode createScript()
{
return new ScriptOrFnNode(Token.SCRIPT);
}
/**
* Script (for associating file/url names with toplevel scripts.)
*/
public void initScript(ScriptOrFnNode scriptNode, Object body)
void initScript(ScriptOrFnNode scriptNode, Object body)
{
Node children = ((Node) body).getFirstChild();
if (children != null) { scriptNode.addChildrenToBack(children); }
@ -66,11 +67,13 @@ public class IRFactory {
/**
* Leaf
*/
public Object createLeaf(int nodeType) {
Object createLeaf(int nodeType)
{
return new Node(nodeType);
}
public Object createLeaf(int nodeType, int nodeOp) {
Object createLeaf(int nodeType, int nodeOp)
{
return new Node(nodeType, nodeOp);
}
@ -78,40 +81,47 @@ public class IRFactory {
* Statement leaf nodes.
*/
public Object createSwitch(int lineno) {
Object createSwitch(int lineno)
{
return new Node.Jump(Token.SWITCH, lineno);
}
public Object createVariables(int lineno) {
Object createVariables(int lineno)
{
return new Node(Token.VAR, lineno);
}
public Object createExprStatement(Object expr, int lineno) {
Object createExprStatement(Object expr, int lineno)
{
return new Node(Token.EXPRSTMT, (Node) expr, lineno);
}
public Object createExprStatementNoReturn(Object expr, int lineno) {
Object createExprStatementNoReturn(Object expr, int lineno)
{
return new Node(Token.POP, (Node) expr, lineno);
}
/**
* Name
*/
public Object createName(String name) {
Object createName(String name)
{
return Node.newString(Token.NAME, name);
}
/**
* String (for literals)
*/
public Object createString(String string) {
Object createString(String string)
{
return Node.newString(string);
}
/**
* Number (for literals)
*/
public Object createNumber(double number) {
Object createNumber(double number)
{
return Node.newNumber(number);
}
@ -123,8 +133,8 @@ public class IRFactory {
* @param stmts the statements in the catch clause
* @param lineno the starting line number of the catch clause
*/
public Object createCatch(String varName, Object catchCond, Object stmts,
int lineno)
Object createCatch(String varName, Object catchCond, Object stmts,
int lineno)
{
if (catchCond == null) {
catchCond = new Node(Token.EMPTY);
@ -136,14 +146,16 @@ public class IRFactory {
/**
* Throw
*/
public Object createThrow(Object expr, int lineno) {
Object createThrow(Object expr, int lineno)
{
return new Node(Token.THROW, (Node)expr, lineno);
}
/**
* Return
*/
public Object createReturn(Object expr, int lineno) {
Object createReturn(Object expr, int lineno)
{
return expr == null
? new Node(Token.RETURN, lineno)
: new Node(Token.RETURN, (Node)expr, lineno);
@ -152,7 +164,7 @@ public class IRFactory {
/**
* Label
*/
public Object createLabel(String label, int lineno)
Object createLabel(String label, int lineno)
{
Node.Jump n = new Node.Jump(Token.LABEL, lineno);
n.setLabel(label);
@ -162,7 +174,7 @@ public class IRFactory {
/**
* Break (possibly labeled)
*/
public Object createBreak(String label, int lineno)
Object createBreak(String label, int lineno)
{
Node.Jump n = new Node.Jump(Token.BREAK, lineno);
if (label != null) {
@ -174,7 +186,7 @@ public class IRFactory {
/**
* Continue (possibly labeled)
*/
public Object createContinue(String label, int lineno)
Object createContinue(String label, int lineno)
{
Node.Jump n = new Node.Jump(Token.CONTINUE, lineno);
if (label != null) {
@ -188,16 +200,18 @@ public class IRFactory {
* Creates the empty statement block
* Must make subsequent calls to add statements to the node
*/
public Object createBlock(int lineno) {
Object createBlock(int lineno)
{
return new Node(Token.BLOCK, lineno);
}
public FunctionNode createFunction(String name) {
return compiler.createFunctionNode(name);
FunctionNode createFunction(String name)
{
return new FunctionNode(name);
}
public Object initFunction(FunctionNode fnNode, int functionIndex,
Object statements, int functionType)
Object initFunction(FunctionNode fnNode, int functionIndex,
Object statements, int functionType)
{
Node stmts = (Node)statements;
fnNode.setFunctionType(functionType);
@ -256,14 +270,16 @@ public class IRFactory {
* breaks the Factory abstraction, but it removes a requirement
* from implementors of Node.
*/
public void addChildToBack(Object parent, Object child) {
void addChildToBack(Object parent, Object child)
{
((Node)parent).addChildToBack((Node)child);
}
/**
* While
*/
public Object createWhile(Object cond, Object body, int lineno) {
Object createWhile(Object cond, Object body, int lineno)
{
return createLoop(LOOP_WHILE, (Node)body, (Node)cond, null, null,
lineno);
}
@ -271,7 +287,8 @@ public class IRFactory {
/**
* DoWhile
*/
public Object createDoWhile(Object body, Object cond, int lineno) {
Object createDoWhile(Object body, Object cond, int lineno)
{
return createLoop(LOOP_DO_WHILE, (Node)body, (Node)cond, null, null,
lineno);
}
@ -279,8 +296,8 @@ public class IRFactory {
/**
* For
*/
public Object createFor(Object init, Object test, Object incr,
Object body, int lineno)
Object createFor(Object init, Object test, Object incr, Object body,
int lineno)
{
return createLoop(LOOP_FOR, (Node)body, (Node)test,
(Node)init, (Node)incr, lineno);
@ -344,7 +361,8 @@ public class IRFactory {
* For .. In
*
*/
public Object createForIn(Object lhs, Object obj, Object body, int lineno) {
Object createForIn(Object lhs, Object obj, Object body, int lineno)
{
String name;
Node lhsNode = (Node) lhs;
Node objNode = (Node) obj;
@ -415,8 +433,8 @@ public class IRFactory {
* ... and a goto to GOTO around these handlers.
*/
public Object createTryCatchFinally(Object tryblock, Object catchblocks,
Object finallyblock, int lineno)
Object createTryCatchFinally(Object tryblock, Object catchblocks,
Object finallyblock, int lineno)
{
Node trynode = (Node)tryblock;
boolean hasFinally = false;
@ -581,7 +599,8 @@ public class IRFactory {
/**
* With
*/
public Object createWith(Object obj, Object body, int lineno) {
Object createWith(Object obj, Object body, int lineno)
{
Node result = new Node(Token.BLOCK, lineno);
result.addChildToBack(new Node(Token.ENTERWITH, (Node)obj));
Node bodyNode = new Node(Token.WITH, (Node) body, lineno);
@ -596,7 +615,8 @@ public class IRFactory {
* plus a series of array element entries, so later compiler
* stages don't need to know about array literals.
*/
public Object createArrayLiteral(Object obj) {
Object createArrayLiteral(Object obj)
{
Node array;
array = new Node(Token.NEW, Node.newString(Token.NAME, "Array"));
Node list = new Node(Token.INIT_LIST, array);
@ -628,7 +648,7 @@ public class IRFactory {
* (Which will make Array optimizations involving allocating a
* Java array to back the javascript array work better.)
*/
if (ts.cx.getLanguageVersion() == Context.VERSION_1_2) {
if (ts.compilerEnv.languageVersion == Context.VERSION_1_2) {
/* When last array element is empty, we need to set the
* length explicitly, because we can't depend on SETELEM
* to do it for us - because empty [,,] array elements
@ -652,7 +672,8 @@ public class IRFactory {
* creation plus object property entries, so later compiler
* stages don't need to know about object literals.
*/
public Object createObjectLiteral(Object obj) {
Object createObjectLiteral(Object obj)
{
Node result = new Node(Token.NEW,
Node.newString(Token.NAME, "Object"));
Node list = new Node(Token.INIT_LIST, result);
@ -675,7 +696,8 @@ public class IRFactory {
/**
* Regular expressions
*/
public Object createRegExp(int regexpIndex) {
Object createRegExp(int regexpIndex)
{
Node n = new Node(Token.REGEXP);
n.putIntProp(Node.REGEXP_PROP, regexpIndex);
return n;
@ -684,8 +706,7 @@ public class IRFactory {
/**
* If statement
*/
public Object createIf(Object condObj, Object ifTrue, Object ifFalse,
int lineno)
Object createIf(Object condObj, Object ifTrue, Object ifFalse, int lineno)
{
Node cond = (Node)condObj;
int condStatus = isAlwaysDefinedBoolean(cond);
@ -722,7 +743,7 @@ public class IRFactory {
return result;
}
public Object createCondExpr(Object condObj, Object ifTrue, Object ifFalse)
Object createCondExpr(Object condObj, Object ifTrue, Object ifFalse)
{
Node cond = (Node)condObj;
int condStatus = isAlwaysDefinedBoolean(cond);
@ -737,7 +758,8 @@ public class IRFactory {
/**
* Unary
*/
public Object createUnary(int nodeType, Object child) {
Object createUnary(int nodeType, Object child)
{
Node childNode = (Node) child;
int childType = childNode.getType();
switch (nodeType) {
@ -802,7 +824,7 @@ public class IRFactory {
return new Node(nodeType, childNode);
}
public Object createIncDec(int nodeType, boolean post, Object child)
Object createIncDec(int nodeType, boolean post, Object child)
{
Node childNode = (Node)child;
int childType = childNode.getType();
@ -854,7 +876,7 @@ public class IRFactory {
/**
* Binary
*/
public Object createBinary(int nodeType, Object leftObj, Object rightObj)
Object createBinary(int nodeType, Object leftObj, Object rightObj)
{
Node left = (Node)leftObj;
Node right = (Node)rightObj;
@ -1026,7 +1048,7 @@ public class IRFactory {
return new Node(nodeType, left, right);
}
public Object createAssignment(Object leftObj, Object rightObj)
Object createAssignment(Object leftObj, Object rightObj)
{
Node left = (Node)leftObj;
Node right = (Node)rightObj;
@ -1064,7 +1086,7 @@ public class IRFactory {
}
}
public Object createAssignmentOp(int assignOp, Object left, Object right)
Object createAssignmentOp(int assignOp, Object left, Object right)
{
return createAssignmentOp(assignOp, (Node)left, (Node)right, false);
}
@ -1111,7 +1133,8 @@ public class IRFactory {
}
}
public Node createUseLocal(Node localBlock) {
Node createUseLocal(Node localBlock)
{
if (Token.LOCAL_BLOCK != localBlock.getType()) Kit.codeBug();
Node result = new Node(Token.LOCAL_LOAD);
result.putProp(Node.LOCAL_BLOCK_PROP, localBlock);
@ -1140,7 +1163,8 @@ public class IRFactory {
return 0;
}
private static boolean hasSideEffects(Node exprTree) {
private static boolean hasSideEffects(Node exprTree)
{
switch (exprTree.getType()) {
case Token.INC:
case Token.DEC:
@ -1162,8 +1186,6 @@ public class IRFactory {
return false;
}
private Interpreter compiler;
// Only needed to call reportCurrentLineError.
private TokenStream ts;

View File

@ -110,42 +110,6 @@ public class Interpreter
// Last icode
END_ICODE = BASE_ICODE + 30;
final void setSyntaxErrorReporter(ErrorReporter syntaxErrorReporter,
boolean fromEval)
{
this.syntaxErrorReporter = syntaxErrorReporter;
this.fromEval = fromEval;
this.syntaxErrorCount = 0;
}
public void reportSyntaxError(boolean isError,
String messageProperty, Object[] args,
String sourceName, int lineno,
String line, int lineOffset)
{
String message = Context.getMessage(messageProperty, args);
if (isError) {
++syntaxErrorCount;
if (fromEval) {
// We're probably in an eval. Need to throw an exception.
throw ScriptRuntime.constructError(
"SyntaxError", message, sourceName,
lineno, line, lineOffset);
} else {
syntaxErrorReporter.error(message, sourceName,
lineno, line, lineOffset);
}
} else {
syntaxErrorReporter.warning(message, sourceName,
lineno, line, lineOffset);
}
}
public FunctionNode createFunctionNode(String name)
{
return new FunctionNode(name);
}
public ScriptOrFnNode transform(Context cx, ScriptOrFnNode tree)
{
(new NodeTransformer(this)).transform(tree);
@ -157,7 +121,8 @@ public class Interpreter
Object securityDomain, String encodedSource)
{
scriptOrFn = tree;
itsData = new InterpreterData(securityDomain, cx.getLanguageVersion());
itsData = new InterpreterData(securityDomain,
compilerEnv.getLanguageVersion());
itsData.itsSourceFile = scriptOrFn.getSourceName();
itsData.encodedSource = encodedSource;
itsData.topLevel = true;
@ -166,7 +131,7 @@ public class Interpreter
return createFunction(cx, scope, itsData, false);
} else {
generateICodeFromTree(cx, scriptOrFn);
itsData.itsFromEvalCode = fromEval;
itsData.itsFromEvalCode = compilerEnv.isFromEval();
return new InterpretedScript(itsData);
}
}
@ -207,7 +172,7 @@ public class Interpreter
itsData.itsNeedsActivation = true;
}
if (!theFunction.getIgnoreDynamicScope()) {
if (cx.hasCompileFunctionsWithDynamicScope()) {
if (compilerEnv.isUseDynamicScope()) {
itsData.useDynamicScope = true;
}
}
@ -295,6 +260,7 @@ public class Interpreter
for (int i = 0; i != functionCount; i++) {
FunctionNode def = scriptOrFn.getFunctionNode(i);
Interpreter jsi = new Interpreter();
jsi.compilerEnv = compilerEnv;
jsi.scriptOrFn = def;
jsi.itsData = new InterpreterData(itsData.securityDomain,
itsData.languageVersion);
@ -3316,11 +3282,9 @@ public class Interpreter
return pc;
}
private ErrorReporter syntaxErrorReporter;
private boolean fromEval;
int syntaxErrorCount;
private boolean itsInFunctionFlag;
protected CompilerEnvirons compilerEnv;
private boolean itsInFunctionFlag;
private InterpreterData itsData;
private ScriptOrFnNode scriptOrFn;

View File

@ -534,8 +534,8 @@ public class NodeTransformer
{
int lineno = stmt.getLineno();
String sourceName = tree.getSourceName();
compiler.reportSyntaxError(true, messageId, messageArgs,
sourceName, lineno, null, 0);
compiler.compilerEnv.reportSyntaxError(
true, messageId, messageArgs, sourceName, lineno, null, 0);
}
private ObjArray loops;

View File

@ -53,15 +53,10 @@ import java.io.IOException;
class Parser {
public Parser() { }
public void setLanguageVersion(int languageVersion) {
this.languageVersion = languageVersion;
}
public void setAllowMemberExprAsFunctionName(boolean flag) {
this.allowMemberExprAsFunctionName = flag;
}
public Parser(CompilerEnvirons compilerEnv)
{
this.compilerEnv = compilerEnv;
}
private void mustMatchToken(TokenStream ts, int toMatch, String messageId)
throws IOException, ParserException
@ -95,11 +90,10 @@ class Parser {
* parse failure will result in a call to the current Context's
* ErrorReporter.)
*/
public ScriptOrFnNode parse(TokenStream ts, IRFactory nf,
Decompiler decompiler)
public ScriptOrFnNode parse(TokenStream ts, Decompiler decompiler)
throws IOException
{
this.nf = nf;
this.nf = new IRFactory(ts);
currentScriptOrFn = nf.createScript();
this.decompiler = decompiler;
int sourceStartOffset = decompiler.getCurrentOffset();
@ -211,7 +205,7 @@ class Parser {
if (ts.matchToken(Token.NAME)) {
name = ts.getString();
if (!ts.matchToken(Token.LP)) {
if (allowMemberExprAsFunctionName) {
if (compilerEnv.allowMemberExprAsFunctionName) {
// Extension to ECMA: if 'function <name>' does not follow
// by '(', assume <name> starts memberExpr
decompiler.addName(name);
@ -226,7 +220,7 @@ class Parser {
name = "";
} else {
name = "";
if (allowMemberExprAsFunctionName) {
if (compilerEnv.allowMemberExprAsFunctionName) {
// Note that memberExpr can not start with '(' like
// in function (1+2).toString(), because 'function (' already
// processed as anonymous function
@ -385,7 +379,7 @@ class Parser {
return;
case Token.FUNCTION:
if (languageVersion < Context.VERSION_1_2) {
if (compilerEnv.languageVersion < Context.VERSION_1_2) {
/*
* Checking against version < 1.2 and version >= 1.0
* in the above line breaks old javascript, so we keep it
@ -400,7 +394,7 @@ class Parser {
private void checkWellTerminatedFunction(TokenStream ts)
throws IOException, ParserException
{
if (languageVersion < Context.VERSION_1_2) {
if (compilerEnv.languageVersion < Context.VERSION_1_2) {
// See comments in checkWellTerminated
return;
}
@ -1021,7 +1015,7 @@ class Parser {
ts.getToken();
int decompilerToken = tt;
int parseToken = tt;
if (languageVersion == Context.VERSION_1_2) {
if (compilerEnv.languageVersion == Context.VERSION_1_2) {
// JavaScript 1.2 uses shallow equality for == and != .
// In addition, convert === and !== for decompiler into
// == and != since the decompiler is supposed to show
@ -1483,9 +1477,9 @@ class Parser {
return null; // should never reach here
}
private IRFactory nf;
private int languageVersion = Context.VERSION_DEFAULT;
private boolean allowMemberExprAsFunctionName = false;
private CompilerEnvirons compilerEnv;
private IRFactory nf;
private boolean ok; // Did the parse encounter an error?

View File

@ -75,12 +75,11 @@ public class TokenStream
private final static int
EOF_CHAR = -1;
public TokenStream(Context cx, Interpreter compiler,
public TokenStream(CompilerEnvirons compilerEnv,
Reader sourceReader, String sourceString,
String sourceName, int lineno)
{
this.cx = cx;
this.compiler = compiler;
this.compilerEnv = compilerEnv;
this.pushbackToken = Token.EOF;
this.sourceName = sourceName;
this.lineno = lineno;
@ -296,17 +295,17 @@ public class TokenStream
public final void reportCurrentLineError(String messageProperty,
Object[] args)
{
compiler.reportSyntaxError(true, messageProperty, args,
getSourceName(), getLineno(),
getLine(), getOffset());
compilerEnv.reportSyntaxError(true, messageProperty, args,
getSourceName(), getLineno(),
getLine(), getOffset());
}
public final void reportCurrentLineWarning(String messageProperty,
Object[] args)
{
compiler.reportSyntaxError(false, messageProperty, args,
getSourceName(), getLineno(),
getLine(), getOffset());
compilerEnv.reportSyntaxError(false, messageProperty, args,
getSourceName(), getLineno(),
getLine(), getOffset());
}
public final String getSourceName() { return sourceName; }
@ -476,13 +475,10 @@ public class TokenStream
if (result != Token.EOF) {
if (result != Token.RESERVED) {
return result;
}
else if (!cx.hasFeature(
Context.FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER))
} else if (!compilerEnv.reservedKeywordAsIdentifier)
{
return result;
}
else {
} else {
// If implementation permits to use future reserved
// keywords in violation with the EcmaScript,
// treat it as name but issue warning
@ -1207,6 +1203,5 @@ public class TokenStream
private int sourceEnd;
private int sourceCursor;
private Interpreter compiler;
Context cx;
CompilerEnvirons compilerEnv;
}

View File

@ -58,18 +58,9 @@ public class Codegen extends Interpreter {
{
}
public FunctionNode createFunctionNode(String name)
{
return new OptFunctionNode(name);
}
public ScriptOrFnNode transform(Context cx, ScriptOrFnNode tree)
{
nameHelper = (OptClassNameHelper)ClassNameHelper.get(cx);
itsUseDynamicScope = cx.hasCompileFunctionsWithDynamicScope();
generateDebugInfo = (!cx.isGeneratingDebugChanged()
|| cx.isGeneratingDebug());
languageVersion = cx.getLanguageVersion();
initOptFunctions_r(tree);
@ -86,10 +77,10 @@ public class Codegen extends Interpreter {
int functionCount = tree.getFunctionCount();
for (int i = 0; i != functionCount; ++i) {
OptFunctionNode ofn = OptFunctionNode.get(tree, i);
if (ofn.getFunctionType()
if (ofn.fnode.getFunctionType()
== FunctionNode.FUNCTION_STATEMENT)
{
String name = ofn.getFunctionName();
String name = ofn.fnode.getFunctionName();
if (name.length() != 0) {
if (possibleDirectCalls == null) {
possibleDirectCalls = new Hashtable();
@ -119,9 +110,9 @@ public class Codegen extends Interpreter {
private static void initOptFunctions_r(ScriptOrFnNode scriptOrFn)
{
for (int i = 0, N = scriptOrFn.getFunctionCount(); i != N; ++i) {
OptFunctionNode ofn = OptFunctionNode.get(scriptOrFn, i);
ofn.init();
initOptFunctions_r(ofn);
FunctionNode fn = scriptOrFn.getFunctionNode(i);
new OptFunctionNode(fn);
initOptFunctions_r(fn);
}
}
@ -271,7 +262,7 @@ public class Codegen extends Interpreter {
boolean hasFunctions = (scriptOrFnNodes.length > 1 || !hasScript);
String sourceFile = null;
if (generateDebugInfo) {
if (compilerEnv.isGenerateDebugInfo()) {
sourceFile = scriptOrFnNodes[0].getSourceName();
}
@ -309,6 +300,7 @@ public class Codegen extends Interpreter {
BodyCodegen bodygen = new BodyCodegen();
bodygen.cfw = cfw;
bodygen.codegen = this;
bodygen.compilerEnv = compilerEnv;
bodygen.scriptOrFn = n;
bodygen.generateBodyCode();
@ -351,12 +343,12 @@ public class Codegen extends Interpreter {
return newInstance;
}
*/
cfw.startMethod(getDirectCtorName(ofn),
getBodyMethodSignature(ofn),
cfw.startMethod(getDirectCtorName(ofn.fnode),
getBodyMethodSignature(ofn.fnode),
(short)(ClassFileWriter.ACC_STATIC
| ClassFileWriter.ACC_PRIVATE));
int argCount = ofn.getParamCount();
int argCount = ofn.fnode.getParamCount();
int firstLocal = (4 + argCount * 3) + 1;
cfw.addALoad(0); // this
@ -381,8 +373,8 @@ public class Codegen extends Interpreter {
cfw.addALoad(4 + argCount * 3);
cfw.addInvoke(ByteCode.INVOKESTATIC,
mainClassName,
getBodyMethodName(ofn),
getBodyMethodSignature(ofn));
getBodyMethodName(ofn.fnode),
getBodyMethodSignature(ofn.fnode));
int exitLabel = cfw.acquireLabel();
cfw.add(ByteCode.DUP); // make a copy of direct call result
cfw.add(ByteCode.INSTANCEOF, "org/mozilla/javascript/Scriptable");
@ -445,7 +437,7 @@ public class Codegen extends Interpreter {
if (n.getType() == Token.FUNCTION) {
OptFunctionNode ofn = OptFunctionNode.get(n);
if (ofn.isTargetOfDirectCall()) {
int pcount = ofn.getParamCount();
int pcount = ofn.fnode.getParamCount();
if (pcount != 0) {
// loop invariant:
// stack top == arguments array from addALoad4()
@ -552,7 +544,7 @@ public class Codegen extends Interpreter {
// NativeFunction.initScriptFunction(version, "", varNamesArray, 0)
cfw.addLoadThis();
cfw.addPush(languageVersion);
cfw.addPush(compilerEnv.getLanguageVersion());
cfw.addPush(""); // Function name
pushParamNamesArray(cfw, script);
cfw.addPush(0); // No parameters, only varnames
@ -634,10 +626,10 @@ public class Codegen extends Interpreter {
// Call NativeFunction.initScriptFunction
cfw.addLoadThis();
cfw.addPush(languageVersion);
cfw.addPush(ofn.getFunctionName());
pushParamNamesArray(cfw, ofn);
cfw.addPush(ofn.getParamCount());
cfw.addPush(compilerEnv.getLanguageVersion());
cfw.addPush(ofn.fnode.getFunctionName());
pushParamNamesArray(cfw, ofn.fnode);
cfw.addPush(ofn.fnode.getParamCount());
cfw.addInvoke(ByteCode.INVOKEVIRTUAL,
"org/mozilla/javascript/NativeFunction",
"initScriptFunction",
@ -651,10 +643,10 @@ public class Codegen extends Interpreter {
"(Lorg/mozilla/javascript/Scriptable;)V");
// precompile all regexp literals
int regexpCount = ofn.getRegexpCount();
int regexpCount = ofn.fnode.getRegexpCount();
if (regexpCount != 0) {
cfw.addLoadThis();
pushRegExpArray(cfw, ofn, CONTEXT_ARG, SCOPE_ARG);
pushRegExpArray(cfw, ofn.fnode, CONTEXT_ARG, SCOPE_ARG);
cfw.add(ByteCode.PUTFIELD, mainClassName,
REGEXP_ARRAY_FIELD_NAME, REGEXP_ARRAY_FIELD_TYPE);
}
@ -1007,7 +999,7 @@ public class Codegen extends Interpreter {
if (n.getType() == Token.FUNCTION) {
OptFunctionNode ofn = OptFunctionNode.get(n);
if (ofn.isTargetOfDirectCall()) {
int pCount = ofn.getParamCount();
int pCount = ofn.fnode.getParamCount();
for (int i = 0; i != pCount; i++) {
sb.append("Ljava/lang/Object;D");
}
@ -1019,7 +1011,7 @@ public class Codegen extends Interpreter {
String getFunctionInitMethodName(OptFunctionNode ofn)
{
return "_i"+getIndex(ofn);
return "_i"+getIndex(ofn.fnode);
}
String getCompiledRegexpName(ScriptOrFnNode n, int regexpIndex)
@ -1067,7 +1059,6 @@ public class Codegen extends Interpreter {
private byte[] mainClassBytes;
boolean itsUseDynamicScope;
boolean generateDebugInfo;
int languageVersion;
private double[] itsConstantList;
@ -1114,7 +1105,8 @@ class BodyCodegen
inDirectCallFunction = (fnCurrent == null) ? false
: fnCurrent.isTargetOfDirectCall();
hasVarsInRegs = (fnCurrent != null && !fnCurrent.requiresActivation());
hasVarsInRegs = (fnCurrent != null
&& !fnCurrent.fnode.requiresActivation());
locals = new boolean[MAX_LOCALS];
@ -1172,8 +1164,8 @@ class BodyCodegen
}
if (fnCurrent != null && directParameterCount == -1
&& (!codegen.itsUseDynamicScope
|| fnCurrent.getIgnoreDynamicScope()))
&& (!compilerEnv.isUseDynamicScope()
|| fnCurrent.fnode.getIgnoreDynamicScope()))
{
// Unless we're either in a direct call or using dynamic scope,
// use the enclosing scope of the function as our variable object.
@ -1203,7 +1195,7 @@ class BodyCodegen
}
}
if (fnCurrent != null && fnCurrent.getCheckThis()) {
if (fnCurrent != null && fnCurrent.fnode.getCheckThis()) {
// Nested functions must check their 'this' value to
// insure it is not an activation object:
// see 10.1.6 Activation Object
@ -1327,13 +1319,15 @@ class BodyCodegen
int functionCount = scriptOrFn.getFunctionCount();
for (int i = 0; i != functionCount; i++) {
OptFunctionNode ofn = OptFunctionNode.get(scriptOrFn, i);
if (ofn.getFunctionType() == FunctionNode.FUNCTION_STATEMENT) {
if (ofn.fnode.getFunctionType()
== FunctionNode.FUNCTION_STATEMENT)
{
visitFunction(ofn, FunctionNode.FUNCTION_STATEMENT);
}
}
// default is to generate debug info
if (codegen.generateDebugInfo) {
if (compilerEnv.isGenerateDebugInfo()) {
OptLocalVariable lv = new OptLocalVariable(debugVariableName,
false);
lv.assignJRegister(variableObjectLocal);
@ -1440,7 +1434,7 @@ class BodyCodegen
int fnIndex = node.getExistingIntProp(Node.FUNCTION_PROP);
OptFunctionNode ofn = OptFunctionNode.get(scriptOrFn,
fnIndex);
int t = ofn.getFunctionType();
int t = ofn.fnode.getFunctionType();
if (t != FunctionNode.FUNCTION_STATEMENT) {
visitFunction(ofn, t);
}
@ -2029,7 +2023,7 @@ class BodyCodegen
private void visitFunction(OptFunctionNode ofn, int functionType)
{
int fnIndex = codegen.getIndex(ofn);
int fnIndex = codegen.getIndex(ofn.fnode);
cfw.add(ByteCode.NEW, codegen.mainClassName);
// Call function constructor
cfw.add(ByteCode.DUP);
@ -2223,7 +2217,7 @@ class BodyCodegen
cfw.add(ByteCode.SWAP);
cfw.add(ByteCode.POP);
if (!codegen.itsUseDynamicScope) {
if (!compilerEnv.isUseDynamicScope()) {
cfw.add(ByteCode.DUP);
cfw.addInvoke(ByteCode.INVOKEINTERFACE,
"org/mozilla/javascript/Scriptable",
@ -2286,9 +2280,9 @@ class BodyCodegen
cfw.addInvoke(ByteCode.INVOKESTATIC,
codegen.mainClassName,
(type == Token.NEW)
? codegen.getDirectCtorName(target)
: codegen.getBodyMethodName(target),
codegen.getBodyMethodSignature(target));
? codegen.getDirectCtorName(target.fnode)
: codegen.getBodyMethodName(target.fnode),
codegen.getBodyMethodSignature(target.fnode));
int beyond = cfw.acquireLabel();
cfw.add(ByteCode.GOTO, beyond);
@ -3887,6 +3881,7 @@ class BodyCodegen
ClassFileWriter cfw;
Codegen codegen;
CompilerEnvirons compilerEnv;
ScriptOrFnNode scriptOrFn;
private OptFunctionNode fnCurrent;

View File

@ -39,81 +39,91 @@ package org.mozilla.javascript.optimizer;
import org.mozilla.javascript.*;
import java.util.*;
final class OptFunctionNode extends FunctionNode {
OptFunctionNode(String name) {
super(name);
final class OptFunctionNode
{
OptFunctionNode(FunctionNode fnode)
{
this.fnode = fnode;
int N = fnode.getParamAndVarCount();
int parameterCount = fnode.getParamCount();
optVars = new OptLocalVariable[N];
for (int i = 0; i != N; ++i) {
String name = fnode.getParamOrVarName(i);
optVars[i] = new OptLocalVariable(name, i < parameterCount);
}
fnode.setCompilerData(this);
}
static OptFunctionNode get(ScriptOrFnNode scriptOrFn, int i)
{
return (OptFunctionNode)scriptOrFn.getFunctionNode(i);
FunctionNode fnode = scriptOrFn.getFunctionNode(i);
return (OptFunctionNode)fnode.getCompilerData();
}
static OptFunctionNode get(ScriptOrFnNode scriptOrFn)
{
return (OptFunctionNode)scriptOrFn;
return (OptFunctionNode)scriptOrFn.getCompilerData();
}
void init()
boolean isTargetOfDirectCall()
{
int N = getParamAndVarCount();
int parameterCount = getParamCount();
optVars = new OptLocalVariable[N];
for (int i = 0; i != N; ++i) {
String name = getParamOrVarName(i);
optVars[i] = new OptLocalVariable(name, i < parameterCount);
}
}
boolean isTargetOfDirectCall() {
return directTargetIndex >= 0;
}
int getDirectTargetIndex() {
int getDirectTargetIndex()
{
return directTargetIndex;
}
void setDirectTargetIndex(int directTargetIndex) {
void setDirectTargetIndex(int directTargetIndex)
{
// One time action
if (directTargetIndex < 0 || this.directTargetIndex >= 0)
Kit.codeBug();
this.directTargetIndex = directTargetIndex;
}
void setParameterNumberContext(boolean b) {
void setParameterNumberContext(boolean b)
{
itsParameterNumberContext = b;
}
boolean getParameterNumberContext() {
boolean getParameterNumberContext()
{
return itsParameterNumberContext;
}
int getVarCount() {
int getVarCount()
{
return optVars.length;
}
OptLocalVariable getVar(int index) {
OptLocalVariable getVar(int index)
{
return optVars[index];
}
OptLocalVariable getVar(String name) {
int index = getParamOrVarIndex(name);
OptLocalVariable getVar(String name)
{
int index = fnode.getParamOrVarIndex(name);
if (index < 0) { return null; }
return optVars[index];
}
void establishVarsIndices() {
void establishVarsIndices()
{
int N = optVars.length;
for (int i = 0; i != N; i++) {
optVars[i].setIndex(i);
}
}
OptLocalVariable[] getVarsArray() {
OptLocalVariable[] getVarsArray()
{
return optVars;
}
FunctionNode fnode;
private OptLocalVariable[] optVars;
private int directTargetIndex = -1;
private boolean itsParameterNumberContext;

View File

@ -176,18 +176,20 @@ class OptTransformer extends NodeTransformer {
targetName = left.getFirstChild().getNext().getString();
}
if (targetName != null) {
OptFunctionNode fn;
fn = (OptFunctionNode)possibleDirectCalls.get(targetName);
if (fn != null && argCount == fn.getParamCount()) {
OptFunctionNode ofn;
ofn = (OptFunctionNode)possibleDirectCalls.get(targetName);
if (ofn != null
&& argCount == ofn.fnode.getParamCount())
{
// Refuse to directCall any function with more
// than 32 parameters - prevent code explosion
// for wacky test cases
if (argCount <= 32) {
node.putProp(Node.DIRECTCALL_PROP, fn);
if (!fn.isTargetOfDirectCall()) {
node.putProp(Node.DIRECTCALL_PROP, ofn);
if (!ofn.isTargetOfDirectCall()) {
int index = directCallTargets.size();
directCallTargets.add(fn);
fn.setDirectTargetIndex(index);
directCallTargets.add(ofn);
ofn.setDirectTargetIndex(index);
}
}
}

View File

@ -72,12 +72,12 @@ class Optimizer
private void optimizeFunction(OptFunctionNode theFunction)
{
if (theFunction.requiresActivation()) return;
if (theFunction.fnode.requiresActivation()) return;
inDirectCallFunction = theFunction.isTargetOfDirectCall();
ObjArray statementsArray = new ObjArray();
buildStatementList_r(theFunction, statementsArray);
buildStatementList_r(theFunction.fnode, statementsArray);
Node[] theStatementNodes = new Node[statementsArray.size()];
statementsArray.toArray(theStatementNodes);
@ -101,7 +101,7 @@ class Optimizer
typeFlow(theFunction, theBlocks);
findSinglyTypedVars(theFunction, theBlocks);
localCSE(theBlocks, theFunction);
if (!theFunction.requiresActivation()) {
if (!theFunction.fnode.requiresActivation()) {
/*
* Now that we know which local vars are in fact always
* Numbers, we re-write the tree to take advantage of
@ -634,8 +634,8 @@ class Optimizer
}
case Token.CALL :
{
FunctionNode target
= (FunctionNode)n.getProp(Node.DIRECTCALL_PROP);
OptFunctionNode target
= (OptFunctionNode)n.getProp(Node.DIRECTCALL_PROP);
if (target != null) {
/*
we leave each child as a Number if it can be. The codegen will