зеркало из https://github.com/mozilla/pjs.git
Implement block scope (let) and array comprehensions.
This required significant changes to symbol table management.
This commit is contained in:
Родитель
0228403e1c
Коммит
7389c08ecb
|
@ -590,6 +590,10 @@ public class Decompiler
|
|||
result.append("var ");
|
||||
break;
|
||||
|
||||
case Token.LET:
|
||||
result.append("let ");
|
||||
break;
|
||||
|
||||
case Token.SEMI:
|
||||
result.append(';');
|
||||
if (Token.EOL != getNext(source, length, i)) {
|
||||
|
|
|
@ -356,32 +356,16 @@ final class IRFactory
|
|||
if (functionCount != 0) {
|
||||
// Functions containing other functions require activation objects
|
||||
fnNode.itsNeedsActivation = true;
|
||||
for (int i = 0; i != functionCount; ++i) {
|
||||
FunctionNode fn = fnNode.getFunctionNode(i);
|
||||
// nested function expression statements overrides var
|
||||
if (fn.getFunctionType()
|
||||
== FunctionNode.FUNCTION_EXPRESSION_STATEMENT)
|
||||
{
|
||||
String name = fn.getFunctionName();
|
||||
if (name != null && name.length() != 0) {
|
||||
fnNode.removeParamOrVar(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (functionType == FunctionNode.FUNCTION_EXPRESSION) {
|
||||
String name = fnNode.getFunctionName();
|
||||
if (name != null && name.length() != 0
|
||||
&& !fnNode.hasParamOrVar(name))
|
||||
{
|
||||
if (name != null && name.length() != 0) {
|
||||
// A function expression needs to have its name as a
|
||||
// variable (if it isn't already allocated as a variable).
|
||||
// See ECMA Ch. 13. We add code to the beginning of the
|
||||
// function to initialize a local variable of the
|
||||
// function's name to the function value.
|
||||
if (fnNode.addVar(name) == ScriptOrFnNode.DUPLICATE_CONST)
|
||||
parser.addError("msg.const.redecl", name);
|
||||
Node setFn = new Node(Token.EXPR_VOID,
|
||||
new Node(Token.SETNAME,
|
||||
Node.newString(Token.BINDNAME, name),
|
||||
|
@ -412,6 +396,18 @@ final class IRFactory
|
|||
parent.addChildToBack(child);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a node that can be used to hold lexically scoped variable
|
||||
* definitions (via let declarations).
|
||||
*
|
||||
* @param token the token of the node to create
|
||||
* @param lineno line number of source
|
||||
* @return the created node
|
||||
*/
|
||||
Node createScopeNode(int token, int lineno) {
|
||||
return new Node.Scope(token, lineno);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create loop node. The parser will later call
|
||||
* createWhile|createDoWhile|createFor|createForIn
|
||||
|
@ -419,7 +415,7 @@ final class IRFactory
|
|||
*/
|
||||
Node createLoopNode(Node loopLabel, int lineno)
|
||||
{
|
||||
Node.Jump result = new Node.Jump(Token.LOOP, lineno);
|
||||
Node.Jump result = new Node.Scope(Token.LOOP, lineno);
|
||||
if (loopLabel != null) {
|
||||
((Node.Jump)loopLabel).setLoop(result);
|
||||
}
|
||||
|
@ -483,8 +479,9 @@ final class IRFactory
|
|||
loop.addChildToFront(makeJump(Token.GOTO, condTarget));
|
||||
|
||||
if (loopType == LOOP_FOR) {
|
||||
if (init.getType() != Token.EMPTY) {
|
||||
if (init.getType() != Token.VAR) {
|
||||
int initType = init.getType();
|
||||
if (initType != Token.EMPTY) {
|
||||
if (initType != Token.VAR && initType != Token.LET) {
|
||||
init = new Node(Token.EXPR_VOID, init);
|
||||
}
|
||||
loop.addChildToFront(init);
|
||||
|
@ -514,7 +511,7 @@ final class IRFactory
|
|||
int type = lhs.getType();
|
||||
|
||||
Node lvalue;
|
||||
if (type == Token.VAR) {
|
||||
if (type == Token.VAR || type == Token.LET) {
|
||||
/*
|
||||
* check that there was only one variable given.
|
||||
* we can't do this in the parser, because then the
|
||||
|
@ -552,7 +549,7 @@ final class IRFactory
|
|||
|
||||
loop = createWhile(loop, cond, newBody);
|
||||
loop.addChildToFront(init);
|
||||
if (type == Token.VAR)
|
||||
if (type == Token.VAR || type == Token.LET)
|
||||
loop.addChildToFront(lhs);
|
||||
localBlock.addChildToBack(loop);
|
||||
|
||||
|
@ -1290,11 +1287,8 @@ final class IRFactory
|
|||
int nodeType = left.getType();
|
||||
switch (nodeType) {
|
||||
case Token.NAME: {
|
||||
String s = left.getString();
|
||||
|
||||
Node opLeft = Node.newString(Token.NAME, s);
|
||||
Node op = new Node(assignOp, opLeft, right);
|
||||
Node lvalueLeft = Node.newString(Token.BINDNAME, s);
|
||||
Node op = new Node(assignOp, left, right);
|
||||
Node lvalueLeft = Node.newString(Token.BINDNAME, left.getString());
|
||||
return new Node(Token.SETNAME, lvalueLeft, op);
|
||||
}
|
||||
case Token.GETPROP:
|
||||
|
@ -1374,30 +1368,6 @@ final class IRFactory
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Commented-out: no longer used
|
||||
// private static boolean hasSideEffects(Node exprTree)
|
||||
// {
|
||||
// switch (exprTree.getType()) {
|
||||
// case Token.INC:
|
||||
// case Token.DEC:
|
||||
// case Token.SETPROP:
|
||||
// case Token.SETELEM:
|
||||
// case Token.SETNAME:
|
||||
// case Token.CALL:
|
||||
// case Token.NEW:
|
||||
// return true;
|
||||
// default:
|
||||
// Node child = exprTree.getFirstChild();
|
||||
// while (child != null) {
|
||||
// if (hasSideEffects(child))
|
||||
// return true;
|
||||
// child = child.getNext();
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
|
||||
private void checkActivationName(String name, int token)
|
||||
{
|
||||
|
|
|
@ -550,7 +550,7 @@ public class Interpreter
|
|||
|
||||
generateRegExpLiterals();
|
||||
|
||||
visitStatement(tree);
|
||||
visitStatement(tree, 0);
|
||||
fixLabelGotos();
|
||||
// add RETURN_RESULT only to scripts as function always ends with RETURN
|
||||
if (itsData.itsFunctionType == 0) {
|
||||
|
@ -666,7 +666,7 @@ public class Interpreter
|
|||
throw new RuntimeException(node.toString());
|
||||
}
|
||||
|
||||
private void visitStatement(Node node)
|
||||
private void visitStatement(Node node, int initialStackDepth)
|
||||
{
|
||||
int type = node.getType();
|
||||
Node child = node.getFirstChild();
|
||||
|
@ -712,7 +712,7 @@ public class Interpreter
|
|||
case Token.WITH:
|
||||
updateLineNumber(node);
|
||||
while (child != null) {
|
||||
visitStatement(child);
|
||||
visitStatement(child, initialStackDepth);
|
||||
child = child.getNext();
|
||||
}
|
||||
break;
|
||||
|
@ -733,7 +733,7 @@ public class Interpreter
|
|||
node.putIntProp(Node.LOCAL_PROP, local);
|
||||
updateLineNumber(node);
|
||||
while (child != null) {
|
||||
visitStatement(child);
|
||||
visitStatement(child, initialStackDepth);
|
||||
child = child.getNext();
|
||||
}
|
||||
addIndexOp(Icode_LOCAL_CLEAR, local);
|
||||
|
@ -805,7 +805,7 @@ public class Interpreter
|
|||
addIndexOp(Icode_STARTSUB, finallyRegister);
|
||||
stackChange(-1);
|
||||
while (child != null) {
|
||||
visitStatement(child);
|
||||
visitStatement(child, initialStackDepth);
|
||||
child = child.getNext();
|
||||
}
|
||||
addIndexOp(Icode_RETSUB, finallyRegister);
|
||||
|
@ -830,7 +830,7 @@ public class Interpreter
|
|||
|
||||
int tryStart = itsICodeTop;
|
||||
while (child != null) {
|
||||
visitStatement(child);
|
||||
visitStatement(child, initialStackDepth);
|
||||
child = child.getNext();
|
||||
}
|
||||
|
||||
|
@ -918,7 +918,7 @@ public class Interpreter
|
|||
throw badTree(node);
|
||||
}
|
||||
|
||||
if (itsStackDepth != 0) {
|
||||
if (itsStackDepth != initialStackDepth) {
|
||||
throw Kit.codeBug();
|
||||
}
|
||||
}
|
||||
|
@ -1191,14 +1191,13 @@ public class Interpreter
|
|||
|
||||
case Token.TYPEOFNAME:
|
||||
{
|
||||
String name = node.getString();
|
||||
int index = -1;
|
||||
// use typeofname if an activation frame exists
|
||||
// since the vars all exist there instead of in jregs
|
||||
if (itsInFunctionFlag && !itsData.itsNeedsActivation)
|
||||
index = scriptOrFn.getParamOrVarIndex(name);
|
||||
index = scriptOrFn.getIndexForNameNode(node);
|
||||
if (index == -1) {
|
||||
addStringOp(Icode_TYPEOFNAME, name);
|
||||
addStringOp(Icode_TYPEOFNAME, node.getString());
|
||||
stackChange(1);
|
||||
} else {
|
||||
addVarOp(Token.GETVAR, index);
|
||||
|
@ -1252,8 +1251,7 @@ public class Interpreter
|
|||
case Token.GETVAR:
|
||||
{
|
||||
if (itsData.itsNeedsActivation) Kit.codeBug();
|
||||
String name = node.getString();
|
||||
int index = scriptOrFn.getParamOrVarIndex(name);
|
||||
int index = scriptOrFn.getIndexForNameNode(node);
|
||||
addVarOp(Token.GETVAR, index);
|
||||
stackChange(1);
|
||||
}
|
||||
|
@ -1262,10 +1260,9 @@ public class Interpreter
|
|||
case Token.SETVAR:
|
||||
{
|
||||
if (itsData.itsNeedsActivation) Kit.codeBug();
|
||||
String name = child.getString();
|
||||
int index = scriptOrFn.getIndexForNameNode(child);
|
||||
child = child.getNext();
|
||||
visitExpression(child, 0);
|
||||
int index = scriptOrFn.getParamOrVarIndex(name);
|
||||
addVarOp(Token.SETVAR, index);
|
||||
}
|
||||
break;
|
||||
|
@ -1273,10 +1270,9 @@ public class Interpreter
|
|||
case Token.SETCONSTVAR:
|
||||
{
|
||||
if (itsData.itsNeedsActivation) Kit.codeBug();
|
||||
String name = child.getString();
|
||||
int index = scriptOrFn.getIndexForNameNode(child);
|
||||
child = child.getNext();
|
||||
visitExpression(child, 0);
|
||||
int index = scriptOrFn.getParamOrVarIndex(name);
|
||||
addVarOp(Token.SETCONSTVAR, index);
|
||||
}
|
||||
break;
|
||||
|
@ -1309,6 +1305,10 @@ public class Interpreter
|
|||
visitLiteral(node, child);
|
||||
break;
|
||||
|
||||
case Token.ARRAYCOMP:
|
||||
visitArrayComprehension(node, child, child.getNext());
|
||||
break;
|
||||
|
||||
case Token.REF_SPECIAL:
|
||||
visitExpression(child, 0);
|
||||
addStringOp(type, (String)node.getProp(Node.NAME_PROP));
|
||||
|
@ -1363,6 +1363,17 @@ public class Interpreter
|
|||
addUint16(node.getLineno() & 0xFFFF);
|
||||
break;
|
||||
|
||||
case Token.WITHEXPR: {
|
||||
Node enterWith = node.getFirstChild();
|
||||
Node with = enterWith.getNext();
|
||||
visitExpression(enterWith.getFirstChild(), 0);
|
||||
addToken(Token.ENTERWITH);
|
||||
stackChange(-1);
|
||||
visitExpression(with.getFirstChild(), 0);
|
||||
addToken(Token.LEAVEWITH);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw badTree(node);
|
||||
}
|
||||
|
@ -1417,8 +1428,7 @@ public class Interpreter
|
|||
switch (childType) {
|
||||
case Token.GETVAR : {
|
||||
if (itsData.itsNeedsActivation) Kit.codeBug();
|
||||
String name = child.getString();
|
||||
int i = scriptOrFn.getParamOrVarIndex(name);
|
||||
int i = scriptOrFn.getIndexForNameNode(child);
|
||||
addVarOp(Icode_VAR_INC_DEC, i);
|
||||
addUint8(incrDecrMask);
|
||||
stackChange(1);
|
||||
|
@ -1511,6 +1521,17 @@ public class Interpreter
|
|||
}
|
||||
stackChange(-1);
|
||||
}
|
||||
|
||||
private void visitArrayComprehension(Node node, Node initStmt, Node expr)
|
||||
{
|
||||
// A bit of a hack: array comprehensions are implemented using
|
||||
// statement nodes for the iteration, yet they appear in an
|
||||
// expression context. So we pass the current stack depth to
|
||||
// visitStatement so it can check that the depth is not altered
|
||||
// by statements.
|
||||
visitStatement(initStmt, itsStackDepth);
|
||||
visitExpression(expr, 0);
|
||||
}
|
||||
|
||||
private int getLocalBlockRef(Node node)
|
||||
{
|
||||
|
|
|
@ -41,6 +41,9 @@
|
|||
|
||||
package org.mozilla.javascript;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* This class implements the root of the intermediate representation.
|
||||
*
|
||||
|
@ -125,6 +128,7 @@ public class Node
|
|||
}
|
||||
|
||||
String str;
|
||||
Node.Scope scope;
|
||||
}
|
||||
|
||||
public static class Jump extends Node
|
||||
|
@ -223,6 +227,74 @@ public class Node
|
|||
private Node target2;
|
||||
private Jump jumpNode;
|
||||
}
|
||||
|
||||
static class Symbol {
|
||||
Symbol(int declType, String name) {
|
||||
this.declType = declType;
|
||||
this.name = name;
|
||||
this.index = -1;
|
||||
}
|
||||
/**
|
||||
* One of Token.FUNCTION, Token.LP (for parameters), Token.VAR,
|
||||
* Token.LET, or Token.CONST
|
||||
*/
|
||||
int declType;
|
||||
int index;
|
||||
String name;
|
||||
Node.Scope containingTable;
|
||||
}
|
||||
|
||||
static class Scope extends Jump {
|
||||
public Scope(int nodeType) {
|
||||
super(nodeType);
|
||||
}
|
||||
|
||||
public Scope(int nodeType, int lineno) {
|
||||
super(nodeType, lineno);
|
||||
}
|
||||
|
||||
public Scope(int nodeType, Node n, int lineno) {
|
||||
super(nodeType, n, lineno);
|
||||
}
|
||||
|
||||
public void setParent(Scope parent) {
|
||||
this.parent = parent;
|
||||
this.top = parent == null ? (ScriptOrFnNode)this : parent.top;
|
||||
}
|
||||
|
||||
public Scope getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public Scope getDefiningScope(String name) {
|
||||
for (Scope sn=this; sn != null; sn = sn.parent) {
|
||||
if (sn.symbolTable == null)
|
||||
continue;
|
||||
if (sn.symbolTable.containsKey(name))
|
||||
return sn;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Symbol getSymbol(String name) {
|
||||
return (Symbol) (symbolTable == null ? null
|
||||
: symbolTable.get(name));
|
||||
}
|
||||
|
||||
public void putSymbol(String name, Symbol symbol) {
|
||||
if (symbolTable == null) {
|
||||
symbolTable = new LinkedHashMap(5);
|
||||
}
|
||||
symbolTable.put(name, symbol);
|
||||
symbol.containingTable = this;
|
||||
top.addSymbol(symbol);
|
||||
}
|
||||
|
||||
// Use LinkedHashMap so that the iteration order is the insertion order
|
||||
protected LinkedHashMap symbolTable;
|
||||
private Scope parent;
|
||||
private ScriptOrFnNode top;
|
||||
}
|
||||
|
||||
private static class PropListItem
|
||||
{
|
||||
|
@ -568,6 +640,20 @@ public class Node
|
|||
if (s == null) Kit.codeBug();
|
||||
((StringNode)this).str = s;
|
||||
}
|
||||
|
||||
/** Can only be called when node has String context. */
|
||||
public final Scope getScope() {
|
||||
return ((StringNode)this).scope;
|
||||
}
|
||||
|
||||
/** Can only be called when node has String context. */
|
||||
public final void setScope(Scope s) {
|
||||
if (s == null) Kit.codeBug();
|
||||
if (!(this instanceof StringNode)) {
|
||||
throw Kit.codeBug();
|
||||
}
|
||||
((StringNode)this).scope = s;
|
||||
}
|
||||
|
||||
public static Node newTarget()
|
||||
{
|
||||
|
@ -784,8 +870,9 @@ public class Node
|
|||
// satisfy.
|
||||
// The target of the predicate is the loop-body for all 4 kinds of
|
||||
// loops.
|
||||
for (n = first; n.next != last; n = n.next)
|
||||
/* skip */;
|
||||
for (n = first; n.next != last; n = n.next) {
|
||||
/* skip */
|
||||
}
|
||||
if (n.type != Token.IFEQ)
|
||||
return END_DROPS_OFF;
|
||||
|
||||
|
@ -992,7 +1079,10 @@ public class Node
|
|||
case Token.CONTINUE:
|
||||
case Token.VAR:
|
||||
case Token.CONST:
|
||||
case Token.LET:
|
||||
case Token.LETEXPR:
|
||||
case Token.WITH:
|
||||
case Token.WITHEXPR:
|
||||
case Token.CATCH:
|
||||
case Token.FINALLY:
|
||||
case Token.BLOCK:
|
||||
|
@ -1029,23 +1119,43 @@ public class Node
|
|||
if (this instanceof StringNode) {
|
||||
sb.append(' ');
|
||||
sb.append(getString());
|
||||
} else if (this instanceof ScriptOrFnNode) {
|
||||
ScriptOrFnNode sof = (ScriptOrFnNode)this;
|
||||
if (this instanceof FunctionNode) {
|
||||
FunctionNode fn = (FunctionNode)this;
|
||||
sb.append(' ');
|
||||
sb.append(fn.getFunctionName());
|
||||
Scope scope = getScope();
|
||||
if (scope != null) {
|
||||
sb.append("[scope: ");
|
||||
appendPrintId(scope, printIds, sb);
|
||||
sb.append("]");
|
||||
}
|
||||
} else if (this instanceof Node.Scope) {
|
||||
if (this instanceof ScriptOrFnNode) {
|
||||
ScriptOrFnNode sof = (ScriptOrFnNode)this;
|
||||
if (this instanceof FunctionNode) {
|
||||
FunctionNode fn = (FunctionNode)this;
|
||||
sb.append(' ');
|
||||
sb.append(fn.getFunctionName());
|
||||
}
|
||||
sb.append(" [source name: ");
|
||||
sb.append(sof.getSourceName());
|
||||
sb.append("] [encoded source length: ");
|
||||
sb.append(sof.getEncodedSourceEnd()
|
||||
- sof.getEncodedSourceStart());
|
||||
sb.append("] [base line: ");
|
||||
sb.append(sof.getBaseLineno());
|
||||
sb.append("] [end line: ");
|
||||
sb.append(sof.getEndLineno());
|
||||
sb.append(']');
|
||||
}
|
||||
if (((Node.Scope)this).symbolTable != null) {
|
||||
sb.append(" [scope ");
|
||||
appendPrintId(this, printIds, sb);
|
||||
sb.append(": ");
|
||||
Iterator iter = ((Node.Scope) this).symbolTable.keySet()
|
||||
.iterator();
|
||||
while (iter.hasNext()) {
|
||||
sb.append(iter.next());
|
||||
sb.append(" ");
|
||||
}
|
||||
sb.append("]");
|
||||
}
|
||||
sb.append(" [source name: ");
|
||||
sb.append(sof.getSourceName());
|
||||
sb.append("] [encoded source length: ");
|
||||
sb.append(sof.getEncodedSourceEnd()
|
||||
- sof.getEncodedSourceStart());
|
||||
sb.append("] [base line: ");
|
||||
sb.append(sof.getBaseLineno());
|
||||
sb.append("] [end line: ");
|
||||
sb.append(sof.getEndLineno());
|
||||
sb.append(']');
|
||||
} else if (this instanceof Jump) {
|
||||
Jump jump = (Jump)this;
|
||||
if (type == Token.BREAK || type == Token.CONTINUE) {
|
||||
|
|
|
@ -42,6 +42,8 @@
|
|||
|
||||
package org.mozilla.javascript;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* This class transforms a tree to a lower-level representation for codegen.
|
||||
*
|
||||
|
@ -69,13 +71,24 @@ public class NodeTransformer
|
|||
{
|
||||
loops = new ObjArray();
|
||||
loopEnds = new ObjArray();
|
||||
|
||||
// to save against upchecks if no finally blocks are used.
|
||||
hasFinally = false;
|
||||
transformCompilationUnit_r(tree, tree);
|
||||
|
||||
// Flatten all only if we are not using scope objects for block scope
|
||||
boolean createScopeObjects = tree.getType() != Token.FUNCTION ||
|
||||
((FunctionNode)tree).requiresActivation();
|
||||
tree.flattenSymbolTable(!createScopeObjects);
|
||||
|
||||
//uncomment to print tree before transformation
|
||||
//System.out.println(tree.toStringTree(tree));
|
||||
transformCompilationUnit_r(tree, tree, tree, createScopeObjects);
|
||||
}
|
||||
|
||||
private void transformCompilationUnit_r(final ScriptOrFnNode tree,
|
||||
final Node parent)
|
||||
final Node parent,
|
||||
Node.Scope scope,
|
||||
boolean createScopeObjects)
|
||||
{
|
||||
Node node = null;
|
||||
siblingLoop:
|
||||
|
@ -92,7 +105,29 @@ public class NodeTransformer
|
|||
}
|
||||
|
||||
int type = node.getType();
|
||||
|
||||
if (createScopeObjects &&
|
||||
(type == Token.BLOCK || type == Token.LOOP) &&
|
||||
(node instanceof Node.Scope))
|
||||
{
|
||||
Node.Scope newScope = (Node.Scope) node;
|
||||
if (newScope.symbolTable != null) {
|
||||
// transform to let statement so we get a with statement
|
||||
// created to contain scoped let variables
|
||||
Node let = new Node(Token.LET);
|
||||
Iterator iter = newScope.symbolTable.keySet().iterator();
|
||||
while (iter.hasNext()) {
|
||||
String name = (String) iter.next();
|
||||
let.addChildToBack(new Node(Token.LET,
|
||||
Node.newString(Token.NAME, name)));
|
||||
}
|
||||
newScope.symbolTable = null; // so we don't transform again
|
||||
Node oldNode = node;
|
||||
node = replaceCurrent(parent, previous, node, let);
|
||||
type = node.getType();
|
||||
let.addChildToBack(oldNode);
|
||||
}
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
|
||||
case Token.LABEL:
|
||||
|
@ -138,15 +173,7 @@ public class NodeTransformer
|
|||
boolean isGenerator = tree.getType() == Token.FUNCTION
|
||||
&& ((FunctionNode)tree).isGenerator();
|
||||
if (isGenerator) {
|
||||
node.putIntProp(Node.GENERATOR_END_PROP, 1);
|
||||
/*
|
||||
// Replace returns inside generators with throw STOP_ITERATION
|
||||
node = replaceCurrent(parent, previous, node,
|
||||
new Node(Token.THROW,
|
||||
Node.newString(Token.NAME,
|
||||
NativeGenerator.STOP_ITERATION),
|
||||
node.getLineno()));
|
||||
*/
|
||||
node.putIntProp(Node.GENERATOR_END_PROP, 1);
|
||||
}
|
||||
/* If we didn't support try/finally, it wouldn't be
|
||||
* necessary to put LEAVEWITH nodes here... but as
|
||||
|
@ -190,7 +217,8 @@ public class NodeTransformer
|
|||
returnNode = new Node(Token.RETURN_RESULT);
|
||||
unwindBlock.addChildToBack(returnNode);
|
||||
// transform return expression
|
||||
transformCompilationUnit_r(tree, store);
|
||||
transformCompilationUnit_r(tree, store, scope,
|
||||
createScopeObjects);
|
||||
}
|
||||
// skip transformCompilationUnit_r to avoid infinite loop
|
||||
continue siblingLoop;
|
||||
|
@ -250,12 +278,27 @@ public class NodeTransformer
|
|||
visitNew(node, tree);
|
||||
break;
|
||||
|
||||
case Token.LETEXPR:
|
||||
case Token.LET: {
|
||||
Node child = node.getFirstChild();
|
||||
if (child.getType() == Token.LET) {
|
||||
// We have a let statement or expression rather than a
|
||||
// let declaration
|
||||
boolean createWith = tree.getType() != Token.FUNCTION
|
||||
|| ((FunctionNode)tree).requiresActivation();
|
||||
node = visitLet(createWith, parent, previous, node);
|
||||
break;
|
||||
} else {
|
||||
// fall through to process let declaration...
|
||||
}
|
||||
}
|
||||
/* fall through */
|
||||
case Token.CONST:
|
||||
case Token.VAR:
|
||||
{
|
||||
Node result = new Node(Token.BLOCK);
|
||||
for (Node cursor = node.getFirstChild(); cursor != null;) {
|
||||
// Move cursor to next before createAssignment get chance
|
||||
// Move cursor to next before createAssignment gets chance
|
||||
// to change n.next
|
||||
Node n = cursor;
|
||||
if (n.getType() != Token.NAME) Kit.codeBug();
|
||||
|
@ -265,9 +308,9 @@ public class NodeTransformer
|
|||
Node init = n.getFirstChild();
|
||||
n.removeChild(init);
|
||||
n.setType(Token.BINDNAME);
|
||||
n = new Node(type == Token.VAR ?
|
||||
Token.SETNAME :
|
||||
Token.SETCONST,
|
||||
n = new Node(type == Token.CONST ?
|
||||
Token.SETCONST :
|
||||
Token.SETNAME,
|
||||
n, init);
|
||||
Node pop = new Node(Token.EXPR_VOID, n, node.getLineno());
|
||||
result.addChildToBack(pop);
|
||||
|
@ -276,15 +319,21 @@ public class NodeTransformer
|
|||
break;
|
||||
}
|
||||
|
||||
case Token.TYPEOFNAME: {
|
||||
Node.Scope defining = scope.getDefiningScope(node.getString());
|
||||
if (defining != null) {
|
||||
node.setScope(defining);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Token.NAME:
|
||||
case Token.SETNAME:
|
||||
case Token.SETCONST:
|
||||
case Token.DELPROP:
|
||||
{
|
||||
// Turn name to var for faster access if possible
|
||||
if (tree.getType() != Token.FUNCTION
|
||||
|| ((FunctionNode)tree).requiresActivation())
|
||||
{
|
||||
if (createScopeObjects) {
|
||||
break;
|
||||
}
|
||||
Node nameSource;
|
||||
|
@ -299,8 +348,13 @@ public class NodeTransformer
|
|||
throw Kit.codeBug();
|
||||
}
|
||||
}
|
||||
if (nameSource.getScope() != null) {
|
||||
break; // already have a scope set
|
||||
}
|
||||
String name = nameSource.getString();
|
||||
if (tree.hasParamOrVar(name)) {
|
||||
Node.Scope defining = scope.getDefiningScope(name);
|
||||
if (defining != null) {
|
||||
nameSource.setScope(defining);
|
||||
if (type == Token.NAME) {
|
||||
node.setType(Token.GETVAR);
|
||||
} else if (type == Token.SETNAME) {
|
||||
|
@ -319,10 +373,11 @@ public class NodeTransformer
|
|||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
transformCompilationUnit_r(tree, node);
|
||||
transformCompilationUnit_r(tree, node,
|
||||
node instanceof Node.Scope ? (Node.Scope)node : scope,
|
||||
createScopeObjects);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -331,6 +386,68 @@ public class NodeTransformer
|
|||
|
||||
protected void visitCall(Node node, ScriptOrFnNode tree) {
|
||||
}
|
||||
|
||||
protected Node visitLet(boolean createWith, Node parent, Node previous,
|
||||
Node scopeNode)
|
||||
{
|
||||
Node vars = scopeNode.getFirstChild();
|
||||
Node body = vars.getNext();
|
||||
scopeNode.removeChild(vars);
|
||||
scopeNode.removeChild(body);
|
||||
boolean isExpression = scopeNode.getType() == Token.LETEXPR;
|
||||
Node result;
|
||||
Node newVars;
|
||||
if (createWith) {
|
||||
result = new Node(isExpression ? Token.WITHEXPR : Token.BLOCK);
|
||||
result = replaceCurrent(parent, previous, scopeNode, result);
|
||||
int count = 0;
|
||||
for (Node v=vars.getFirstChild(); v != null; v = v.getNext())
|
||||
count++;
|
||||
Object[] properties = new Object[count];
|
||||
Node objectLiteral = new Node(Token.OBJECTLIT);
|
||||
count = 0;
|
||||
for (Node v=vars.getFirstChild(); v != null; v = v.getNext()) {
|
||||
if (v.getType() != Token.NAME) throw Kit.codeBug();
|
||||
properties[count++] = ScriptRuntime.getIndexObject(v.getString());
|
||||
Node init = v.getFirstChild();
|
||||
if (init == null) {
|
||||
init = new Node(Token.VOID, Node.newNumber(0.0));
|
||||
}
|
||||
objectLiteral.addChildToBack(init);
|
||||
}
|
||||
objectLiteral.putProp(Node.OBJECT_IDS_PROP, properties);
|
||||
newVars = new Node(Token.ENTERWITH, objectLiteral);
|
||||
result.addChildToBack(newVars);
|
||||
result.addChildToBack(new Node(Token.WITH, body));
|
||||
result.addChildToBack(new Node(Token.LEAVEWITH));
|
||||
} else {
|
||||
result = new Node(isExpression ? Token.COMMA : Token.BLOCK);
|
||||
result = replaceCurrent(parent, previous, scopeNode, result);
|
||||
newVars = new Node(Token.COMMA);
|
||||
for (Node v=vars.getFirstChild(); v != null; v = v.getNext()) {
|
||||
if (v.getType() != Token.NAME) throw Kit.codeBug();
|
||||
Node stringNode = Node.newString(v.getString());
|
||||
stringNode.setScope((Node.Scope)scopeNode);
|
||||
Node init = v.getFirstChild();
|
||||
if (init == null) {
|
||||
init = new Node(Token.VOID, Node.newNumber(0.0));
|
||||
}
|
||||
newVars.addChildToBack(new Node(Token.SETVAR, stringNode, init));
|
||||
}
|
||||
if (isExpression) {
|
||||
result.addChildToBack(newVars);
|
||||
scopeNode.setType(Token.COMMA);
|
||||
result.addChildToBack(scopeNode);
|
||||
scopeNode.addChildToBack(body);
|
||||
} else {
|
||||
result.addChildToBack(new Node(Token.EXPR_VOID, newVars));
|
||||
scopeNode.setType(Token.BLOCK);
|
||||
result.addChildToBack(scopeNode);
|
||||
scopeNode.addChildrenToBack(body);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Node addBeforeCurrent(Node parent, Node previous,
|
||||
Node current, Node toAdd)
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
* Terry Lucas
|
||||
* Mike McCabe
|
||||
* Milen Nankov
|
||||
* Norris Boyd
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* the GNU General Public License Version 2 or later (the "GPL"), in which
|
||||
|
@ -90,6 +91,7 @@ public class Parser
|
|||
// during function parsing.
|
||||
// XXX Move to separated class?
|
||||
ScriptOrFnNode currentScriptOrFn;
|
||||
Node.Scope currentScope;
|
||||
private int nestingOfWith;
|
||||
private Hashtable labelSet; // map of label names into nodes
|
||||
private ObjArray loopSet;
|
||||
|
@ -262,8 +264,19 @@ public class Parser
|
|||
{
|
||||
return nestingOfFunction != 0;
|
||||
}
|
||||
|
||||
private void pushScope(Node node) {
|
||||
Node.Scope scopeNode = (Node.Scope) node;
|
||||
if (scopeNode.getParent() != null) throw Kit.codeBug();
|
||||
scopeNode.setParent(currentScope);
|
||||
currentScope = scopeNode;
|
||||
}
|
||||
|
||||
private void popScope() {
|
||||
currentScope = currentScope.getParent();
|
||||
}
|
||||
|
||||
private Node enterLoop(Node loopLabel)
|
||||
private Node enterLoop(Node loopLabel, boolean doPushScope)
|
||||
{
|
||||
Node loop = nf.createLoopNode(loopLabel, ts.getLineno());
|
||||
if (loopSet == null) {
|
||||
|
@ -274,13 +287,19 @@ public class Parser
|
|||
}
|
||||
loopSet.push(loop);
|
||||
loopAndSwitchSet.push(loop);
|
||||
if (doPushScope) {
|
||||
pushScope(loop);
|
||||
}
|
||||
return loop;
|
||||
}
|
||||
|
||||
private void exitLoop()
|
||||
private void exitLoop(boolean doPopScope)
|
||||
{
|
||||
loopSet.pop();
|
||||
loopAndSwitchSet.pop();
|
||||
if (doPopScope) {
|
||||
popScope();
|
||||
}
|
||||
}
|
||||
|
||||
private Node enterSwitch(Node switchSelector, int lineno)
|
||||
|
@ -342,6 +361,7 @@ public class Parser
|
|||
this.decompiler = createDecompiler(compilerEnv);
|
||||
this.nf = new IRFactory(this);
|
||||
currentScriptOrFn = nf.createScript();
|
||||
currentScope = currentScriptOrFn;
|
||||
int sourceStartOffset = decompiler.getCurrentOffset();
|
||||
this.encodedSource = null;
|
||||
decompiler.addToken(Token.SCRIPT);
|
||||
|
@ -488,6 +508,13 @@ public class Parser
|
|||
|
||||
if (memberExprNode != null) {
|
||||
syntheticType = FunctionNode.FUNCTION_EXPRESSION;
|
||||
}
|
||||
|
||||
if (syntheticType != FunctionNode.FUNCTION_EXPRESSION &&
|
||||
name.length() > 0)
|
||||
{
|
||||
// Function statements define a symbol in the enclosing scope
|
||||
defineSymbol(Token.FUNCTION, name);
|
||||
}
|
||||
|
||||
boolean nested = insideFunction();
|
||||
|
@ -501,13 +528,14 @@ public class Parser
|
|||
// of with object.
|
||||
fnNode.itsIgnoreDynamicScope = true;
|
||||
}
|
||||
|
||||
int functionIndex = currentScriptOrFn.addFunction(fnNode);
|
||||
|
||||
int functionSourceEnd;
|
||||
|
||||
ScriptOrFnNode savedScriptOrFn = currentScriptOrFn;
|
||||
currentScriptOrFn = fnNode;
|
||||
Node.Scope savedCurrentScope = currentScope;
|
||||
currentScope = fnNode;
|
||||
int savedNestingOfWith = nestingOfWith;
|
||||
nestingOfWith = 0;
|
||||
Hashtable savedLabelSet = labelSet;
|
||||
|
@ -530,10 +558,7 @@ public class Parser
|
|||
first = false;
|
||||
mustMatchToken(Token.NAME, "msg.no.parm");
|
||||
String s = ts.getString();
|
||||
if (fnNode.hasParamOrVar(s)) {
|
||||
addWarning("msg.dup.parms", s);
|
||||
}
|
||||
fnNode.addParam(s);
|
||||
defineSymbol(Token.LP, s);
|
||||
decompiler.addName(s);
|
||||
} while (matchToken(Token.COMMA));
|
||||
|
||||
|
@ -552,7 +577,15 @@ public class Parser
|
|||
: "msg.anon.no.return.value";
|
||||
addStrictWarning(msg, name);
|
||||
}
|
||||
|
||||
|
||||
if (syntheticType == FunctionNode.FUNCTION_EXPRESSION &&
|
||||
name.length() > 0 && currentScope.getSymbol(name) == null)
|
||||
{
|
||||
// Function expressions define a name only in the body of the
|
||||
// function, and only if not hidden by a parameter name
|
||||
defineSymbol(Token.FUNCTION, name);
|
||||
}
|
||||
|
||||
decompiler.addToken(Token.RC);
|
||||
functionSourceEnd = decompiler.markFunctionEnd(functionSourceStart);
|
||||
if (functionType != FunctionNode.FUNCTION_EXPRESSION) {
|
||||
|
@ -580,6 +613,7 @@ public class Parser
|
|||
labelSet = savedLabelSet;
|
||||
nestingOfWith = savedNestingOfWith;
|
||||
currentScriptOrFn = savedScriptOrFn;
|
||||
currentScope = savedCurrentScope;
|
||||
}
|
||||
|
||||
fnNode.setEncodedSourceBounds(functionSourceStart, functionSourceEnd);
|
||||
|
@ -587,12 +621,6 @@ public class Parser
|
|||
fnNode.setBaseLineno(baseLineno);
|
||||
fnNode.setEndLineno(ts.getLineno());
|
||||
|
||||
if (name != null) {
|
||||
int index = currentScriptOrFn.getParamOrVarIndex(name);
|
||||
if (index >= 0 && index < currentScriptOrFn.getParamCount())
|
||||
addStrictWarning("msg.var.hides.arg", name);
|
||||
}
|
||||
|
||||
Node pn = nf.initFunction(fnNode, functionIndex, body, syntheticType);
|
||||
if (memberExprNode != null) {
|
||||
pn = nf.createAssignment(Token.ASSIGN, memberExprNode, pn);
|
||||
|
@ -604,13 +632,13 @@ public class Parser
|
|||
return pn;
|
||||
}
|
||||
|
||||
private Node statements()
|
||||
private Node statements(Node scope)
|
||||
throws IOException
|
||||
{
|
||||
Node pn = nf.createBlock(ts.getLineno());
|
||||
Node pn = scope != null ? scope : nf.createBlock(ts.getLineno());
|
||||
|
||||
int tt;
|
||||
while((tt = peekToken()) > Token.EOF && tt != Token.RC) {
|
||||
while ((tt = peekToken()) > Token.EOF && tt != Token.RC) {
|
||||
nf.addChildToBack(pn, statement());
|
||||
}
|
||||
|
||||
|
@ -687,21 +715,13 @@ public class Parser
|
|||
return nf.createExprStatement(nf.createName("error"), lineno);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the "catch (e: e instanceof Exception) { ... }" syntax
|
||||
* is implemented.
|
||||
*/
|
||||
|
||||
private Node statementHelper(Node statementLabel)
|
||||
throws IOException, ParserException
|
||||
{
|
||||
Node pn = null;
|
||||
int tt = peekToken();
|
||||
|
||||
int tt;
|
||||
|
||||
tt = peekToken();
|
||||
|
||||
switch(tt) {
|
||||
switch (tt) {
|
||||
case Token.IF: {
|
||||
consumeToken();
|
||||
|
||||
|
@ -776,7 +796,7 @@ public class Parser
|
|||
nf.addChildToBack(block, statement());
|
||||
}
|
||||
|
||||
// caseExpression == null => add default lable
|
||||
// caseExpression == null => add default label
|
||||
nf.addSwitchCase(pn, caseExpression, block);
|
||||
}
|
||||
decompiler.addEOL(Token.RC);
|
||||
|
@ -791,7 +811,7 @@ public class Parser
|
|||
consumeToken();
|
||||
decompiler.addToken(Token.WHILE);
|
||||
|
||||
Node loop = enterLoop(statementLabel);
|
||||
Node loop = enterLoop(statementLabel, true);
|
||||
try {
|
||||
Node cond = condition();
|
||||
decompiler.addEOL(Token.LC);
|
||||
|
@ -799,7 +819,7 @@ public class Parser
|
|||
decompiler.addEOL(Token.RC);
|
||||
pn = nf.createWhile(loop, cond, body);
|
||||
} finally {
|
||||
exitLoop();
|
||||
exitLoop(true);
|
||||
}
|
||||
return pn;
|
||||
}
|
||||
|
@ -809,7 +829,7 @@ public class Parser
|
|||
decompiler.addToken(Token.DO);
|
||||
decompiler.addEOL(Token.LC);
|
||||
|
||||
Node loop = enterLoop(statementLabel);
|
||||
Node loop = enterLoop(statementLabel, true);
|
||||
try {
|
||||
Node body = statement();
|
||||
decompiler.addToken(Token.RC);
|
||||
|
@ -818,10 +838,10 @@ public class Parser
|
|||
Node cond = condition();
|
||||
pn = nf.createDoWhile(loop, body, cond);
|
||||
} finally {
|
||||
exitLoop();
|
||||
exitLoop(true);
|
||||
}
|
||||
// Always auto-insert semicon to follow SpiderMonkey:
|
||||
// It is required by EMAScript but is ignored by the rest of
|
||||
// Always auto-insert semicolon to follow SpiderMonkey:
|
||||
// It is required by ECMAScript but is ignored by the rest of
|
||||
// world, see bug 238945
|
||||
matchToken(Token.SEMI);
|
||||
decompiler.addEOL(Token.SEMI);
|
||||
|
@ -833,7 +853,7 @@ public class Parser
|
|||
boolean isForEach = false;
|
||||
decompiler.addToken(Token.FOR);
|
||||
|
||||
Node loop = enterLoop(statementLabel);
|
||||
Node loop = enterLoop(statementLabel, true);
|
||||
try {
|
||||
|
||||
Node init; // Node init is also foo in 'foo in Object'
|
||||
|
@ -857,10 +877,11 @@ public class Parser
|
|||
if (tt == Token.SEMI) {
|
||||
init = nf.createLeaf(Token.EMPTY);
|
||||
} else {
|
||||
if (tt == Token.VAR) {
|
||||
if (tt == Token.VAR || tt == Token.LET) {
|
||||
// set init to a var list or initial
|
||||
consumeToken(); // consume the 'var' token
|
||||
init = variables(Token.FOR);
|
||||
consumeToken(); // consume the token
|
||||
decompiler.addToken(tt);
|
||||
init = variables(true, true, tt);
|
||||
}
|
||||
else {
|
||||
init = expr(true);
|
||||
|
@ -904,7 +925,7 @@ public class Parser
|
|||
pn = nf.createFor(loop, init, cond, incr, body);
|
||||
}
|
||||
} finally {
|
||||
exitLoop();
|
||||
exitLoop(true);
|
||||
}
|
||||
return pn;
|
||||
}
|
||||
|
@ -918,6 +939,9 @@ public class Parser
|
|||
Node finallyblock = null;
|
||||
|
||||
decompiler.addToken(Token.TRY);
|
||||
if (peekToken() != Token.LC) {
|
||||
reportError("msg.no.brace.try");
|
||||
}
|
||||
decompiler.addEOL(Token.LC);
|
||||
tryblock = statement();
|
||||
decompiler.addEOL(Token.RC);
|
||||
|
@ -954,7 +978,7 @@ public class Parser
|
|||
|
||||
nf.addChildToBack(catchblocks,
|
||||
nf.createCatch(varName, catchCond,
|
||||
statements(),
|
||||
statements(null),
|
||||
ts.getLineno()));
|
||||
|
||||
mustMatchToken(Token.RC, "msg.no.brace.after.body");
|
||||
|
@ -1065,9 +1089,21 @@ public class Parser
|
|||
case Token.CONST:
|
||||
case Token.VAR: {
|
||||
consumeToken();
|
||||
pn = variables(tt);
|
||||
decompiler.addToken(tt);
|
||||
pn = variables(false, true, tt);
|
||||
break;
|
||||
}
|
||||
|
||||
case Token.LET: {
|
||||
consumeToken();
|
||||
decompiler.addToken(Token.LET);
|
||||
if (peekToken() == Token.LP) {
|
||||
pn = let(true);
|
||||
} else {
|
||||
pn = variables(false, true, tt);
|
||||
}
|
||||
return pn;
|
||||
}
|
||||
|
||||
case Token.RETURN:
|
||||
case Token.YIELD: {
|
||||
|
@ -1080,12 +1116,18 @@ public class Parser
|
|||
if (statementLabel != null) {
|
||||
decompiler.addToken(Token.LC);
|
||||
}
|
||||
pn = statements();
|
||||
mustMatchToken(Token.RC, "msg.no.brace.block");
|
||||
if (statementLabel != null) {
|
||||
decompiler.addEOL(Token.RC);
|
||||
Node scope = nf.createScopeNode(Token.BLOCK, ts.getLineno());
|
||||
pushScope(scope);
|
||||
try {
|
||||
statements(scope);
|
||||
mustMatchToken(Token.RC, "msg.no.brace.block");
|
||||
if (statementLabel != null) {
|
||||
decompiler.addEOL(Token.RC);
|
||||
}
|
||||
return scope;
|
||||
} finally {
|
||||
popScope();
|
||||
}
|
||||
return pn;
|
||||
|
||||
case Token.ERROR:
|
||||
// Fall thru, to have a node for error recovery to work on
|
||||
|
@ -1291,29 +1333,24 @@ public class Parser
|
|||
/**
|
||||
* Parse a 'var' or 'const' statement, or a 'var' init list in a for
|
||||
* statement.
|
||||
* @param context A token value: either VAR, CONST or FOR depending on
|
||||
* @param inFor true if we are currently in the midst of the init
|
||||
* clause of a for.
|
||||
* @param inStatement true if called in a statement (as opposed to an
|
||||
* expression) context
|
||||
* @param declType A token value: either VAR, CONST, or LET depending on
|
||||
* context.
|
||||
* @return The parsed statement
|
||||
* @throws IOException
|
||||
* @throws ParserException
|
||||
*/
|
||||
private Node variables(int context)
|
||||
private Node variables(boolean inFor, boolean inStatement, int declType)
|
||||
throws IOException, ParserException
|
||||
{
|
||||
Node pn;
|
||||
Node result = nf.createVariables(inStatement ? declType : Token.COMMA,
|
||||
ts.getLineno());
|
||||
|
||||
boolean first = true;
|
||||
|
||||
if (context == Token.CONST){
|
||||
pn = nf.createVariables(Token.CONST, ts.getLineno());
|
||||
decompiler.addToken(Token.CONST);
|
||||
} else {
|
||||
pn = nf.createVariables(Token.VAR, ts.getLineno());
|
||||
decompiler.addToken(Token.VAR);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
Node name;
|
||||
Node init;
|
||||
mustMatchToken(Token.NAME, "msg.bad.var");
|
||||
String s = ts.getString();
|
||||
|
||||
|
@ -1322,41 +1359,122 @@ public class Parser
|
|||
first = false;
|
||||
|
||||
decompiler.addName(s);
|
||||
defineSymbol(declType, s);
|
||||
|
||||
if (context == Token.CONST) {
|
||||
if (!currentScriptOrFn.addConst(s)) {
|
||||
// We know it's already defined, since addConst passes if
|
||||
// it's not defined at all. The addVar call just confirms
|
||||
// what it is.
|
||||
if (currentScriptOrFn.addVar(s) != ScriptOrFnNode.DUPLICATE_CONST)
|
||||
addError("msg.var.redecl", s);
|
||||
else
|
||||
addError("msg.const.redecl", s);
|
||||
}
|
||||
} else {
|
||||
int dupState = currentScriptOrFn.addVar(s);
|
||||
if (dupState == ScriptOrFnNode.DUPLICATE_CONST)
|
||||
addError("msg.const.redecl", s);
|
||||
else if (dupState == ScriptOrFnNode.DUPLICATE_PARAMETER)
|
||||
addStrictWarning("msg.var.hides.arg", s);
|
||||
else if (dupState == ScriptOrFnNode.DUPLICATE_VAR)
|
||||
addStrictWarning("msg.var.redecl", s);
|
||||
}
|
||||
name = nf.createName(s);
|
||||
|
||||
// omitted check for argument hiding
|
||||
|
||||
Node init = null;
|
||||
if (matchToken(Token.ASSIGN)) {
|
||||
decompiler.addToken(Token.ASSIGN);
|
||||
|
||||
init = assignExpr(context == Token.FOR);
|
||||
nf.addChildToBack(name, init);
|
||||
init = assignExpr(inFor);
|
||||
}
|
||||
nf.addChildToBack(pn, name);
|
||||
|
||||
if (inStatement) {
|
||||
Node name = nf.createName(s);
|
||||
if (init != null)
|
||||
nf.addChildToBack(name, init);
|
||||
nf.addChildToBack(result, name);
|
||||
} else if (init != null) {
|
||||
Node string = nf.createString(s);
|
||||
string.setScope(currentScope);
|
||||
nf.addChildToBack(result,
|
||||
nf.createBinary(Token.SETVAR, string, init));
|
||||
}
|
||||
|
||||
if (!matchToken(Token.COMMA))
|
||||
break;
|
||||
}
|
||||
return pn;
|
||||
return result;
|
||||
}
|
||||
|
||||
private Node let(boolean isStatement)
|
||||
throws IOException, ParserException
|
||||
{
|
||||
mustMatchToken(Token.LP, "msg.no.paren.after.let");
|
||||
decompiler.addToken(Token.LP);
|
||||
Node result = nf.createScopeNode(Token.LET, ts.getLineno());
|
||||
pushScope(result);
|
||||
try {
|
||||
Node vars = variables(false, true, Token.LET);
|
||||
nf.addChildToBack(result, vars);
|
||||
mustMatchToken(Token.RP, "msg.no.paren.let");
|
||||
decompiler.addToken(Token.RP);
|
||||
if (isStatement && peekToken() == Token.LC) {
|
||||
// let statement
|
||||
consumeToken();
|
||||
decompiler.addEOL(Token.LC);
|
||||
nf.addChildToBack(result, statements(null));
|
||||
mustMatchToken(Token.RC, "msg.no.curly.let");
|
||||
decompiler.addToken(Token.RC);
|
||||
} else {
|
||||
// let expression
|
||||
result.setType(Token.LETEXPR);
|
||||
nf.addChildToBack(result, expr(false));
|
||||
if (isStatement) {
|
||||
// let expression in statement context
|
||||
result = nf.createExprStatement(result, ts.getLineno());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
popScope();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void defineSymbol(int declType, String name) {
|
||||
Node.Scope definingScope = currentScope.getDefiningScope(name);
|
||||
Node.Scope.Symbol symbol = definingScope != null
|
||||
? definingScope.getSymbol(name)
|
||||
: null;
|
||||
boolean error = false;
|
||||
if (symbol != null && (symbol.declType == Token.CONST ||
|
||||
declType == Token.CONST))
|
||||
{
|
||||
error = true;
|
||||
} else {
|
||||
switch (declType) {
|
||||
case Token.LET:
|
||||
if (symbol != null && definingScope == currentScope) {
|
||||
error = symbol.declType == Token.LET;
|
||||
}
|
||||
currentScope.putSymbol(name,
|
||||
new Node.Scope.Symbol(declType, name));
|
||||
break;
|
||||
|
||||
case Token.VAR:
|
||||
case Token.CONST:
|
||||
case Token.FUNCTION:
|
||||
if (symbol != null) {
|
||||
if (symbol.declType == Token.VAR)
|
||||
addStrictWarning("msg.var.redecl", name);
|
||||
else if (symbol.declType == Token.LP) {
|
||||
addStrictWarning("msg.var.hides.arg", name);
|
||||
}
|
||||
} else {
|
||||
currentScriptOrFn.putSymbol(name,
|
||||
new Node.Scope.Symbol(declType, name));
|
||||
}
|
||||
break;
|
||||
|
||||
case Token.LP:
|
||||
if (symbol != null) {
|
||||
// must be duplicate parameter. Second parameter hides the
|
||||
// first, so go ahead and add the second pararameter
|
||||
addWarning("msg.dup.parms", name);
|
||||
}
|
||||
currentScriptOrFn.putSymbol(name,
|
||||
new Node.Scope.Symbol(declType, name));
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Kit.codeBug();
|
||||
}
|
||||
}
|
||||
if (error) {
|
||||
addError(symbol.declType == Token.CONST ? "msg.const.redecl" :
|
||||
symbol.declType == Token.LET ? "msg.let.redecl" :
|
||||
symbol.declType == Token.VAR ? "msg.var.redecl" :
|
||||
symbol.declType == Token.FUNCTION ? "msg.fn.redecl" :
|
||||
"msg.parm.redecl", name);
|
||||
}
|
||||
}
|
||||
|
||||
private Node expr(boolean inForInit)
|
||||
|
@ -1663,7 +1781,7 @@ public class Parser
|
|||
}
|
||||
return pn;
|
||||
}
|
||||
return nf.createName("err"); // Only reached on error. Try to continue.
|
||||
return nf.createName("error"); // Only reached on error.Try to continue.
|
||||
|
||||
}
|
||||
|
||||
|
@ -1981,6 +2099,75 @@ public class Parser
|
|||
return pn;
|
||||
}
|
||||
|
||||
private Node arrayComprehension(String arrayName, Node expr)
|
||||
throws IOException, ParserException
|
||||
{
|
||||
if (nextToken() != Token.FOR)
|
||||
throw Kit.codeBug(); // shouldn't be here if next token isn't 'for'
|
||||
decompiler.addName(" "); // space after array literal expr
|
||||
decompiler.addToken(Token.FOR);
|
||||
boolean isForEach = false;
|
||||
if (matchToken(Token.NAME)) {
|
||||
decompiler.addName(ts.getString());
|
||||
if (ts.getString().equals("each")) {
|
||||
isForEach = true;
|
||||
} else {
|
||||
reportError("msg.no.paren.for");
|
||||
}
|
||||
}
|
||||
mustMatchToken(Token.LP, "msg.no.paren.for");
|
||||
decompiler.addToken(Token.LP);
|
||||
Node init = null;
|
||||
int tt = peekToken();
|
||||
if (tt == Token.LB || tt == Token.LC) {
|
||||
// TODO: handle destructuring assignment
|
||||
throw Kit.codeBug();
|
||||
} else if (tt == Token.NAME) {
|
||||
consumeToken();
|
||||
decompiler.addName(ts.getString());
|
||||
init = nf.createName(ts.getString());
|
||||
// Define as a let since we want the scope of the variable to
|
||||
// be restricted to the array comprehension
|
||||
defineSymbol(Token.LET, ts.getString());
|
||||
} else {
|
||||
reportError("msg.bad.var");
|
||||
}
|
||||
mustMatchToken(Token.IN, "msg.in.after.for.name");
|
||||
decompiler.addToken(Token.IN);
|
||||
Node iterator = expr(false);
|
||||
mustMatchToken(Token.RP, "msg.no.paren.for.ctrl");
|
||||
decompiler.addToken(Token.RP);
|
||||
|
||||
Node body;
|
||||
tt = peekToken();
|
||||
if (tt == Token.FOR) {
|
||||
body = arrayComprehension(arrayName, expr);
|
||||
} else {
|
||||
Node call = nf.createCallOrNew(Token.CALL,
|
||||
nf.createPropertyGet(nf.createName(arrayName), null,
|
||||
"push", 0));
|
||||
call.addChildToBack(expr);
|
||||
body = new Node(Token.EXPR_VOID, call, expr, ts.getLineno());
|
||||
if (tt == Token.IF) {
|
||||
consumeToken();
|
||||
decompiler.addToken(Token.IF);
|
||||
int lineno = ts.getLineno();
|
||||
Node cond = condition();
|
||||
body = nf.createIf(cond, body, null, lineno);
|
||||
} else {
|
||||
mustMatchToken(Token.RB, "msg.no.bracket.arg");
|
||||
decompiler.addToken(Token.RB);
|
||||
}
|
||||
}
|
||||
|
||||
Node loop = enterLoop(null, false);
|
||||
try {
|
||||
return nf.createForIn(loop, init, iterator, body, isForEach);
|
||||
} finally {
|
||||
exitLoop(false);
|
||||
}
|
||||
}
|
||||
|
||||
private Node primaryExpr()
|
||||
throws IOException, ParserException
|
||||
{
|
||||
|
@ -2015,6 +2202,27 @@ public class Parser
|
|||
consumeToken();
|
||||
decompiler.addToken(Token.RB);
|
||||
break;
|
||||
} else if (skipCount == 0 && elems.size() == 1 &&
|
||||
tt == Token.FOR)
|
||||
{
|
||||
Node scopeNode = nf.createScopeNode(Token.ARRAYCOMP,
|
||||
ts.getLineno());
|
||||
pushScope(scopeNode);
|
||||
final String ARRAY_NAME = "$a";
|
||||
defineSymbol(Token.LET, ARRAY_NAME);
|
||||
Node expr = (Node) elems.get(0);
|
||||
Node block = nf.createBlock(ts.getLineno());
|
||||
Node init = new Node(Token.EXPR_VOID,
|
||||
nf.createAssignment(Token.ASSIGN,
|
||||
nf.createName(ARRAY_NAME),
|
||||
nf.createCallOrNew(Token.NEW,
|
||||
nf.createName("Array"))), ts.getLineno());
|
||||
block.addChildToBack(init);
|
||||
block.addChildToBack(arrayComprehension(ARRAY_NAME, expr));
|
||||
scopeNode.addChildToBack(block);
|
||||
scopeNode.addChildToBack(nf.createName(ARRAY_NAME));
|
||||
popScope();
|
||||
return scopeNode;
|
||||
} else {
|
||||
if (!after_lb_or_comma) {
|
||||
reportError("msg.no.bracket.arg");
|
||||
|
@ -2103,6 +2311,10 @@ public class Parser
|
|||
decompiler.addToken(Token.RC);
|
||||
return nf.createObjectLiteral(elems);
|
||||
}
|
||||
|
||||
case Token.LET:
|
||||
decompiler.addToken(Token.LET);
|
||||
return let(false);
|
||||
|
||||
case Token.LP:
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
* Contributor(s):
|
||||
* Igor Bukanov
|
||||
* Bob Jervis
|
||||
* Norris Boyd
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* the GNU General Public License Version 2 or later (the "GPL"), in which
|
||||
|
@ -39,10 +40,15 @@
|
|||
|
||||
package org.mozilla.javascript;
|
||||
|
||||
public class ScriptOrFnNode extends Node {
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
|
||||
public class ScriptOrFnNode extends Node.Scope {
|
||||
|
||||
public ScriptOrFnNode(int nodeType) {
|
||||
super(nodeType);
|
||||
symbols = new ArrayList(4);
|
||||
setParent(null);
|
||||
}
|
||||
|
||||
public final String getSourceName() { return sourceName; }
|
||||
|
@ -113,115 +119,79 @@ public class ScriptOrFnNode extends Node {
|
|||
return regexps.size() / 2 - 1;
|
||||
}
|
||||
|
||||
public final boolean hasParamOrVar(String name) {
|
||||
return itsVariableNames.has(name);
|
||||
}
|
||||
|
||||
public final int getParamOrVarIndex(String name) {
|
||||
return itsVariableNames.get(name, -1);
|
||||
public int getIndexForNameNode(Node nameNode) {
|
||||
if (variableNames == null) throw Kit.codeBug();
|
||||
Node.Scope node = nameNode.getScope();
|
||||
Symbol symbol = node == null ? null
|
||||
: node.getSymbol(nameNode.getString());
|
||||
if (symbol == null)
|
||||
return -1;
|
||||
return symbol.index;
|
||||
}
|
||||
|
||||
public final String getParamOrVarName(int index) {
|
||||
return (String)itsVariables.get(index);
|
||||
if (variableNames == null) throw Kit.codeBug();
|
||||
return variableNames[index];
|
||||
}
|
||||
|
||||
public final int getParamCount() {
|
||||
return varStart;
|
||||
return paramCount;
|
||||
}
|
||||
|
||||
public final int getParamAndVarCount() {
|
||||
return itsVariables.size();
|
||||
if (variableNames == null) throw Kit.codeBug();
|
||||
return symbols.size();
|
||||
}
|
||||
|
||||
public final String[] getParamAndVarNames() {
|
||||
int N = itsVariables.size();
|
||||
if (N == 0) {
|
||||
return ScriptRuntime.emptyStrings;
|
||||
}
|
||||
String[] array = new String[N];
|
||||
itsVariables.toArray(array);
|
||||
return array;
|
||||
if (variableNames == null) throw Kit.codeBug();
|
||||
return variableNames;
|
||||
}
|
||||
|
||||
public final boolean[] getParamAndVarConst() {
|
||||
int N = itsVariables.size();
|
||||
boolean[] array = new boolean[N];
|
||||
for (int i = 0; i < N; i++)
|
||||
if (itsConst.get(i) != null)
|
||||
array[i] = true;
|
||||
return array;
|
||||
if (variableNames == null) throw Kit.codeBug();
|
||||
return isConsts;
|
||||
}
|
||||
|
||||
public final void addParam(String name) {
|
||||
// Check addparam is not called after addLocal
|
||||
if (varStart != itsVariables.size()) Kit.codeBug();
|
||||
// Allow non-unique parameter names: use the last occurrence (parser
|
||||
// will warn about dups)
|
||||
int index = varStart++;
|
||||
itsVariables.add(name);
|
||||
itsConst.add(null);
|
||||
itsVariableNames.put(name, index);
|
||||
void addSymbol(Symbol symbol) {
|
||||
if (variableNames != null) throw Kit.codeBug();
|
||||
if (symbol.declType == Token.LP) {
|
||||
paramCount++;
|
||||
}
|
||||
symbols.add(symbol);
|
||||
}
|
||||
|
||||
public static final int NO_DUPLICATE = 1;
|
||||
public static final int DUPLICATE_VAR = 0;
|
||||
public static final int DUPLICATE_PARAMETER = -1;
|
||||
public static final int DUPLICATE_CONST = -2;
|
||||
|
||||
/**
|
||||
* This function adds a variable to the set of var declarations for a
|
||||
* function (or script). This returns an indicator of a duplicate that
|
||||
* overrides a formal parameter (false if this dups a parameter).
|
||||
* @param name variable name
|
||||
* @return 1 if the name is not any form of duplicate, 0 if it duplicates a
|
||||
* non-parameter, -1 if it duplicates a parameter and -2 if it duplicates a
|
||||
* const.
|
||||
* Assign every symbol a unique integer index. Generate arrays of variable
|
||||
* names and constness that can be indexed by those indices.
|
||||
*
|
||||
* @param flattenAllTables if true, flatten all symbol tables, included
|
||||
* nested block scope symbol tables. If false, just flatten the script's
|
||||
* or function's symbol table.
|
||||
*/
|
||||
public final int addVar(String name) {
|
||||
int vIndex = itsVariableNames.get(name, -1);
|
||||
if (vIndex != -1) {
|
||||
// There's already a variable or parameter with this name.
|
||||
if (vIndex >= varStart) {
|
||||
Object v = itsConst.get(vIndex);
|
||||
if (v != null)
|
||||
return DUPLICATE_CONST;
|
||||
else
|
||||
return DUPLICATE_VAR;
|
||||
} else
|
||||
return DUPLICATE_PARAMETER;
|
||||
}
|
||||
int index = itsVariables.size();
|
||||
itsVariables.add(name);
|
||||
itsConst.add(null);
|
||||
itsVariableNames.put(name, index);
|
||||
return NO_DUPLICATE;
|
||||
}
|
||||
|
||||
public final boolean addConst(String name) {
|
||||
int vIndex = itsVariableNames.get(name, -1);
|
||||
if (vIndex != -1) {
|
||||
// There's already a variable or parameter with this name.
|
||||
return false;
|
||||
}
|
||||
int index = itsVariables.size();
|
||||
itsVariables.add(name);
|
||||
itsConst.add(name);
|
||||
itsVariableNames.put(name, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
public final void removeParamOrVar(String name) {
|
||||
int i = itsVariableNames.get(name, -1);
|
||||
if (i != -1) {
|
||||
itsVariables.remove(i);
|
||||
itsVariableNames.remove(name);
|
||||
ObjToIntMap.Iterator iter = itsVariableNames.newIterator();
|
||||
for (iter.start(); !iter.done(); iter.next()) {
|
||||
int v = iter.getValue();
|
||||
if (v > i) {
|
||||
iter.setValue(v - 1);
|
||||
void flattenSymbolTable(boolean flattenAllTables) {
|
||||
if (!flattenAllTables) {
|
||||
ArrayList newSymbols = new ArrayList();
|
||||
if (this.symbolTable != null) {
|
||||
// Just replace "symbols" with the symbols in this object's
|
||||
// symbol table. Can't just work from symbolTable map since
|
||||
// we need to retain duplicate parameters.
|
||||
for (int i=0; i < symbols.size(); i++) {
|
||||
Symbol symbol = (Symbol) symbols.get(i);
|
||||
if (symbol.containingTable == this) {
|
||||
newSymbols.add(symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
symbols = newSymbols;
|
||||
}
|
||||
variableNames = new String[symbols.size()];
|
||||
isConsts = new boolean[symbols.size()];
|
||||
for (int i=0; i < symbols.size(); i++) {
|
||||
Symbol symbol = (Symbol) symbols.get(i);
|
||||
variableNames[i] = symbol.name;
|
||||
isConsts[i] = symbol.declType == Token.CONST;
|
||||
symbol.index = i;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -245,17 +215,12 @@ public class ScriptOrFnNode extends Node {
|
|||
private int endLineno = -1;
|
||||
|
||||
private ObjArray functions;
|
||||
|
||||
private ObjArray regexps;
|
||||
|
||||
// a list of the formal parameters and local variables
|
||||
private ObjArray itsVariables = new ObjArray();
|
||||
private ObjArray itsConst = new ObjArray();
|
||||
|
||||
// mapping from name to index in list
|
||||
private ObjToIntMap itsVariableNames = new ObjToIntMap(11);
|
||||
|
||||
private int varStart; // index in list of first variable
|
||||
|
||||
private ArrayList symbols;
|
||||
private int paramCount = 0;
|
||||
private String[] variableNames;
|
||||
private boolean[] isConsts;
|
||||
|
||||
private Object compilerData;
|
||||
}
|
||||
|
|
|
@ -1885,9 +1885,11 @@ public class ScriptRuntime {
|
|||
x.enumValues = enumValues;
|
||||
x.iterator = toIterator(cx, x.obj.getParentScope(), x.obj, true);
|
||||
|
||||
// enumInit should read all initial ids before returning
|
||||
// or "for (a.i in a)" would wrongly enumerate i in a as well
|
||||
enumChangeObject(x);
|
||||
if (x.iterator == null) {
|
||||
// enumInit should read all initial ids before returning
|
||||
// or "for (a.i in a)" would wrongly enumerate i in a as well
|
||||
enumChangeObject(x);
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
|
|
@ -136,7 +136,7 @@ public class Token
|
|||
ENUM_NEXT = 59,
|
||||
ENUM_ID = 60,
|
||||
THISFN = 61,
|
||||
RETURN_RESULT = 62, // to return prevoisly stored return result
|
||||
RETURN_RESULT = 62, // to return previously stored return result
|
||||
ARRAYLIT = 63, // array literal
|
||||
OBJECTLIT = 64, // object literal
|
||||
GET_REF = 65, // *reference
|
||||
|
@ -252,7 +252,10 @@ public class Token
|
|||
CONST = 151,
|
||||
SETCONST = 152,
|
||||
SETCONSTVAR = 153,
|
||||
LAST_TOKEN = 154;
|
||||
ARRAYCOMP = 154, // array comprehension
|
||||
LETEXPR = 155,
|
||||
WITHEXPR = 156,
|
||||
LAST_TOKEN = 156;
|
||||
|
||||
public static String name(int token)
|
||||
{
|
||||
|
@ -383,6 +386,7 @@ public class Token
|
|||
case WITH: return "WITH";
|
||||
case CATCH: return "CATCH";
|
||||
case FINALLY: return "FINALLY";
|
||||
case VOID: return "VOID";
|
||||
case RESERVED: return "RESERVED";
|
||||
case EMPTY: return "EMPTY";
|
||||
case BLOCK: return "BLOCK";
|
||||
|
@ -413,6 +417,9 @@ public class Token
|
|||
case YIELD: return "YIELD";
|
||||
case CONST: return "CONST";
|
||||
case SETCONST: return "SETCONST";
|
||||
case ARRAYCOMP: return "ARRAYCOMP";
|
||||
case WITHEXPR: return "WITHEXPR";
|
||||
case LETEXPR: return "LETEXPR";
|
||||
}
|
||||
|
||||
// Token without name
|
||||
|
|
|
@ -1402,7 +1402,7 @@ class BodyCodegen
|
|||
varRegisters[i] = reg;
|
||||
}
|
||||
|
||||
// Add debug table enry if we're generating debug info
|
||||
// Add debug table entry if we're generating debug info
|
||||
if (compilerEnv.isGenerateDebugInfo()) {
|
||||
String name = fnCurrent.fnode.getParamOrVarName(i);
|
||||
String type = fnCurrent.isNumberVar(i)
|
||||
|
@ -2419,8 +2419,26 @@ class BodyCodegen
|
|||
* in generator and if so generate code for the analog
|
||||
* to Icode_GENERATOR_END.
|
||||
*/
|
||||
break;
|
||||
throw Kit.codeBug();
|
||||
|
||||
case Token.WITHEXPR: {
|
||||
Node enterWith = child;
|
||||
Node with = enterWith.getNext();
|
||||
Node leaveWith = with.getNext();
|
||||
generateStatement(enterWith);
|
||||
generateExpression(with.getFirstChild(), with);
|
||||
generateStatement(leaveWith);
|
||||
break;
|
||||
}
|
||||
|
||||
case Token.ARRAYCOMP: {
|
||||
Node initStmt = child;
|
||||
Node expr = child.getNext();
|
||||
generateStatement(initStmt);
|
||||
generateExpression(expr, node);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new RuntimeException("Unexpected node type "+type);
|
||||
}
|
||||
|
@ -3260,9 +3278,8 @@ Else pass the JS object in the aReg and 0.0 in the dReg.
|
|||
|
||||
private void visitTypeofname(Node node)
|
||||
{
|
||||
String name = node.getString();
|
||||
if (hasVarsInRegs) {
|
||||
int varIndex = fnCurrent.fnode.getParamOrVarIndex(name);
|
||||
int varIndex = fnCurrent.fnode.getIndexForNameNode(node);
|
||||
if (varIndex >= 0) {
|
||||
if (fnCurrent.isNumberVar(varIndex)) {
|
||||
cfw.addPush("number");
|
||||
|
@ -3293,7 +3310,7 @@ Else pass the JS object in the aReg and 0.0 in the dReg.
|
|||
}
|
||||
}
|
||||
cfw.addALoad(variableObjectLocal);
|
||||
cfw.addPush(name);
|
||||
cfw.addPush(node.getString());
|
||||
addScriptRuntimeInvoke("typeofName",
|
||||
"(Lorg/mozilla/javascript/Scriptable;"
|
||||
+"Ljava/lang/String;"
|
||||
|
|
|
@ -123,17 +123,17 @@ final class OptFunctionNode
|
|||
{
|
||||
int index = n.getIntProp(Node.VARIABLE_PROP, -1);
|
||||
if (index == -1) {
|
||||
String name;
|
||||
Node node;
|
||||
int type = n.getType();
|
||||
if (type == Token.GETVAR) {
|
||||
name = n.getString();
|
||||
node = n;
|
||||
} else if (type == Token.SETVAR ||
|
||||
type == Token.SETCONSTVAR) {
|
||||
name = n.getFirstChild().getString();
|
||||
node = n.getFirstChild();
|
||||
} else {
|
||||
throw Kit.codeBug();
|
||||
}
|
||||
index = fnode.getParamOrVarIndex(name);
|
||||
index = fnode.getIndexForNameNode(node);
|
||||
if (index < 0) throw Kit.codeBug();
|
||||
n.putIntProp(Node.VARIABLE_PROP, index);
|
||||
}
|
||||
|
|
|
@ -257,6 +257,15 @@ msg.var.redecl =\
|
|||
|
||||
msg.const.redecl =\
|
||||
TypeError: redeclaration of const {0}.
|
||||
|
||||
msg.let.redecl =\
|
||||
TypeError: redeclaration of variable {0}.
|
||||
|
||||
msg.parm.redecl =\
|
||||
TypeError: redeclaration of formal parameter {0}.
|
||||
|
||||
msg.fn.redecl =\
|
||||
TypeError: redeclaration of function {0}.
|
||||
|
||||
# NodeTransformer
|
||||
msg.dup.label =\
|
||||
|
@ -349,6 +358,9 @@ msg.no.semi.for =\
|
|||
|
||||
msg.no.semi.for.cond =\
|
||||
missing ; after for-loop condition
|
||||
|
||||
msg.in.after.for.name =\
|
||||
missing in after for
|
||||
|
||||
msg.no.paren.for.ctrl =\
|
||||
missing ) after for-loop control
|
||||
|
@ -359,6 +371,15 @@ msg.no.paren.with =\
|
|||
msg.no.paren.after.with =\
|
||||
missing ) after with-statement object
|
||||
|
||||
msg.no.paren.after.let =\
|
||||
missing ( after let
|
||||
|
||||
msg.no.paren.let =\
|
||||
missing ) after variable list
|
||||
|
||||
msg.no.curly.let =\
|
||||
missing } after let statement
|
||||
|
||||
msg.bad.return =\
|
||||
invalid return
|
||||
|
||||
|
@ -407,6 +428,9 @@ msg.bad.catchcond =\
|
|||
msg.catch.unreachable =\
|
||||
any catch clauses following an unqualified catch are unreachable
|
||||
|
||||
msg.no.brace.try =\
|
||||
missing '{' before try block
|
||||
|
||||
msg.no.brace.catchblock =\
|
||||
missing '{' before catch-block body
|
||||
|
||||
|
|
|
@ -167,18 +167,14 @@ js1_7/geniter/regress-347739.js
|
|||
js1_7/geniter/regress-349012-01.js
|
||||
js1_7/geniter/regress-349331.js
|
||||
|
||||
# Generator functionality not yet implemented:
|
||||
# array comprehensions
|
||||
js1_7/geniter/regress-345736.js
|
||||
# let statements
|
||||
js1_7/geniter/regress-347593.js
|
||||
# yield and xml-filtering predicate
|
||||
js1_7/geniter/regress-352605.js
|
||||
# destructuring assignment
|
||||
js1_7/geniter/regress-366941.js
|
||||
# unimplemented "options" shell function
|
||||
js1_7/block/regress-347559.js
|
||||
|
||||
# decompilation
|
||||
js1_7/block/regress-344601.js
|
||||
js1_7/block/regress-351794.js
|
||||
|
||||
# JS 1.7 not yet implemented
|
||||
js1_7/block
|
||||
js1_7/decompilation
|
||||
js1_7/expressions
|
||||
js1_7/extensions
|
||||
|
@ -186,6 +182,13 @@ js1_7/iterable
|
|||
js1_7/lexical
|
||||
js1_7/regexp
|
||||
js1_7/regress
|
||||
# yield and xml-filtering predicate
|
||||
js1_7/geniter/regress-352605.js
|
||||
# destructuring assignment
|
||||
js1_7/geniter/regress-345736.js
|
||||
js1_7/geniter/regress-347593.js
|
||||
js1_7/geniter/regress-366941.js
|
||||
js1_7/block/regress-358508.js
|
||||
|
||||
# JS 1.8 not yet implemented
|
||||
js1_8
|
||||
|
|
|
@ -2,6 +2,12 @@
|
|||
# These tests are skipped by Rhino either because a bug with a regression
|
||||
# test has yet to be fixed, or because the test is not applicable to Rhino.
|
||||
|
||||
# generators/iterators not yet implementated for Java bytecodes
|
||||
js1_7/GC/regress-341675.js
|
||||
js1_7/geniter
|
||||
js1_7/block/regress-343765.js
|
||||
js1_7/block/regress-350793-01.js
|
||||
|
||||
# program too large/complex
|
||||
ecma_3/Statements/regress-302439.js
|
||||
ecma_3/Statements/regress-324650.js
|
||||
|
|
Загрузка…
Ссылка в новой задаче