diff --git a/js/rhino/src/org/mozilla/javascript/IRFactory.java b/js/rhino/src/org/mozilla/javascript/IRFactory.java index 9fa9d7603c2..401c911f635 100644 --- a/js/rhino/src/org/mozilla/javascript/IRFactory.java +++ b/js/rhino/src/org/mozilla/javascript/IRFactory.java @@ -1412,7 +1412,7 @@ final class IRFactory createName(Token.NAME, tempName, right))); try { parser.pushScope(result); - parser.defineSymbol(Token.LET, tempName); + parser.defineSymbol(Token.LET, true, tempName); } finally { parser.popScope(); } @@ -1447,7 +1447,7 @@ final class IRFactory createName(Token.BINDNAME, name, null), rightElem)); if (variableType != -1) { - parser.defineSymbol(variableType, name); + parser.defineSymbol(variableType, true, name); destructuringNames.add(name); } } else { @@ -1480,7 +1480,7 @@ final class IRFactory createName(Token.BINDNAME, name, null), rightElem)); if (variableType != -1) { - parser.defineSymbol(variableType, name); + parser.defineSymbol(variableType, true, name); destructuringNames.add(name); } } else { diff --git a/js/rhino/src/org/mozilla/javascript/Parser.java b/js/rhino/src/org/mozilla/javascript/Parser.java index 4dbee0a1953..285ce34a541 100644 --- a/js/rhino/src/org/mozilla/javascript/Parser.java +++ b/js/rhino/src/org/mozilla/javascript/Parser.java @@ -518,7 +518,7 @@ public class Parser name.length() > 0) { // Function statements define a symbol in the enclosing scope - defineSymbol(Token.FUNCTION, name); + defineSymbol(Token.FUNCTION, false, name); } boolean nested = insideFunction(); @@ -571,14 +571,14 @@ public class Parser destructuring = new Node(Token.COMMA); } String parmName = currentScriptOrFn.getNextTempName(); - defineSymbol(Token.LP, parmName); + defineSymbol(Token.LP, false, parmName); destructuring.addChildToBack( nf.createDestructuringAssignment(Token.VAR, primaryExpr(), nf.createName(parmName))); } else { mustMatchToken(Token.NAME, "msg.no.parm"); String s = ts.getString(); - defineSymbol(Token.LP, s); + defineSymbol(Token.LP, false, s); decompiler.addName(s); } } while (matchToken(Token.COMMA)); @@ -608,7 +608,7 @@ public class Parser { // 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); + defineSymbol(Token.FUNCTION, false, name); } decompiler.addToken(Token.RC); @@ -1113,11 +1113,13 @@ public class Parser consumeToken(); decompiler.addToken(Token.LET); if (peekToken() == Token.LP) { - pn = let(true); + return let(true); } else { pn = variables(false, tt); + if (peekToken() == Token.SEMI) + break; + return pn; } - return pn; } case Token.RETURN: @@ -1383,7 +1385,7 @@ public class Parser first = false; decompiler.addName(s); - defineSymbol(declType, s); + defineSymbol(declType, inFor, s); } Node init = null; @@ -1450,7 +1452,7 @@ public class Parser return result; } - void defineSymbol(int declType, String name) { + void defineSymbol(int declType, boolean ignoreNotInBlock, String name) { Node.Scope definingScope = currentScope.getDefiningScope(name); Node.Scope.Symbol symbol = definingScope != null ? definingScope.getSymbol(name) @@ -1466,6 +1468,13 @@ public class Parser if (symbol != null && definingScope == currentScope) { error = symbol.declType == Token.LET; } + int currentScopeType = currentScope.getType(); + if (!ignoreNotInBlock && + ((currentScopeType == Token.LOOP) || + (currentScopeType == Token.IF))) + { + addError("msg.let.decl.not.in.block"); + } currentScope.putSymbol(name, new Node.Scope.Symbol(declType, name)); break; @@ -2158,7 +2167,7 @@ public class Parser if (tt == Token.LB || tt == Token.LC) { // handle destructuring assignment name = currentScriptOrFn.getNextTempName(); - defineSymbol(Token.LP, name); + defineSymbol(Token.LP, false, name); expr = nf.createBinary(Token.COMMA, nf.createAssignment(Token.ASSIGN, primaryExpr(), nf.createName(name)), @@ -2175,7 +2184,7 @@ public class Parser Node init = nf.createName(name); // Define as a let since we want the scope of the variable to // be restricted to the array comprehension - defineSymbol(Token.LET, name); + defineSymbol(Token.LET, false, name); mustMatchToken(Token.IN, "msg.in.after.for.name"); decompiler.addToken(Token.IN); @@ -2263,7 +2272,7 @@ public class Parser String tempName = currentScriptOrFn.getNextTempName(); pushScope(scopeNode); try { - defineSymbol(Token.LET, tempName); + defineSymbol(Token.LET, false, tempName); Node expr = (Node) elems.get(0); Node block = nf.createBlock(ts.getLineno()); Node init = new Node(Token.EXPR_VOID, diff --git a/js/rhino/src/org/mozilla/javascript/resources/Messages.properties b/js/rhino/src/org/mozilla/javascript/resources/Messages.properties index eec81d2adbe..8ab9a41cae3 100644 --- a/js/rhino/src/org/mozilla/javascript/resources/Messages.properties +++ b/js/rhino/src/org/mozilla/javascript/resources/Messages.properties @@ -272,13 +272,16 @@ msg.const.redecl =\ 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}. +msg.let.decl.not.in.block =\ + SyntaxError: let declaration not directly within block + # NodeTransformer msg.dup.label =\ duplicated label diff --git a/js/rhino/testsrc/doctests/433878.doctest b/js/rhino/testsrc/doctests/433878.doctest new file mode 100644 index 00000000000..eae4b3dcba7 --- /dev/null +++ b/js/rhino/testsrc/doctests/433878.doctest @@ -0,0 +1,19 @@ +js> version(170) +0 +js> function f(a,b,c) { + > let sum = a + b + c; + > return sum / 3; + > } +js> f.toString() + +function f(a, b, c) { + let sum = a + b + c; + return sum / 3; +} + +js> try { + > eval("function f() { for (;;) let a=3; return 3; }"); + > } catch (e) { + > e; + > } +SyntaxError: SyntaxError: let declaration not directly within block