Thu, 27 Nov 2014 18:02:28 +0100
8057980: let & const: remaining issues with lexical scoping
Reviewed-by: lagergren, attila
1.1 --- a/src/jdk/nashorn/internal/codegen/AssignSymbols.java Thu Nov 27 17:14:01 2014 +0400 1.2 +++ b/src/jdk/nashorn/internal/codegen/AssignSymbols.java Thu Nov 27 18:02:28 2014 +0100 1.3 @@ -189,7 +189,7 @@ 1.4 * @param body the body of the FunctionNode we are entering 1.5 */ 1.6 private void acceptDeclarations(final FunctionNode functionNode, final Block body) { 1.7 - // This visitor will assign symbol to all declared variables, except "var" declarations in for loop initializers. 1.8 + // This visitor will assign symbol to all declared variables. 1.9 body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { 1.10 @Override 1.11 protected boolean enterDefault(final Node node) { 1.12 @@ -200,16 +200,17 @@ 1.13 1.14 @Override 1.15 public Node leaveVarNode(final VarNode varNode) { 1.16 - if (varNode.isStatement()) { 1.17 - final IdentNode ident = varNode.getName(); 1.18 - final Block block = varNode.isBlockScoped() ? getLexicalContext().getCurrentBlock() : body; 1.19 - final Symbol symbol = defineSymbol(block, ident.getName(), ident, varNode.getSymbolFlags()); 1.20 - if (varNode.isFunctionDeclaration()) { 1.21 - symbol.setIsFunctionDeclaration(); 1.22 - } 1.23 - return varNode.setName(ident.setSymbol(symbol)); 1.24 + final IdentNode ident = varNode.getName(); 1.25 + final boolean blockScoped = varNode.isBlockScoped(); 1.26 + if (blockScoped && lc.inUnprotectedSwitchContext()) { 1.27 + throwUnprotectedSwitchError(varNode); 1.28 } 1.29 - return varNode; 1.30 + final Block block = blockScoped ? lc.getCurrentBlock() : body; 1.31 + final Symbol symbol = defineSymbol(block, ident.getName(), ident, varNode.getSymbolFlags()); 1.32 + if (varNode.isFunctionDeclaration()) { 1.33 + symbol.setIsFunctionDeclaration(); 1.34 + } 1.35 + return varNode.setName(ident.setSymbol(symbol)); 1.36 } 1.37 }); 1.38 } 1.39 @@ -1048,6 +1049,15 @@ 1.40 return !(units == null || units.isEmpty()); 1.41 } 1.42 1.43 + private void throwUnprotectedSwitchError(final VarNode varNode) { 1.44 + // Block scoped declarations in switch statements without explicit blocks should be declared 1.45 + // in a common block that contains all the case clauses. We cannot support this without a 1.46 + // fundamental rewrite of how switch statements are handled (case nodes contain blocks and are 1.47 + // directly contained by switch node). As a temporary solution we throw a reference error here. 1.48 + final String msg = ECMAErrors.getMessage("syntax.error.unprotected.switch.declaration", varNode.isLet() ? "let" : "const"); 1.49 + throwParserException(msg, varNode); 1.50 + } 1.51 + 1.52 private void throwParserException(final String message, final Node origin) { 1.53 if (origin == null) { 1.54 throw new ParserException(message);
2.1 --- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java Thu Nov 27 17:14:01 2014 +0400 2.2 +++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java Thu Nov 27 18:02:28 2014 +0100 2.3 @@ -3264,6 +3264,13 @@ 2.4 emitContinueLabel(continueLabel, liveLocalsOnContinue); 2.5 } 2.6 2.7 + if (loopNode.hasPerIterationScope() && lc.getParentBlock().needsScope()) { 2.8 + // ES6 for loops with LET init need a new scope for each iteration. We just create a shallow copy here. 2.9 + method.loadCompilerConstant(SCOPE); 2.10 + method.invoke(virtualCallNoLookup(ScriptObject.class, "copy", ScriptObject.class)); 2.11 + method.storeCompilerConstant(SCOPE); 2.12 + } 2.13 + 2.14 if(method.isReachable()) { 2.15 if(modify != null) { 2.16 lineNumber(loopNode);
3.1 --- a/src/jdk/nashorn/internal/codegen/Lower.java Thu Nov 27 17:14:01 2014 +0400 3.2 +++ b/src/jdk/nashorn/internal/codegen/Lower.java Thu Nov 27 18:02:28 2014 +0100 3.3 @@ -525,7 +525,7 @@ 3.4 3.5 if (isAlwaysTrue(test)) { 3.6 //turn it into a for node without a test. 3.7 - final ForNode forNode = (ForNode)new ForNode(whileNode.getLineNumber(), whileNode.getToken(), whileNode.getFinish(), body, ForNode.IS_FOR).accept(this); 3.8 + final ForNode forNode = (ForNode)new ForNode(whileNode.getLineNumber(), whileNode.getToken(), whileNode.getFinish(), body, 0).accept(this); 3.9 lc.replace(whileNode, forNode); 3.10 return forNode; 3.11 }
4.1 --- a/src/jdk/nashorn/internal/ir/ForNode.java Thu Nov 27 17:14:01 2014 +0400 4.2 +++ b/src/jdk/nashorn/internal/ir/ForNode.java Thu Nov 27 18:02:28 2014 +0100 4.3 @@ -45,14 +45,14 @@ 4.4 /** Iterator symbol. */ 4.5 private Symbol iterator; 4.6 4.7 - /** Is this a normal for loop? */ 4.8 - public static final int IS_FOR = 1 << 0; 4.9 - 4.10 /** Is this a normal for in loop? */ 4.11 - public static final int IS_FOR_IN = 1 << 1; 4.12 + public static final int IS_FOR_IN = 1 << 0; 4.13 4.14 /** Is this a normal for each in loop? */ 4.15 - public static final int IS_FOR_EACH = 1 << 2; 4.16 + public static final int IS_FOR_EACH = 1 << 1; 4.17 + 4.18 + /** Does this loop need a per-iteration scope because its init contain a LET declaration? */ 4.19 + public static final int PER_ITERATION_SCOPE = 1 << 2; 4.20 4.21 private final int flags; 4.22 4.23 @@ -273,4 +273,18 @@ 4.24 JoinPredecessor setLocalVariableConversionChanged(final LexicalContext lc, final LocalVariableConversion conversion) { 4.25 return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); 4.26 } 4.27 + 4.28 + @Override 4.29 + public boolean hasPerIterationScope() { 4.30 + return (flags & PER_ITERATION_SCOPE) != 0; 4.31 + } 4.32 + 4.33 + /** 4.34 + * Set the per-iteration-scope flag on this node. 4.35 + * @param lc lexical context 4.36 + * @return the node with flag set 4.37 + */ 4.38 + public ForNode setPerIterationScope(final LexicalContext lc) { 4.39 + return setFlags(lc, flags | PER_ITERATION_SCOPE); 4.40 + } 4.41 }
5.1 --- a/src/jdk/nashorn/internal/ir/LexicalContext.java Thu Nov 27 17:14:01 2014 +0400 5.2 +++ b/src/jdk/nashorn/internal/ir/LexicalContext.java Thu Nov 27 18:02:28 2014 +0100 5.3 @@ -597,6 +597,20 @@ 5.4 throw new AssertionError(target + " was expected in lexical context " + LexicalContext.this + " but wasn't"); 5.5 } 5.6 5.7 + /** 5.8 + * Checks whether the current context is inside a switch statement without explicit blocks (curly braces). 5.9 + * @return true if in unprotected switch statement 5.10 + */ 5.11 + public boolean inUnprotectedSwitchContext() { 5.12 + for (int i = sp; i > 0; i--) { 5.13 + final LexicalContextNode next = stack[i]; 5.14 + if (next instanceof Block) { 5.15 + return stack[i - 1] instanceof SwitchNode; 5.16 + } 5.17 + } 5.18 + return false; 5.19 + } 5.20 + 5.21 @Override 5.22 public String toString() { 5.23 final StringBuffer sb = new StringBuffer();
6.1 --- a/src/jdk/nashorn/internal/ir/LoopNode.java Thu Nov 27 17:14:01 2014 +0400 6.2 +++ b/src/jdk/nashorn/internal/ir/LoopNode.java Thu Nov 27 18:02:28 2014 +0100 6.3 @@ -176,4 +176,10 @@ 6.4 * @return new loop node if changed otherwise the same 6.5 */ 6.6 public abstract LoopNode setControlFlowEscapes(final LexicalContext lc, final boolean controlFlowEscapes); 6.7 + 6.8 + /** 6.9 + * Does this loop have a LET declaration and hence require a per-iteration scope? 6.10 + * @return true if a per-iteration scope is required. 6.11 + */ 6.12 + public abstract boolean hasPerIterationScope(); 6.13 }
7.1 --- a/src/jdk/nashorn/internal/ir/VarNode.java Thu Nov 27 17:14:01 2014 +0400 7.2 +++ b/src/jdk/nashorn/internal/ir/VarNode.java Thu Nov 27 18:02:28 2014 +0100 7.3 @@ -45,19 +45,16 @@ 7.4 /** Is this a var statement (as opposed to a "var" in a for loop statement) */ 7.5 private final int flags; 7.6 7.7 - /** Flag that determines if this function node is a statement */ 7.8 - public static final int IS_STATEMENT = 1 << 0; 7.9 - 7.10 /** Flag for ES6 LET declaration */ 7.11 - public static final int IS_LET = 1 << 1; 7.12 + public static final int IS_LET = 1 << 0; 7.13 7.14 /** Flag for ES6 CONST declaration */ 7.15 - public static final int IS_CONST = 1 << 2; 7.16 + public static final int IS_CONST = 1 << 1; 7.17 7.18 /** Flag that determines if this is the last function declaration in a function 7.19 * This is used to micro optimize the placement of return value assignments for 7.20 * a program node */ 7.21 - public static final int IS_LAST_FUNCTION_DECLARATION = 1 << 3; 7.22 + public static final int IS_LAST_FUNCTION_DECLARATION = 1 << 2; 7.23 7.24 /** 7.25 * Constructor 7.26 @@ -69,7 +66,7 @@ 7.27 * @param init init node or null if just a declaration 7.28 */ 7.29 public VarNode(final int lineNumber, final long token, final int finish, final IdentNode name, final Expression init) { 7.30 - this(lineNumber, token, finish, name, init, IS_STATEMENT); 7.31 + this(lineNumber, token, finish, name, init, 0); 7.32 } 7.33 7.34 private VarNode(final VarNode varNode, final IdentNode name, final Expression init, final int flags) { 7.35 @@ -260,14 +257,6 @@ 7.36 } 7.37 7.38 /** 7.39 - * Returns true if this is a var statement (as opposed to a var initializer in a for loop). 7.40 - * @return true if this is a var statement (as opposed to a var initializer in a for loop). 7.41 - */ 7.42 - public boolean isStatement() { 7.43 - return (flags & IS_STATEMENT) != 0; 7.44 - } 7.45 - 7.46 - /** 7.47 * Returns true if this is a function declaration. 7.48 * @return true if this is a function declaration. 7.49 */
8.1 --- a/src/jdk/nashorn/internal/ir/WhileNode.java Thu Nov 27 17:14:01 2014 +0400 8.2 +++ b/src/jdk/nashorn/internal/ir/WhileNode.java Thu Nov 27 18:02:28 2014 +0100 8.3 @@ -148,4 +148,9 @@ 8.4 } 8.5 return test == null; 8.6 } 8.7 + 8.8 + @Override 8.9 + public boolean hasPerIterationScope() { 8.10 + return false; 8.11 + } 8.12 }
9.1 --- a/src/jdk/nashorn/internal/parser/Parser.java Thu Nov 27 17:14:01 2014 +0400 9.2 +++ b/src/jdk/nashorn/internal/parser/Parser.java Thu Nov 27 18:02:28 2014 +0100 9.3 @@ -559,7 +559,7 @@ 9.4 // Set up new block. Captures first token. 9.5 Block newBlock = newBlock(); 9.6 try { 9.7 - statement(); 9.8 + statement(false, false, true); 9.9 } finally { 9.10 newBlock = restoreBlock(newBlock); 9.11 } 9.12 @@ -772,7 +772,7 @@ 9.13 9.14 try { 9.15 // Get the next element. 9.16 - statement(true, allowPropertyFunction); 9.17 + statement(true, allowPropertyFunction, false); 9.18 allowPropertyFunction = false; 9.19 9.20 // check for directive prologues 9.21 @@ -862,13 +862,15 @@ 9.22 * Parse any of the basic statement types. 9.23 */ 9.24 private void statement() { 9.25 - statement(false, false); 9.26 + statement(false, false, false); 9.27 } 9.28 9.29 /** 9.30 * @param topLevel does this statement occur at the "top level" of a script or a function? 9.31 + * @param allowPropertyFunction allow property "get" and "set" functions? 9.32 + * @param singleStatement are we in a single statement context? 9.33 */ 9.34 - private void statement(final boolean topLevel, final boolean allowPropertyFunction) { 9.35 + private void statement(final boolean topLevel, final boolean allowPropertyFunction, final boolean singleStatement) { 9.36 if (type == FUNCTION) { 9.37 // As per spec (ECMA section 12), function declarations as arbitrary statement 9.38 // is not "portable". Implementation can issue a warning or disallow the same. 9.39 @@ -932,6 +934,9 @@ 9.40 break; 9.41 default: 9.42 if (useBlockScope() && (type == LET || type == CONST)) { 9.43 + if (singleStatement) { 9.44 + throw error(AbstractParser.message("expected.stmt", type.getName() + " declaration"), token); 9.45 + } 9.46 variableStatement(type, true); 9.47 break; 9.48 } 9.49 @@ -1057,7 +1062,7 @@ 9.50 next(); 9.51 9.52 final List<VarNode> vars = new ArrayList<>(); 9.53 - int varFlags = VarNode.IS_STATEMENT; 9.54 + int varFlags = 0; 9.55 if (varType == LET) { 9.56 varFlags |= VarNode.IS_LET; 9.57 } else if (varType == CONST) { 9.58 @@ -1210,7 +1215,7 @@ 9.59 Block outer = useBlockScope() ? newBlock() : null; 9.60 9.61 // Create FOR node, capturing FOR token. 9.62 - ForNode forNode = new ForNode(line, token, Token.descPosition(token), null, ForNode.IS_FOR); 9.63 + ForNode forNode = new ForNode(line, token, Token.descPosition(token), null, 0); 9.64 lc.push(forNode); 9.65 9.66 try { 9.67 @@ -1230,19 +1235,22 @@ 9.68 9.69 switch (type) { 9.70 case VAR: 9.71 - // Var statements captured in for outer block. 9.72 + // Var declaration captured in for outer block. 9.73 vars = variableStatement(type, false); 9.74 break; 9.75 case SEMICOLON: 9.76 break; 9.77 default: 9.78 if (useBlockScope() && (type == LET || type == CONST)) { 9.79 - // LET/CONST captured in container block created above. 9.80 + if (type == LET) { 9.81 + forNode = forNode.setPerIterationScope(lc); 9.82 + } 9.83 + // LET/CONST declaration captured in container block created above. 9.84 vars = variableStatement(type, false); 9.85 break; 9.86 } 9.87 if (env._const_as_var && type == CONST) { 9.88 - // Var statements captured in for outer block. 9.89 + // Var declaration captured in for outer block. 9.90 vars = variableStatement(TokenType.VAR, false); 9.91 break; 9.92 } 9.93 @@ -1323,11 +1331,12 @@ 9.94 appendStatement(forNode); 9.95 } finally { 9.96 lc.pop(forNode); 9.97 - if (outer != null) { 9.98 - outer.setFinish(forNode.getFinish()); 9.99 - outer = restoreBlock(outer); 9.100 - appendStatement(new BlockStatement(startLine, outer)); 9.101 - } 9.102 + } 9.103 + 9.104 + if (outer != null) { 9.105 + outer.setFinish(forNode.getFinish()); 9.106 + outer = restoreBlock(outer); 9.107 + appendStatement(new BlockStatement(startLine, outer)); 9.108 } 9.109 } 9.110 9.111 @@ -2699,11 +2708,7 @@ 9.112 } 9.113 9.114 if (isStatement) { 9.115 - int varFlags = VarNode.IS_STATEMENT; 9.116 - if (!topLevel && useBlockScope()) { 9.117 - // mark ES6 block functions as lexically scoped 9.118 - varFlags |= VarNode.IS_LET; 9.119 - } 9.120 + final int varFlags = (topLevel || !useBlockScope()) ? 0 : VarNode.IS_LET; 9.121 final VarNode varNode = new VarNode(functionLine, functionToken, finish, name, functionNode, varFlags); 9.122 if (topLevel) { 9.123 functionDeclarations.add(varNode);
10.1 --- a/src/jdk/nashorn/internal/runtime/ScriptObject.java Thu Nov 27 17:14:01 2014 +0400 10.2 +++ b/src/jdk/nashorn/internal/runtime/ScriptObject.java Thu Nov 27 18:02:28 2014 +0100 10.3 @@ -46,6 +46,8 @@ 10.4 import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid; 10.5 import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndex; 10.6 import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex; 10.7 +import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.isScopeFlag; 10.8 +import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.isStrictFlag; 10.9 import static jdk.nashorn.internal.runtime.linker.NashornGuards.explicitInstanceOfCheck; 10.10 10.11 import java.lang.invoke.MethodHandle; 10.12 @@ -98,7 +100,7 @@ 10.13 * </ul> 10.14 */ 10.15 10.16 -public abstract class ScriptObject implements PropertyAccess { 10.17 +public abstract class ScriptObject implements PropertyAccess, Cloneable { 10.18 /** __proto__ special property name inside object literals. ES6 draft. */ 10.19 public static final String PROTO_PROPERTY_NAME = "__proto__"; 10.20 10.21 @@ -2202,6 +2204,9 @@ 10.22 10.23 if (find != null) { 10.24 if (!find.getProperty().isWritable() && !NashornCallSiteDescriptor.isDeclaration(desc)) { 10.25 + if (NashornCallSiteDescriptor.isScope(desc) && find.getProperty().isLexicalBinding()) { 10.26 + throw typeError("assign.constant", name); // Overwriting ES6 const should throw also in non-strict mode. 10.27 + } 10.28 // Existing, non-writable property 10.29 return createEmptySetMethod(desc, explicitInstanceOfCheck, "property.not.writable", true); 10.30 } 10.31 @@ -3103,7 +3108,7 @@ 10.32 private boolean doesNotHaveEnsureLength(final long longIndex, final long oldLength, final int callSiteFlags) { 10.33 if (longIndex >= oldLength) { 10.34 if (!isExtensible()) { 10.35 - if (NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)) { 10.36 + if (isStrictFlag(callSiteFlags)) { 10.37 throw typeError("object.non.extensible", JSType.toString(longIndex), ScriptRuntime.safeToString(this)); 10.38 } 10.39 return true; 10.40 @@ -3127,7 +3132,7 @@ 10.41 final long oldLength = getArray().length(); 10.42 final long longIndex = ArrayIndex.toLongIndex(index); 10.43 if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) { 10.44 - final boolean strict = NashornCallSiteDescriptor.isStrictFlag(callSiteFlags); 10.45 + final boolean strict = isStrictFlag(callSiteFlags); 10.46 setArray(getArray().set(index, value, strict)); 10.47 doesNotHaveEnsureDelete(longIndex, oldLength, strict); 10.48 } 10.49 @@ -3137,7 +3142,7 @@ 10.50 final long oldLength = getArray().length(); 10.51 final long longIndex = ArrayIndex.toLongIndex(index); 10.52 if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) { 10.53 - final boolean strict = NashornCallSiteDescriptor.isStrictFlag(callSiteFlags); 10.54 + final boolean strict = isStrictFlag(callSiteFlags); 10.55 setArray(getArray().set(index, value, strict)); 10.56 doesNotHaveEnsureDelete(longIndex, oldLength, strict); 10.57 } 10.58 @@ -3147,7 +3152,7 @@ 10.59 final long oldLength = getArray().length(); 10.60 final long longIndex = ArrayIndex.toLongIndex(index); 10.61 if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) { 10.62 - final boolean strict = NashornCallSiteDescriptor.isStrictFlag(callSiteFlags); 10.63 + final boolean strict = isStrictFlag(callSiteFlags); 10.64 setArray(getArray().set(index, value, strict)); 10.65 doesNotHaveEnsureDelete(longIndex, oldLength, strict); 10.66 } 10.67 @@ -3157,7 +3162,7 @@ 10.68 final long oldLength = getArray().length(); 10.69 final long longIndex = ArrayIndex.toLongIndex(index); 10.70 if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) { 10.71 - final boolean strict = NashornCallSiteDescriptor.isStrictFlag(callSiteFlags); 10.72 + final boolean strict = isStrictFlag(callSiteFlags); 10.73 setArray(getArray().set(index, value, strict)); 10.74 doesNotHaveEnsureDelete(longIndex, oldLength, strict); 10.75 } 10.76 @@ -3178,7 +3183,7 @@ 10.77 invalidateGlobalConstant(key); 10.78 10.79 if (f != null && f.isInherited() && !(f.getProperty() instanceof UserAccessorProperty)) { 10.80 - final boolean isScope = NashornCallSiteDescriptor.isScopeFlag(callSiteFlags); 10.81 + final boolean isScope = isScopeFlag(callSiteFlags); 10.82 // If the start object of the find is not this object it means the property was found inside a 10.83 // 'with' statement expression (see WithObject.findProperty()). In this case we forward the 'set' 10.84 // to the 'with' object. 10.85 @@ -3199,16 +3204,19 @@ 10.86 10.87 if (f != null) { 10.88 if (!f.getProperty().isWritable()) { 10.89 - if (NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)) { 10.90 + if (isScopeFlag(callSiteFlags) && f.getProperty().isLexicalBinding()) { 10.91 + throw typeError("assign.constant", key); // Overwriting ES6 const should throw also in non-strict mode. 10.92 + } 10.93 + if (isStrictFlag(callSiteFlags)) { 10.94 throw typeError("property.not.writable", key, ScriptRuntime.safeToString(this)); 10.95 } 10.96 return; 10.97 } 10.98 10.99 - f.setValue(value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)); 10.100 + f.setValue(value, isStrictFlag(callSiteFlags)); 10.101 10.102 } else if (!isExtensible()) { 10.103 - if (NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)) { 10.104 + if (isStrictFlag(callSiteFlags)) { 10.105 throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this)); 10.106 } 10.107 } else { 10.108 @@ -3235,7 +3243,7 @@ 10.109 if (isValidArrayIndex(index)) { 10.110 final ArrayData data = getArray(); 10.111 if (data.has(index)) { 10.112 - setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); 10.113 + setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 10.114 } else { 10.115 doesNotHave(index, value, callSiteFlags); 10.116 } 10.117 @@ -3255,7 +3263,7 @@ 10.118 if (isValidArrayIndex(index)) { 10.119 final ArrayData data = getArray(); 10.120 if (data.has(index)) { 10.121 - setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); 10.122 + setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 10.123 } else { 10.124 doesNotHave(index, value, callSiteFlags); 10.125 } 10.126 @@ -3275,7 +3283,7 @@ 10.127 if (isValidArrayIndex(index)) { 10.128 final ArrayData data = getArray(); 10.129 if (data.has(index)) { 10.130 - setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); 10.131 + setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 10.132 } else { 10.133 doesNotHave(index, value, callSiteFlags); 10.134 } 10.135 @@ -3295,7 +3303,7 @@ 10.136 if (isValidArrayIndex(index)) { 10.137 final ArrayData data = getArray(); 10.138 if (data.has(index)) { 10.139 - setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); 10.140 + setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 10.141 } else { 10.142 doesNotHave(index, value, callSiteFlags); 10.143 } 10.144 @@ -3314,7 +3322,7 @@ 10.145 if (isValidArrayIndex(index)) { 10.146 final ArrayData data = getArray(); 10.147 if (data.has(index)) { 10.148 - setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); 10.149 + setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 10.150 } else { 10.151 doesNotHave(index, value, callSiteFlags); 10.152 } 10.153 @@ -3333,7 +3341,7 @@ 10.154 if (isValidArrayIndex(index)) { 10.155 final ArrayData data = getArray(); 10.156 if (data.has(index)) { 10.157 - setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); 10.158 + setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 10.159 } else { 10.160 doesNotHave(index, value, callSiteFlags); 10.161 } 10.162 @@ -3352,7 +3360,7 @@ 10.163 if (isValidArrayIndex(index)) { 10.164 final ArrayData data = getArray(); 10.165 if (data.has(index)) { 10.166 - setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); 10.167 + setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 10.168 } else { 10.169 doesNotHave(index, value, callSiteFlags); 10.170 } 10.171 @@ -3371,7 +3379,7 @@ 10.172 if (isValidArrayIndex(index)) { 10.173 final ArrayData data = getArray(); 10.174 if (data.has(index)) { 10.175 - setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); 10.176 + setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 10.177 } else { 10.178 doesNotHave(index, value, callSiteFlags); 10.179 } 10.180 @@ -3390,7 +3398,7 @@ 10.181 if (isValidArrayIndex(index)) { 10.182 final ArrayData data = getArray(); 10.183 if (data.has(index)) { 10.184 - setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); 10.185 + setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 10.186 } else { 10.187 doesNotHave(index, value, callSiteFlags); 10.188 } 10.189 @@ -3409,7 +3417,7 @@ 10.190 if (isValidArrayIndex(index)) { 10.191 final ArrayData data = getArray(); 10.192 if (data.has(index)) { 10.193 - setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); 10.194 + setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 10.195 } else { 10.196 doesNotHave(index, value, callSiteFlags); 10.197 } 10.198 @@ -3428,7 +3436,7 @@ 10.199 if (isValidArrayIndex(index)) { 10.200 final ArrayData data = getArray(); 10.201 if (data.has(index)) { 10.202 - setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); 10.203 + setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 10.204 } else { 10.205 doesNotHave(index, value, callSiteFlags); 10.206 } 10.207 @@ -3447,7 +3455,7 @@ 10.208 if (isValidArrayIndex(index)) { 10.209 final ArrayData data = getArray(); 10.210 if (data.has(index)) { 10.211 - setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); 10.212 + setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 10.213 } else { 10.214 doesNotHave(index, value, callSiteFlags); 10.215 } 10.216 @@ -3465,7 +3473,7 @@ 10.217 if (isValidArrayIndex(index)) { 10.218 if (getArray().has(index)) { 10.219 final ArrayData data = getArray(); 10.220 - setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); 10.221 + setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 10.222 } else { 10.223 doesNotHave(index, value, callSiteFlags); 10.224 } 10.225 @@ -3483,7 +3491,7 @@ 10.226 if (isValidArrayIndex(index)) { 10.227 final ArrayData data = getArray(); 10.228 if (data.has(index)) { 10.229 - setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); 10.230 + setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 10.231 } else { 10.232 doesNotHave(index, value, callSiteFlags); 10.233 } 10.234 @@ -3502,7 +3510,7 @@ 10.235 if (isValidArrayIndex(index)) { 10.236 final ArrayData data = getArray(); 10.237 if (data.has(index)) { 10.238 - setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); 10.239 + setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 10.240 } else { 10.241 doesNotHave(index, value, callSiteFlags); 10.242 } 10.243 @@ -3521,7 +3529,7 @@ 10.244 if (isValidArrayIndex(index)) { 10.245 final ArrayData data = getArray(); 10.246 if (data.has(index)) { 10.247 - setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); 10.248 + setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 10.249 } else { 10.250 doesNotHave(index, value, callSiteFlags); 10.251 } 10.252 @@ -3686,6 +3694,29 @@ 10.253 } 10.254 10.255 /** 10.256 + * Return a shallow copy of this ScriptObject. 10.257 + * @return a shallow copy. 10.258 + */ 10.259 + public final ScriptObject copy() { 10.260 + try { 10.261 + return clone(); 10.262 + } catch (final CloneNotSupportedException e) { 10.263 + throw new RuntimeException(e); 10.264 + } 10.265 + } 10.266 + 10.267 + @Override 10.268 + protected ScriptObject clone() throws CloneNotSupportedException { 10.269 + final ScriptObject clone = (ScriptObject) super.clone(); 10.270 + if (objectSpill != null) { 10.271 + clone.objectSpill = objectSpill.clone(); 10.272 + clone.primitiveSpill = primitiveSpill.clone(); 10.273 + } 10.274 + clone.arrayData = arrayData.copy(); 10.275 + return clone; 10.276 + } 10.277 + 10.278 + /** 10.279 * Make a new UserAccessorProperty property. getter and setter functions are stored in 10.280 * this ScriptObject and slot values are used in property object. 10.281 *
11.1 --- a/src/jdk/nashorn/internal/runtime/arrays/ArrayData.java Thu Nov 27 17:14:01 2014 +0400 11.2 +++ b/src/jdk/nashorn/internal/runtime/arrays/ArrayData.java Thu Nov 27 18:02:28 2014 +0100 11.3 @@ -61,9 +61,9 @@ 11.4 /** 11.5 * Length of the array data. Not necessarily length of the wrapped array. 11.6 * This is private to ensure that no one in a subclass is able to touch the length 11.7 - * without going through {@link setLength}. This is used to implement 11.8 + * without going through {@link #setLength}. This is used to implement 11.9 * {@link LengthNotWritableFilter}s, ensuring that there are no ways past 11.10 - * a {@link setLength} function replaced by a nop 11.11 + * a {@link #setLength} function replaced by a nop 11.12 */ 11.13 private long length; 11.14 11.15 @@ -79,11 +79,7 @@ 11.16 */ 11.17 private static class UntouchedArrayData extends ContinuousArrayData { 11.18 private UntouchedArrayData() { 11.19 - this(0); 11.20 - } 11.21 - 11.22 - private UntouchedArrayData(final int length) { 11.23 - super(length); 11.24 + super(0); 11.25 } 11.26 11.27 private ArrayData toRealArrayData() { 11.28 @@ -100,7 +96,8 @@ 11.29 11.30 @Override 11.31 public ContinuousArrayData copy() { 11.32 - return new UntouchedArrayData((int)length()); 11.33 + assert length() == 0; 11.34 + return this; 11.35 } 11.36 11.37 @Override 11.38 @@ -246,7 +243,7 @@ 11.39 public Class<?> getBoxedElementType() { 11.40 return Integer.class; 11.41 } 11.42 - }; 11.43 + } 11.44 11.45 /** 11.46 * Constructor
12.1 --- a/src/jdk/nashorn/internal/runtime/resources/Messages.properties Thu Nov 27 17:14:01 2014 +0400 12.2 +++ b/src/jdk/nashorn/internal/runtime/resources/Messages.properties Thu Nov 27 18:02:28 2014 +0100 12.3 @@ -116,6 +116,7 @@ 12.4 type.error.cannot.convert.to.interface=object {0} cannot be converted to {1} due to "{2}" 12.5 type.error.array.reduce.invalid.init=invalid initialValue for Array.prototype.reduce 12.6 type.error.array.reduceright.invalid.init=invalid initialValue for Array.prototype.reduceRight 12.7 +type.error.assign.constant=Assignment to constant "{0}" 12.8 type.error.cannot.get.default.string=Cannot get default string value 12.9 type.error.cannot.get.default.number=Cannot get default number value 12.10 type.error.cant.apply.with.to.null=Cannot apply "with" to null 12.11 @@ -166,6 +167,7 @@ 12.12 syntax.error.strict.cant.delete=cannot delete "{0}" in strict mode 12.13 syntax.error.redeclare.variable=Variable "{0}" has already been declared 12.14 syntax.error.assign.constant=Assignment to constant "{0}" 12.15 +syntax.error.unprotected.switch.declaration=Unsupported {0} declaration in unprotected switch statement 12.16 12.17 io.error.cant.write=cannot write "{0}" 12.18 config.error.no.dest=no destination directory supplied
13.1 --- a/test/script/basic/es6/for-let.js Thu Nov 27 17:14:01 2014 +0400 13.2 +++ b/test/script/basic/es6/for-let.js Thu Nov 27 18:02:28 2014 +0100 13.3 @@ -39,3 +39,40 @@ 13.4 } catch (e) { 13.5 print(e); 13.6 } 13.7 + 13.8 +let a = []; 13.9 + 13.10 +for (let i = 0; i < 10; i++) { 13.11 + a.push(function() { print(i); }); 13.12 +} 13.13 + 13.14 +a.forEach(function(f) { f(); }); 13.15 + 13.16 +a = []; 13.17 + 13.18 +for (let i = 0; i < 10; i++) { 13.19 + if (i == 5) { 13.20 + i = "foo"; 13.21 + } 13.22 + a.push(function() { print(i); }); 13.23 +} 13.24 + 13.25 +a.forEach(function(f) { f(); }); 13.26 + 13.27 +try { 13.28 + print(i); 13.29 +} catch (e) { 13.30 + print(e); 13.31 +} 13.32 + 13.33 +a = []; 13.34 + 13.35 +for (let i = 0; i < 20; i++) { 13.36 + if (i % 2 == 1) { 13.37 + i += 2; 13.38 + continue; 13.39 + } 13.40 + a.push(function() { print(i); }); 13.41 +} 13.42 + 13.43 +a.forEach(function(f) { f(); });
14.1 --- a/test/script/basic/es6/for-let.js.EXPECTED Thu Nov 27 17:14:01 2014 +0400 14.2 +++ b/test/script/basic/es6/for-let.js.EXPECTED Thu Nov 27 18:02:28 2014 +0100 14.3 @@ -9,3 +9,25 @@ 14.4 8 14.5 9 14.6 ReferenceError: "i" is not defined 14.7 +0 14.8 +1 14.9 +2 14.10 +3 14.11 +4 14.12 +5 14.13 +6 14.14 +7 14.15 +8 14.16 +9 14.17 +0 14.18 +1 14.19 +2 14.20 +3 14.21 +4 14.22 +foo 14.23 +ReferenceError: "i" is not defined 14.24 +0 14.25 +4 14.26 +8 14.27 +12 14.28 +16
15.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 15.2 +++ b/test/script/basic/es6/let-const-statement-context.js Thu Nov 27 18:02:28 2014 +0100 15.3 @@ -0,0 +1,49 @@ 15.4 +/* 15.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 15.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 15.7 + * 15.8 + * This code is free software; you can redistribute it and/or modify it 15.9 + * under the terms of the GNU General Public License version 2 only, as 15.10 + * published by the Free Software Foundation. 15.11 + * 15.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 15.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15.15 + * version 2 for more details (a copy is included in the LICENSE file that 15.16 + * accompanied this code). 15.17 + * 15.18 + * You should have received a copy of the GNU General Public License version 15.19 + * 2 along with this work; if not, write to the Free Software Foundation, 15.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 15.21 + * 15.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 15.23 + * or visit www.oracle.com if you need additional information or have any 15.24 + * questions. 15.25 + */ 15.26 + 15.27 +/** 15.28 + * JDK-8057980: let & const: remaining issues with lexical scoping 15.29 + * 15.30 + * @test 15.31 + * @run 15.32 + * @option --language=es6 15.33 + */ 15.34 + 15.35 +function tryEval(s) { 15.36 + try { 15.37 + eval(s); 15.38 + } catch (e) { 15.39 + print(String(e).replace(/\\/g, "/")); 15.40 + } 15.41 +} 15.42 + 15.43 +tryEval('if (true) let x = 1;'); 15.44 +tryEval('if (true) const x = 1;'); 15.45 +tryEval('while (true) let x = 1;'); 15.46 +tryEval('while (true) const x = 1;'); 15.47 +tryEval('for (;;) let x = 1;'); 15.48 +tryEval('for (;;) const x = 1;'); 15.49 +tryEval('do let x = 1; while (true);'); 15.50 +tryEval('do const x = 1; while (true);'); 15.51 +tryEval('with (y) const x = 1;'); 15.52 +tryEval('with (y) let x = 1;');
16.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 16.2 +++ b/test/script/basic/es6/let-const-statement-context.js.EXPECTED Thu Nov 27 18:02:28 2014 +0100 16.3 @@ -0,0 +1,30 @@ 16.4 +SyntaxError: test/script/basic/es6/let-const-statement-context.js#34:8<eval>:1:10 Expected statement but found let declaration 16.5 +if (true) let x = 1; 16.6 + ^ 16.7 +SyntaxError: test/script/basic/es6/let-const-statement-context.js#34:8<eval>:1:10 Expected statement but found const declaration 16.8 +if (true) const x = 1; 16.9 + ^ 16.10 +SyntaxError: test/script/basic/es6/let-const-statement-context.js#34:8<eval>:1:13 Expected statement but found let declaration 16.11 +while (true) let x = 1; 16.12 + ^ 16.13 +SyntaxError: test/script/basic/es6/let-const-statement-context.js#34:8<eval>:1:13 Expected statement but found const declaration 16.14 +while (true) const x = 1; 16.15 + ^ 16.16 +SyntaxError: test/script/basic/es6/let-const-statement-context.js#34:8<eval>:1:9 Expected statement but found let declaration 16.17 +for (;;) let x = 1; 16.18 + ^ 16.19 +SyntaxError: test/script/basic/es6/let-const-statement-context.js#34:8<eval>:1:9 Expected statement but found const declaration 16.20 +for (;;) const x = 1; 16.21 + ^ 16.22 +SyntaxError: test/script/basic/es6/let-const-statement-context.js#34:8<eval>:1:3 Expected statement but found let declaration 16.23 +do let x = 1; while (true); 16.24 + ^ 16.25 +SyntaxError: test/script/basic/es6/let-const-statement-context.js#34:8<eval>:1:3 Expected statement but found const declaration 16.26 +do const x = 1; while (true); 16.27 + ^ 16.28 +SyntaxError: test/script/basic/es6/let-const-statement-context.js#34:8<eval>:1:9 Expected statement but found const declaration 16.29 +with (y) const x = 1; 16.30 + ^ 16.31 +SyntaxError: test/script/basic/es6/let-const-statement-context.js#34:8<eval>:1:9 Expected statement but found let declaration 16.32 +with (y) let x = 1; 16.33 + ^
17.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 17.2 +++ b/test/script/basic/es6/let-const-switch.js Thu Nov 27 18:02:28 2014 +0100 17.3 @@ -0,0 +1,45 @@ 17.4 +/* 17.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 17.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 17.7 + * 17.8 + * This code is free software; you can redistribute it and/or modify it 17.9 + * under the terms of the GNU General Public License version 2 only, as 17.10 + * published by the Free Software Foundation. 17.11 + * 17.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 17.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 17.15 + * version 2 for more details (a copy is included in the LICENSE file that 17.16 + * accompanied this code). 17.17 + * 17.18 + * You should have received a copy of the GNU General Public License version 17.19 + * 2 along with this work; if not, write to the Free Software Foundation, 17.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 17.21 + * 17.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 17.23 + * or visit www.oracle.com if you need additional information or have any 17.24 + * questions. 17.25 + */ 17.26 + 17.27 +/** 17.28 + * JDK-8057980: let & const: remaining issues with lexical scoping 17.29 + * 17.30 + * @test 17.31 + * @run 17.32 + * @option --language=es6 17.33 + */ 17.34 + 17.35 +function tryEval(s) { 17.36 + try { 17.37 + eval(s); 17.38 + } catch (e) { 17.39 + print(String(e).replace(/\\/g, "/")); 17.40 + } 17.41 +} 17.42 + 17.43 +tryEval('var x = 0; switch (x) { case 0: { let x = 1; print(x); } case 1: { let x = 2; print(x); }} print(x);'); 17.44 +tryEval('var x = 0; switch (x) { case 0: { const x = 1; print(x); } case 1: { const x = 2; print(x); }} print(x);'); 17.45 + 17.46 +// TODO: the following should not throw 17.47 +tryEval('switch (x) { case 0: let x = 1; }'); 17.48 +tryEval('switch (x) { case 0: const x = 1; }');
18.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 18.2 +++ b/test/script/basic/es6/let-const-switch.js.EXPECTED Thu Nov 27 18:02:28 2014 +0100 18.3 @@ -0,0 +1,12 @@ 18.4 +1 18.5 +2 18.6 +0 18.7 +1 18.8 +2 18.9 +0 18.10 +SyntaxError: test/script/basic/es6/let-const-switch.js#34:8<eval>:1:25 Unsupported let declaration in unprotected switch statement 18.11 +switch (x) { case 0: let x = 1; } 18.12 + ^ 18.13 +SyntaxError: test/script/basic/es6/let-const-switch.js#34:8<eval>:1:27 Unsupported const declaration in unprotected switch statement 18.14 +switch (x) { case 0: const x = 1; } 18.15 + ^
19.1 --- a/test/script/basic/es6/let-load.js Thu Nov 27 17:14:01 2014 +0400 19.2 +++ b/test/script/basic/es6/let-load.js Thu Nov 27 18:02:28 2014 +0100 19.3 @@ -40,17 +40,8 @@ 19.4 } 19.5 19.6 print("imported var: " + a); 19.7 -try { 19.8 - print("imported let: " + b); 19.9 -} catch (e) { 19.10 - print(e); 19.11 -} 19.12 - 19.13 -try { 19.14 - print("imported const: " + c); 19.15 -} catch (e) { 19.16 - print(e); 19.17 -} 19.18 +print("imported let: " + b); 19.19 +print("imported const: " + c); 19.20 19.21 top(); 19.22 19.23 @@ -60,4 +51,10 @@ 19.24 print(e); 19.25 } 19.26 19.27 +try { 19.28 + c = "foo"; 19.29 +} catch (e) { 19.30 + print(e); 19.31 +} 19.32 19.33 +
20.1 --- a/test/script/basic/es6/let-load.js.EXPECTED Thu Nov 27 17:14:01 2014 +0400 20.2 +++ b/test/script/basic/es6/let-load.js.EXPECTED Thu Nov 27 18:02:28 2014 +0100 20.3 @@ -6,3 +6,4 @@ 20.4 imported const: 3 20.5 top level function 20.6 ReferenceError: "block" is not defined 20.7 +TypeError: Assignment to constant "c"
21.1 --- a/test/script/basic/es6/let_const_closure.js.EXPECTED Thu Nov 27 17:14:01 2014 +0400 21.2 +++ b/test/script/basic/es6/let_const_closure.js.EXPECTED Thu Nov 27 18:02:28 2014 +0100 21.3 @@ -5,9 +5,9 @@ 21.4 test 21.5 test 21.6 test 21.7 -3 21.8 -3 21.9 -3 21.10 0 21.11 1 21.12 2 21.13 +0 21.14 +1 21.15 +2
22.1 --- a/test/script/basic/es6/lexical-toplevel.js.EXPECTED Thu Nov 27 17:14:01 2014 +0400 22.2 +++ b/test/script/basic/es6/lexical-toplevel.js.EXPECTED Thu Nov 27 18:02:28 2014 +0100 22.3 @@ -13,6 +13,7 @@ 22.4 false 22.5 true 22.6 true 22.7 +TypeError: Assignment to constant "CONST" 22.8 VAR 22.9 LETLET 22.10 CONST 22.11 @@ -28,3 +29,4 @@ 22.12 false 22.13 true 22.14 true 22.15 +TypeError: Assignment to constant "CONST"