зеркало из https://github.com/mozilla/pjs.git
Do explicit parsing of function expression statements so a correct function type will be passed to IRFactory during node creation and fix incorrect code generation when FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME is true.
I also added explicit flags to Parser: languageVersion and allowMemberExprAsFunctionName and set them from Context. In this way Parser can be used without Context which is useful for debugging.
This commit is contained in:
Родитель
aab18156df
Коммит
c62a606a26
|
@ -847,7 +847,7 @@ public class Context {
|
||||||
boolean errorseen = false;
|
boolean errorseen = false;
|
||||||
try {
|
try {
|
||||||
IRFactory irf = new IRFactory(ts, null);
|
IRFactory irf = new IRFactory(ts, null);
|
||||||
Parser p = new Parser(irf);
|
Parser p = createParser(irf);
|
||||||
p.parse(ts);
|
p.parse(ts);
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
errorseen = true;
|
errorseen = true;
|
||||||
|
@ -2000,7 +2000,7 @@ public class Context {
|
||||||
|
|
||||||
errorCount = 0;
|
errorCount = 0;
|
||||||
IRFactory irf = compiler.createIRFactory(this, ts, scope);
|
IRFactory irf = compiler.createIRFactory(this, ts, scope);
|
||||||
Parser p = new Parser(irf);
|
Parser p = createParser(irf);
|
||||||
Node tree = (Node) p.parse(ts);
|
Node tree = (Node) p.parse(ts);
|
||||||
if (tree == null)
|
if (tree == null)
|
||||||
return null;
|
return null;
|
||||||
|
@ -2058,6 +2058,14 @@ public class Context {
|
||||||
return new Interpreter();
|
return new Interpreter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Parser createParser(IRFactory irf) {
|
||||||
|
Parser parser = new Parser(irf);
|
||||||
|
parser.setLanguageVersion(getLanguageVersion());
|
||||||
|
parser.setAllowMemberExprAsFunctionName(
|
||||||
|
hasFeature(Context.FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME));
|
||||||
|
return parser;
|
||||||
|
}
|
||||||
|
|
||||||
static String getSourcePositionFromStack(int[] linep) {
|
static String getSourcePositionFromStack(int[] linep) {
|
||||||
Context cx = getCurrentContext();
|
Context cx = getCurrentContext();
|
||||||
if (cx == null)
|
if (cx == null)
|
||||||
|
|
|
@ -210,15 +210,14 @@ public class IRFactory {
|
||||||
Object statements,
|
Object statements,
|
||||||
String sourceName, int baseLineno,
|
String sourceName, int baseLineno,
|
||||||
int endLineno, Object source,
|
int endLineno, Object source,
|
||||||
boolean isExpr)
|
int functionType)
|
||||||
{
|
{
|
||||||
if (name == null) {
|
if (name == null) {
|
||||||
name = "";
|
name = "";
|
||||||
}
|
}
|
||||||
FunctionNode f = (FunctionNode) createFunctionNode(name, statements);
|
FunctionNode f = (FunctionNode) createFunctionNode(name, statements);
|
||||||
f.argNames = argNames;
|
f.argNames = argNames;
|
||||||
f.setFunctionType(isExpr ? FunctionNode.FUNCTION_EXPRESSION
|
f.setFunctionType(functionType);
|
||||||
: FunctionNode.FUNCTION_STATEMENT);
|
|
||||||
f.putProp(Node.SOURCENAME_PROP, sourceName);
|
f.putProp(Node.SOURCENAME_PROP, sourceName);
|
||||||
f.putIntProp(Node.BASE_LINENO_PROP, baseLineno);
|
f.putIntProp(Node.BASE_LINENO_PROP, baseLineno);
|
||||||
f.putIntProp(Node.END_LINENO_PROP, endLineno);
|
f.putIntProp(Node.END_LINENO_PROP, endLineno);
|
||||||
|
@ -229,12 +228,6 @@ public class IRFactory {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFunctionExpressionStatement(Object o) {
|
|
||||||
Node n = (Node) o;
|
|
||||||
FunctionNode f = (FunctionNode) n.getProp(Node.FUNCTION_PROP);
|
|
||||||
f.setFunctionType(FunctionNode.FUNCTION_EXPRESSION_STATEMENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a child to the back of the given node. This function
|
* Add a child to the back of the given node. This function
|
||||||
* breaks the Factory abstraction, but it removes a requirement
|
* breaks the Factory abstraction, but it removes a requirement
|
||||||
|
|
|
@ -56,6 +56,14 @@ class Parser {
|
||||||
this.nf = nf;
|
this.nf = nf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setLanguageVersion(int languageVersion) {
|
||||||
|
this.languageVersion = languageVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAllowMemberExprAsFunctionName(boolean flag) {
|
||||||
|
this.allowMemberExprAsFunctionName = flag;
|
||||||
|
}
|
||||||
|
|
||||||
private void mustMatchToken(TokenStream ts, int toMatch, String messageId)
|
private void mustMatchToken(TokenStream ts, int toMatch, String messageId)
|
||||||
throws IOException, JavaScriptException
|
throws IOException, JavaScriptException
|
||||||
{
|
{
|
||||||
|
@ -94,15 +102,15 @@ class Parser {
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
this.ok = true;
|
this.ok = true;
|
||||||
sourceTop = 0;
|
fn_sourceTop = 0;
|
||||||
functionNumber = 0;
|
fn_functionNumber = 0;
|
||||||
|
|
||||||
int tt; // last token from getToken();
|
int tt; // last token from getToken();
|
||||||
int baseLineno = ts.getLineno(); // line number where source starts
|
int baseLineno = ts.getLineno(); // line number where source starts
|
||||||
|
|
||||||
/* so we have something to add nodes to until
|
/* so we have something to add nodes to until
|
||||||
* we've collected all the source */
|
* we've collected all the source */
|
||||||
Object tempBlock = nf.createLeaf(TokenStream.BLOCK);
|
Object pn = nf.createLeaf(TokenStream.BLOCK);
|
||||||
|
|
||||||
// Add script indicator
|
// Add script indicator
|
||||||
sourceAdd((char)ts.SCRIPT);
|
sourceAdd((char)ts.SCRIPT);
|
||||||
|
@ -116,17 +124,19 @@ class Parser {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object n;
|
||||||
if (tt == ts.FUNCTION) {
|
if (tt == ts.FUNCTION) {
|
||||||
try {
|
try {
|
||||||
nf.addChildToBack(tempBlock, function(ts, false));
|
n = function(ts, FunctionNode.FUNCTION_STATEMENT);
|
||||||
} catch (JavaScriptException e) {
|
} catch (JavaScriptException e) {
|
||||||
this.ok = false;
|
this.ok = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ts.ungetToken(tt);
|
ts.ungetToken(tt);
|
||||||
nf.addChildToBack(tempBlock, statement(ts));
|
n = statement(ts);
|
||||||
}
|
}
|
||||||
|
nf.addChildToBack(pn, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.ok) {
|
if (!this.ok) {
|
||||||
|
@ -136,9 +146,8 @@ class Parser {
|
||||||
|
|
||||||
String source = sourceToString(0);
|
String source = sourceToString(0);
|
||||||
sourceBuffer = null; // To help GC
|
sourceBuffer = null; // To help GC
|
||||||
Object pn = nf.createScript(tempBlock, ts.getSourceName(),
|
pn = nf.createScript(pn, ts.getSourceName(),
|
||||||
baseLineno, ts.getLineno(),
|
baseLineno, ts.getLineno(), source);
|
||||||
source);
|
|
||||||
return pn;
|
return pn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,12 +169,14 @@ class Parser {
|
||||||
try {
|
try {
|
||||||
int tt;
|
int tt;
|
||||||
while((tt = ts.peekToken()) > ts.EOF && tt != ts.RC) {
|
while((tt = ts.peekToken()) > ts.EOF && tt != ts.RC) {
|
||||||
|
Object n;
|
||||||
if (tt == TokenStream.FUNCTION) {
|
if (tt == TokenStream.FUNCTION) {
|
||||||
ts.getToken();
|
ts.getToken();
|
||||||
nf.addChildToBack(pn, function(ts, false));
|
n = function(ts, FunctionNode.FUNCTION_STATEMENT);
|
||||||
} else {
|
} else {
|
||||||
nf.addChildToBack(pn, statement(ts));
|
n = statement(ts);
|
||||||
}
|
}
|
||||||
|
nf.addChildToBack(pn, n);
|
||||||
}
|
}
|
||||||
} catch (JavaScriptException e) {
|
} catch (JavaScriptException e) {
|
||||||
this.ok = false;
|
this.ok = false;
|
||||||
|
@ -179,7 +190,7 @@ class Parser {
|
||||||
return pn;
|
return pn;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object function(TokenStream ts, boolean isExpr)
|
private Object function(TokenStream ts, int functionType)
|
||||||
throws IOException, JavaScriptException
|
throws IOException, JavaScriptException
|
||||||
{
|
{
|
||||||
int baseLineno = ts.getLineno(); // line number where source starts
|
int baseLineno = ts.getLineno(); // line number where source starts
|
||||||
|
@ -189,9 +200,7 @@ class Parser {
|
||||||
if (ts.matchToken(ts.NAME)) {
|
if (ts.matchToken(ts.NAME)) {
|
||||||
name = ts.getString();
|
name = ts.getString();
|
||||||
if (!ts.matchToken(ts.LP)) {
|
if (!ts.matchToken(ts.LP)) {
|
||||||
if (Context.getContext().hasFeature
|
if (allowMemberExprAsFunctionName) {
|
||||||
(Context.FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME))
|
|
||||||
{
|
|
||||||
// Extension to ECMA: if 'function <name>' does not follow
|
// Extension to ECMA: if 'function <name>' does not follow
|
||||||
// by '(', assume <name> starts memberExpr
|
// by '(', assume <name> starts memberExpr
|
||||||
sourceAddString(ts.NAME, name);
|
sourceAddString(ts.NAME, name);
|
||||||
|
@ -201,18 +210,14 @@ class Parser {
|
||||||
}
|
}
|
||||||
mustMatchToken(ts, ts.LP, "msg.no.paren.parms");
|
mustMatchToken(ts, ts.LP, "msg.no.paren.parms");
|
||||||
}
|
}
|
||||||
}
|
} else if (ts.matchToken(ts.LP)) {
|
||||||
else if (ts.matchToken(ts.LP)) {
|
|
||||||
// Anonymous function
|
// Anonymous function
|
||||||
name = null;
|
name = null;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
name = null;
|
name = null;
|
||||||
if (Context.getContext().hasFeature
|
if (allowMemberExprAsFunctionName) {
|
||||||
(Context.FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME))
|
|
||||||
{
|
|
||||||
// Note that memberExpr can not start with '(' like
|
// Note that memberExpr can not start with '(' like
|
||||||
// in (1+2).toString, because 'function (' already
|
// in function (1+2).toString(), because 'function (' already
|
||||||
// processed as anonymous function
|
// processed as anonymous function
|
||||||
memberExprNode = memberExpr(ts, false);
|
memberExprNode = memberExpr(ts, false);
|
||||||
}
|
}
|
||||||
|
@ -228,18 +233,18 @@ class Parser {
|
||||||
|
|
||||||
// save a reference to the function in the enclosing source.
|
// save a reference to the function in the enclosing source.
|
||||||
sourceAdd((char) ts.FUNCTION);
|
sourceAdd((char) ts.FUNCTION);
|
||||||
sourceAdd((char)functionNumber);
|
sourceAdd((char)fn_functionNumber);
|
||||||
++functionNumber;
|
++fn_functionNumber;
|
||||||
|
|
||||||
// Save current source top to restore it on exit not to include
|
// Save current source top to restore it on exit not to include
|
||||||
// function to parent source
|
// function to parent source
|
||||||
int savedSourceTop = sourceTop;
|
int saved_sourceTop = fn_sourceTop;
|
||||||
int savedFunctionNumber = functionNumber;
|
int saved_functionNumber = fn_functionNumber;
|
||||||
ObjArray args = new ObjArray();
|
ObjArray args = new ObjArray();
|
||||||
Object body;
|
Object body;
|
||||||
String source;
|
String source;
|
||||||
try {
|
try {
|
||||||
functionNumber = 0;
|
fn_functionNumber = 0;
|
||||||
|
|
||||||
// FUNCTION as the first token in a Source means it's a function
|
// FUNCTION as the first token in a Source means it's a function
|
||||||
// definition, and not a reference.
|
// definition, and not a reference.
|
||||||
|
@ -272,34 +277,48 @@ class Parser {
|
||||||
// skip the last EOL so nested functions work...
|
// skip the last EOL so nested functions work...
|
||||||
|
|
||||||
// name might be null;
|
// name might be null;
|
||||||
source = sourceToString(savedSourceTop);
|
source = sourceToString(saved_sourceTop);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
sourceTop = savedSourceTop;
|
fn_sourceTop = saved_sourceTop;
|
||||||
functionNumber = savedFunctionNumber;
|
fn_functionNumber = saved_functionNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
Object pn = nf.createFunction(name, args, body,
|
Object pn;
|
||||||
ts.getSourceName(),
|
if (memberExprNode == null) {
|
||||||
baseLineno, ts.getLineno(),
|
pn = nf.createFunction(name, args, body,
|
||||||
source,
|
ts.getSourceName(),
|
||||||
isExpr || memberExprNode != null);
|
baseLineno, ts.getLineno(),
|
||||||
if (memberExprNode != null) {
|
source,
|
||||||
|
functionType);
|
||||||
|
if (functionType == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) {
|
||||||
|
// The following can be removed but then code generators should
|
||||||
|
// be modified not to push on the stack function expression
|
||||||
|
// statements
|
||||||
|
pn = nf.createExprStatement(pn, baseLineno);
|
||||||
|
}
|
||||||
|
// Add EOL but only if function is not part of expression, in which
|
||||||
|
// case it gets SEMI + EOL from Statement.
|
||||||
|
if (functionType != FunctionNode.FUNCTION_EXPRESSION) {
|
||||||
|
sourceAdd((char)ts.EOL);
|
||||||
|
checkWellTerminatedFunction(ts);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pn = nf.createFunction(name, args, body,
|
||||||
|
ts.getSourceName(),
|
||||||
|
baseLineno, ts.getLineno(),
|
||||||
|
source,
|
||||||
|
FunctionNode.FUNCTION_EXPRESSION);
|
||||||
pn = nf.createBinary(ts.ASSIGN, ts.NOP, memberExprNode, pn);
|
pn = nf.createBinary(ts.ASSIGN, ts.NOP, memberExprNode, pn);
|
||||||
}
|
if (functionType != FunctionNode.FUNCTION_EXPRESSION) {
|
||||||
|
pn = nf.createExprStatement(pn, baseLineno);
|
||||||
// Add EOL but only if function is not part of expression, in which
|
|
||||||
// case it gets SEMI + EOL from Statement.
|
|
||||||
if (!isExpr) {
|
|
||||||
if (memberExprNode != null) {
|
|
||||||
// Add ';' to make 'function x.f(){}' and 'x.f = function(){}'
|
// Add ';' to make 'function x.f(){}' and 'x.f = function(){}'
|
||||||
// to print the same strings when decompiling
|
// to print the same strings when decompiling
|
||||||
sourceAdd((char)ts.SEMI);
|
sourceAdd((char)ts.SEMI);
|
||||||
|
sourceAdd((char)ts.EOL);
|
||||||
|
checkWellTerminatedFunction(ts);
|
||||||
}
|
}
|
||||||
sourceAdd((char)ts.EOL);
|
|
||||||
wellTerminated(ts, ts.FUNCTION);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return pn;
|
return pn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,31 +350,39 @@ class Parser {
|
||||||
return pn;
|
return pn;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean wellTerminated(TokenStream ts, int lastExprType)
|
private void checkWellTerminated(TokenStream ts)
|
||||||
throws IOException, JavaScriptException
|
throws IOException, JavaScriptException
|
||||||
{
|
{
|
||||||
int tt = ts.peekTokenSameLine();
|
int tt = ts.peekTokenSameLine();
|
||||||
if (tt == ts.ERROR) {
|
switch (tt) {
|
||||||
return false;
|
case TokenStream.ERROR:
|
||||||
}
|
case TokenStream.EOF:
|
||||||
|
case TokenStream.EOL:
|
||||||
|
case TokenStream.SEMI:
|
||||||
|
case TokenStream.RC:
|
||||||
|
return;
|
||||||
|
|
||||||
if (tt != ts.EOF && tt != ts.EOL
|
case TokenStream.FUNCTION:
|
||||||
&& tt != ts.SEMI && tt != ts.RC)
|
if (languageVersion < Context.VERSION_1_2) {
|
||||||
{
|
/*
|
||||||
int version = Context.getContext().getLanguageVersion();
|
* Checking against version < 1.2 and version >= 1.0
|
||||||
if ((tt == ts.FUNCTION || lastExprType == ts.FUNCTION) &&
|
* in the above line breaks old javascript, so we keep it
|
||||||
(version < Context.VERSION_1_2)) {
|
* this way for now... XXX warning needed?
|
||||||
/*
|
*/
|
||||||
* Checking against version < 1.2 and version >= 1.0
|
return;
|
||||||
* in the above line breaks old javascript, so we keep it
|
|
||||||
* this way for now... XXX warning needed?
|
|
||||||
*/
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
reportError(ts, "msg.no.semi.stmt");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true;
|
}
|
||||||
|
reportError(ts, "msg.no.semi.stmt");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkWellTerminatedFunction(TokenStream ts)
|
||||||
|
throws IOException, JavaScriptException
|
||||||
|
{
|
||||||
|
if (languageVersion < Context.VERSION_1_2) {
|
||||||
|
// See comments in checkWellTerminated
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
checkWellTerminated(ts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// match a NAME; return null if no match.
|
// match a NAME; return null if no match.
|
||||||
|
@ -373,7 +400,7 @@ class Parser {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lineno == ts.getLineno())
|
if (lineno == ts.getLineno())
|
||||||
wellTerminated(ts, ts.ERROR);
|
checkWellTerminated(ts);
|
||||||
|
|
||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
@ -411,8 +438,6 @@ class Parser {
|
||||||
|
|
||||||
int tt;
|
int tt;
|
||||||
|
|
||||||
int lastExprType = 0; // For wellTerminated. 0 to avoid warning.
|
|
||||||
|
|
||||||
tt = ts.getToken();
|
tt = ts.getToken();
|
||||||
|
|
||||||
switch(tt) {
|
switch(tt) {
|
||||||
|
@ -680,7 +705,7 @@ class Parser {
|
||||||
sourceAdd((char)ts.THROW);
|
sourceAdd((char)ts.THROW);
|
||||||
pn = nf.createThrow(expr(ts, false), lineno);
|
pn = nf.createThrow(expr(ts, false), lineno);
|
||||||
if (lineno == ts.getLineno())
|
if (lineno == ts.getLineno())
|
||||||
wellTerminated(ts, ts.ERROR);
|
checkWellTerminated(ts);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TokenStream.BREAK: {
|
case TokenStream.BREAK: {
|
||||||
|
@ -734,7 +759,7 @@ class Parser {
|
||||||
int lineno = ts.getLineno();
|
int lineno = ts.getLineno();
|
||||||
pn = variables(ts, false);
|
pn = variables(ts, false);
|
||||||
if (ts.getLineno() == lineno)
|
if (ts.getLineno() == lineno)
|
||||||
wellTerminated(ts, ts.ERROR);
|
checkWellTerminated(ts);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TokenStream.RETURN: {
|
case TokenStream.RETURN: {
|
||||||
|
@ -755,7 +780,7 @@ class Parser {
|
||||||
if (tt != ts.EOF && tt != ts.EOL && tt != ts.SEMI && tt != ts.RC) {
|
if (tt != ts.EOF && tt != ts.EOL && tt != ts.SEMI && tt != ts.RC) {
|
||||||
retExpr = expr(ts, false);
|
retExpr = expr(ts, false);
|
||||||
if (ts.getLineno() == lineno)
|
if (ts.getLineno() == lineno)
|
||||||
wellTerminated(ts, ts.ERROR);
|
checkWellTerminated(ts);
|
||||||
ts.flags |= ts.TSF_RETURN_EXPR;
|
ts.flags |= ts.TSF_RETURN_EXPR;
|
||||||
} else {
|
} else {
|
||||||
ts.flags |= ts.TSF_RETURN_VOID;
|
ts.flags |= ts.TSF_RETURN_VOID;
|
||||||
|
@ -780,8 +805,13 @@ class Parser {
|
||||||
skipsemi = true;
|
skipsemi = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case TokenStream.FUNCTION: {
|
||||||
|
pn = function(ts, FunctionNode.FUNCTION_EXPRESSION_STATEMENT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
lastExprType = tt;
|
int lastExprType = tt;
|
||||||
int tokenno = ts.getTokenno();
|
int tokenno = ts.getTokenno();
|
||||||
ts.ungetToken(tt);
|
ts.ungetToken(tt);
|
||||||
int lineno = ts.getLineno();
|
int lineno = ts.getLineno();
|
||||||
|
@ -811,32 +841,10 @@ class Parser {
|
||||||
return pn;
|
return pn;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastExprType == ts.FUNCTION) {
|
|
||||||
if (nf.getLeafType(pn) != ts.FUNCTION) {
|
|
||||||
reportError(ts, "msg.syntax");
|
|
||||||
}
|
|
||||||
nf.setFunctionExpressionStatement(pn);
|
|
||||||
}
|
|
||||||
|
|
||||||
pn = nf.createExprStatement(pn, lineno);
|
pn = nf.createExprStatement(pn, lineno);
|
||||||
|
|
||||||
/*
|
if (ts.getLineno() == lineno) {
|
||||||
* Check explicitly against (multi-line) function
|
checkWellTerminated(ts);
|
||||||
* statement.
|
|
||||||
|
|
||||||
* lastExprEndLine is a hack to fix an
|
|
||||||
* automatic semicolon insertion problem with function
|
|
||||||
* expressions; the ts.getLineno() == lineno check was
|
|
||||||
* firing after a function definition even though the
|
|
||||||
* next statement was on a new line, because
|
|
||||||
* speculative getToken calls advanced the line number
|
|
||||||
* even when they didn't succeed.
|
|
||||||
*/
|
|
||||||
if (ts.getLineno() == lineno ||
|
|
||||||
(lastExprType == ts.FUNCTION &&
|
|
||||||
ts.getLineno() == lastExprEndLine))
|
|
||||||
{
|
|
||||||
wellTerminated(ts, lastExprType);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1206,7 +1214,6 @@ class Parser {
|
||||||
Object pn)
|
Object pn)
|
||||||
throws IOException, JavaScriptException
|
throws IOException, JavaScriptException
|
||||||
{
|
{
|
||||||
lastExprEndLine = ts.getLineno();
|
|
||||||
int tt;
|
int tt;
|
||||||
while ((tt = ts.getToken()) > ts.EOF) {
|
while ((tt = ts.getToken()) > ts.EOF) {
|
||||||
if (tt == ts.DOT) {
|
if (tt == ts.DOT) {
|
||||||
|
@ -1220,14 +1227,12 @@ class Parser {
|
||||||
* is the version in Brendan's IR C version. Not in ECMA...
|
* is the version in Brendan's IR C version. Not in ECMA...
|
||||||
* does it reflect the 'new' operator syntax he mentioned?
|
* does it reflect the 'new' operator syntax he mentioned?
|
||||||
*/
|
*/
|
||||||
lastExprEndLine = ts.getLineno();
|
|
||||||
} else if (tt == ts.LB) {
|
} else if (tt == ts.LB) {
|
||||||
sourceAdd((char)ts.LB);
|
sourceAdd((char)ts.LB);
|
||||||
pn = nf.createBinary(ts.LB, pn, expr(ts, false));
|
pn = nf.createBinary(ts.LB, pn, expr(ts, false));
|
||||||
|
|
||||||
mustMatchToken(ts, ts.RB, "msg.no.bracket.index");
|
mustMatchToken(ts, ts.RB, "msg.no.bracket.index");
|
||||||
sourceAdd((char)ts.RB);
|
sourceAdd((char)ts.RB);
|
||||||
lastExprEndLine = ts.getLineno();
|
|
||||||
} else if (allowCallSyntax && tt == ts.LP) {
|
} else if (allowCallSyntax && tt == ts.LP) {
|
||||||
/* make a call node */
|
/* make a call node */
|
||||||
|
|
||||||
|
@ -1236,7 +1241,6 @@ class Parser {
|
||||||
|
|
||||||
/* Add the arguments to pn, if any are supplied. */
|
/* Add the arguments to pn, if any are supplied. */
|
||||||
pn = argumentList(ts, pn);
|
pn = argumentList(ts, pn);
|
||||||
lastExprEndLine = ts.getLineno();
|
|
||||||
} else {
|
} else {
|
||||||
ts.ungetToken(tt);
|
ts.ungetToken(tt);
|
||||||
|
|
||||||
|
@ -1260,7 +1264,7 @@ class Parser {
|
||||||
switch(tt) {
|
switch(tt) {
|
||||||
|
|
||||||
case TokenStream.FUNCTION:
|
case TokenStream.FUNCTION:
|
||||||
return function(ts, true);
|
return function(ts, FunctionNode.FUNCTION_EXPRESSION);
|
||||||
|
|
||||||
case TokenStream.LB:
|
case TokenStream.LB:
|
||||||
{
|
{
|
||||||
|
@ -1455,11 +1459,11 @@ class Parser {
|
||||||
|
|
||||||
*/
|
*/
|
||||||
private void sourceAdd(char c) {
|
private void sourceAdd(char c) {
|
||||||
if (sourceTop == sourceBuffer.length) {
|
if (fn_sourceTop == sourceBuffer.length) {
|
||||||
increaseSourceCapacity(sourceTop + 1);
|
increaseSourceCapacity(fn_sourceTop + 1);
|
||||||
}
|
}
|
||||||
sourceBuffer[sourceTop] = c;
|
sourceBuffer[fn_sourceTop] = c;
|
||||||
++sourceTop;
|
++fn_sourceTop;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sourceAddString(int type, String str) {
|
private void sourceAddString(int type, String str) {
|
||||||
|
@ -1473,20 +1477,20 @@ class Parser {
|
||||||
if (L >= 0x8000) {
|
if (L >= 0x8000) {
|
||||||
lengthEncodingSize = 2;
|
lengthEncodingSize = 2;
|
||||||
}
|
}
|
||||||
int nextTop = sourceTop + lengthEncodingSize + L;
|
int nextTop = fn_sourceTop + lengthEncodingSize + L;
|
||||||
if (nextTop > sourceBuffer.length) {
|
if (nextTop > sourceBuffer.length) {
|
||||||
increaseSourceCapacity(nextTop);
|
increaseSourceCapacity(nextTop);
|
||||||
}
|
}
|
||||||
if (L >= 0x8000) {
|
if (L >= 0x8000) {
|
||||||
// Use 2 chars to encode strings exceeding 32K, were the highest
|
// Use 2 chars to encode strings exceeding 32K, were the highest
|
||||||
// bit in the first char indicates presence of the next byte
|
// bit in the first char indicates presence of the next byte
|
||||||
sourceBuffer[sourceTop] = (char)(0x8000 | (L >>> 16));
|
sourceBuffer[fn_sourceTop] = (char)(0x8000 | (L >>> 16));
|
||||||
++sourceTop;
|
++fn_sourceTop;
|
||||||
}
|
}
|
||||||
sourceBuffer[sourceTop] = (char)L;
|
sourceBuffer[fn_sourceTop] = (char)L;
|
||||||
++sourceTop;
|
++fn_sourceTop;
|
||||||
str.getChars(0, L, sourceBuffer, sourceTop);
|
str.getChars(0, L, sourceBuffer, fn_sourceTop);
|
||||||
sourceTop = nextTop;
|
fn_sourceTop = nextTop;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sourceAddNumber(double n) {
|
private void sourceAddNumber(double n) {
|
||||||
|
@ -1523,7 +1527,7 @@ class Parser {
|
||||||
else {
|
else {
|
||||||
// we can ignore negative values, bc they're already prefixed
|
// we can ignore negative values, bc they're already prefixed
|
||||||
// by UNARYOP SUB
|
// by UNARYOP SUB
|
||||||
if (Context.check && lbits < 0) Context.codeBug();
|
if (lbits < 0) Context.codeBug();
|
||||||
|
|
||||||
// will it fit in a char?
|
// will it fit in a char?
|
||||||
// this gives a short encoding for integer values up to 2^16.
|
// this gives a short encoding for integer values up to 2^16.
|
||||||
|
@ -1543,21 +1547,19 @@ class Parser {
|
||||||
|
|
||||||
private void increaseSourceCapacity(int minimalCapacity) {
|
private void increaseSourceCapacity(int minimalCapacity) {
|
||||||
// Call this only when capacity increase is must
|
// Call this only when capacity increase is must
|
||||||
if (Context.check && minimalCapacity <= sourceBuffer.length)
|
if (minimalCapacity <= sourceBuffer.length) Context.codeBug();
|
||||||
Context.codeBug();
|
|
||||||
int newCapacity = sourceBuffer.length * 2;
|
int newCapacity = sourceBuffer.length * 2;
|
||||||
if (newCapacity < minimalCapacity) {
|
if (newCapacity < minimalCapacity) {
|
||||||
newCapacity = minimalCapacity;
|
newCapacity = minimalCapacity;
|
||||||
}
|
}
|
||||||
char[] tmp = new char[newCapacity];
|
char[] tmp = new char[newCapacity];
|
||||||
System.arraycopy(sourceBuffer, 0, tmp, 0, sourceTop);
|
System.arraycopy(sourceBuffer, 0, tmp, 0, fn_sourceTop);
|
||||||
sourceBuffer = tmp;
|
sourceBuffer = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String sourceToString(int offset) {
|
private String sourceToString(int offset) {
|
||||||
if (Context.check && (offset < 0 || sourceTop < offset))
|
if (offset < 0 || fn_sourceTop < offset) Context.codeBug();
|
||||||
Context.codeBug();
|
return new String(sourceBuffer, offset, fn_sourceTop - offset);
|
||||||
return new String(sourceBuffer, offset, sourceTop - offset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1605,6 +1607,9 @@ class Parser {
|
||||||
int indent, int type, boolean justbody,
|
int indent, int type, boolean justbody,
|
||||||
Object[] srcData, StringBuffer result)
|
Object[] srcData, StringBuffer result)
|
||||||
{
|
{
|
||||||
|
final int OFFSET = 4; // how much to indent
|
||||||
|
final int SETBACK = 2; // less how much for case labels
|
||||||
|
|
||||||
String source;
|
String source;
|
||||||
Object[] childNodes = null;
|
Object[] childNodes = null;
|
||||||
if (encodedSourcesTree == null) {
|
if (encodedSourcesTree == null) {
|
||||||
|
@ -2254,20 +2259,22 @@ class Parser {
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int lastExprEndLine; // Hack to handle function expr termination.
|
|
||||||
private IRFactory nf;
|
private IRFactory nf;
|
||||||
private ErrorReporter er;
|
private int languageVersion = Context.VERSION_DEFAULT;
|
||||||
|
private boolean allowMemberExprAsFunctionName = false;
|
||||||
|
|
||||||
private boolean ok; // Did the parse encounter an error?
|
private boolean ok; // Did the parse encounter an error?
|
||||||
|
|
||||||
private char[] sourceBuffer = new char[128];
|
private char[] sourceBuffer = new char[128];
|
||||||
private int sourceTop;
|
|
||||||
private int functionNumber;
|
|
||||||
|
|
||||||
// how much to indent
|
// fn_ prefix means per-function data that should be reset/restored in function
|
||||||
private final static int OFFSET = 4;
|
|
||||||
|
|
||||||
// less how much for case labels
|
// Per function source buffer top: nested functions sources are not
|
||||||
private final static int SETBACK = 2;
|
// included in parent.
|
||||||
|
private int fn_sourceTop;
|
||||||
|
|
||||||
|
// Nested function number
|
||||||
|
private int fn_functionNumber;
|
||||||
|
|
||||||
private static final int TOP_LEVEL_SCRIPT_OR_FUNCTION = 0;
|
private static final int TOP_LEVEL_SCRIPT_OR_FUNCTION = 0;
|
||||||
private static final int CONSTRUCTED_FUNCTION = 1;
|
private static final int CONSTRUCTED_FUNCTION = 1;
|
||||||
|
|
|
@ -667,10 +667,6 @@ public class TokenStream {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearPushback() {
|
|
||||||
this.pushbackToken = EOF;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ungetToken(int tt) {
|
public void ungetToken(int tt) {
|
||||||
// Can not unread more then one token
|
// Can not unread more then one token
|
||||||
if (this.pushbackToken != EOF && tt != ERROR) Context.codeBug();
|
if (this.pushbackToken != EOF && tt != ERROR) Context.codeBug();
|
||||||
|
@ -687,13 +683,15 @@ public class TokenStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
public int peekTokenSameLine() throws IOException {
|
public int peekTokenSameLine() throws IOException {
|
||||||
int result;
|
|
||||||
|
|
||||||
flags |= TSF_NEWLINES; // SCAN_NEWLINES from jsscan.h
|
flags |= TSF_NEWLINES; // SCAN_NEWLINES from jsscan.h
|
||||||
result = peekToken();
|
int result = getToken();
|
||||||
flags &= ~TSF_NEWLINES; // HIDE_NEWLINES from jsscan.h
|
if (result == EOL) {
|
||||||
if (this.pushbackToken == EOL)
|
|
||||||
this.pushbackToken = EOF;
|
this.pushbackToken = EOF;
|
||||||
|
} else {
|
||||||
|
this.pushbackToken = result;
|
||||||
|
}
|
||||||
|
tokenno--;
|
||||||
|
flags &= ~TSF_NEWLINES; // HIDE_NEWLINES from jsscan.h
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -718,7 +716,7 @@ public class TokenStream {
|
||||||
} else if (c == '\n') {
|
} else if (c == '\n') {
|
||||||
flags &= ~TSF_DIRTYLINE;
|
flags &= ~TSF_DIRTYLINE;
|
||||||
if ((flags & TSF_NEWLINES) != 0) {
|
if ((flags & TSF_NEWLINES) != 0) {
|
||||||
break;
|
return EOL;
|
||||||
}
|
}
|
||||||
} else if (!isJSSpace(c)) {
|
} else if (!isJSSpace(c)) {
|
||||||
if (c != '-') {
|
if (c != '-') {
|
||||||
|
@ -1024,7 +1022,6 @@ public class TokenStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case '\n': return EOL;
|
|
||||||
case ';': return SEMI;
|
case ';': return SEMI;
|
||||||
case '[': return LB;
|
case '[': return LB;
|
||||||
case ']': return RB;
|
case ']': return RB;
|
||||||
|
@ -1331,12 +1328,14 @@ public class TokenStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addToString(int c) {
|
private void addToString(int c) {
|
||||||
if (stringBufferTop == stringBuffer.length) {
|
int N = stringBufferTop;
|
||||||
|
if (N == stringBuffer.length) {
|
||||||
char[] tmp = new char[stringBuffer.length * 2];
|
char[] tmp = new char[stringBuffer.length * 2];
|
||||||
System.arraycopy(stringBuffer, 0, tmp, 0, stringBufferTop);
|
System.arraycopy(stringBuffer, 0, tmp, 0, N);
|
||||||
stringBuffer = tmp;
|
stringBuffer = tmp;
|
||||||
}
|
}
|
||||||
stringBuffer[stringBufferTop++] = (char)c;
|
stringBuffer[N] = (char)c;
|
||||||
|
stringBufferTop = N + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reportSyntaxError(String messageProperty, Object[] args) {
|
public void reportSyntaxError(String messageProperty, Object[] args) {
|
||||||
|
|
Загрузка…
Ссылка в новой задаче