Implement block scope (let) and array comprehensions.

This required significant changes to symbol table management.
This commit is contained in:
nboyd%atg.com 2007-07-11 16:00:03 +00:00
Родитель 0228403e1c
Коммит 7389c08ecb
59 изменённых файлов: 778 добавлений и 320 удалений

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -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