# HG changeset patch # User hannesw # Date 1417102973 -3600 # Node ID 56f6161c3e5519b2f896b58c939948a30729d0a6 # Parent c6c53c5adc519715181a4eb610323ca59d7b6f91 8057980: let & const: remaining issues with lexical scoping Reviewed-by: lagergren, attila diff -r c6c53c5adc51 -r 56f6161c3e55 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java Thu Nov 27 17:14:01 2014 +0400 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java Thu Nov 27 16:42:53 2014 +0100 @@ -189,7 +189,7 @@ * @param body the body of the FunctionNode we are entering */ private void acceptDeclarations(final FunctionNode functionNode, final Block body) { - // This visitor will assign symbol to all declared variables, except "var" declarations in for loop initializers. + // This visitor will assign symbol to all declared variables. body.accept(new NodeVisitor(new LexicalContext()) { @Override protected boolean enterDefault(final Node node) { @@ -200,16 +200,17 @@ @Override public Node leaveVarNode(final VarNode varNode) { - if (varNode.isStatement()) { - final IdentNode ident = varNode.getName(); - final Block block = varNode.isBlockScoped() ? getLexicalContext().getCurrentBlock() : body; - final Symbol symbol = defineSymbol(block, ident.getName(), ident, varNode.getSymbolFlags()); - if (varNode.isFunctionDeclaration()) { - symbol.setIsFunctionDeclaration(); - } - return varNode.setName(ident.setSymbol(symbol)); + final IdentNode ident = varNode.getName(); + final boolean blockScoped = varNode.isBlockScoped(); + if (blockScoped && lc.inUnprotectedSwitchContext()) { + throwUnprotectedSwitchError(varNode); } - return varNode; + final Block block = blockScoped ? lc.getCurrentBlock() : body; + final Symbol symbol = defineSymbol(block, ident.getName(), ident, varNode.getSymbolFlags()); + if (varNode.isFunctionDeclaration()) { + symbol.setIsFunctionDeclaration(); + } + return varNode.setName(ident.setSymbol(symbol)); } }); } @@ -1048,6 +1049,15 @@ return !(units == null || units.isEmpty()); } + private void throwUnprotectedSwitchError(final VarNode varNode) { + // Block scoped declarations in switch statements without explicit blocks should be declared + // in a common block that contains all the case clauses. We cannot support this without a + // fundamental rewrite of how switch statements are handled (case nodes contain blocks and are + // directly contained by switch node). As a temporary solution we throw a reference error here. + final String msg = ECMAErrors.getMessage("syntax.error.unprotected.switch.declaration", varNode.isLet() ? "let" : "const"); + throwParserException(msg, varNode); + } + private void throwParserException(final String message, final Node origin) { if (origin == null) { throw new ParserException(message); diff -r c6c53c5adc51 -r 56f6161c3e55 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java Thu Nov 27 17:14:01 2014 +0400 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java Thu Nov 27 16:42:53 2014 +0100 @@ -3264,6 +3264,13 @@ emitContinueLabel(continueLabel, liveLocalsOnContinue); } + if (loopNode.hasPerIterationScope() && lc.getParentBlock().needsScope()) { + // ES6 for loops with LET init need a new scope for each iteration. We just create a shallow copy here. + method.loadCompilerConstant(SCOPE); + method.invoke(virtualCallNoLookup(ScriptObject.class, "copy", ScriptObject.class)); + method.storeCompilerConstant(SCOPE); + } + if(method.isReachable()) { if(modify != null) { lineNumber(loopNode); diff -r c6c53c5adc51 -r 56f6161c3e55 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Lower.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Lower.java Thu Nov 27 17:14:01 2014 +0400 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Lower.java Thu Nov 27 16:42:53 2014 +0100 @@ -525,7 +525,7 @@ if (isAlwaysTrue(test)) { //turn it into a for node without a test. - final ForNode forNode = (ForNode)new ForNode(whileNode.getLineNumber(), whileNode.getToken(), whileNode.getFinish(), body, ForNode.IS_FOR).accept(this); + final ForNode forNode = (ForNode)new ForNode(whileNode.getLineNumber(), whileNode.getToken(), whileNode.getFinish(), body, 0).accept(this); lc.replace(whileNode, forNode); return forNode; } diff -r c6c53c5adc51 -r 56f6161c3e55 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ForNode.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ForNode.java Thu Nov 27 17:14:01 2014 +0400 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ForNode.java Thu Nov 27 16:42:53 2014 +0100 @@ -45,14 +45,14 @@ /** Iterator symbol. */ private Symbol iterator; - /** Is this a normal for loop? */ - public static final int IS_FOR = 1 << 0; - /** Is this a normal for in loop? */ - public static final int IS_FOR_IN = 1 << 1; + public static final int IS_FOR_IN = 1 << 0; /** Is this a normal for each in loop? */ - public static final int IS_FOR_EACH = 1 << 2; + public static final int IS_FOR_EACH = 1 << 1; + + /** Does this loop need a per-iteration scope because its init contain a LET declaration? */ + public static final int PER_ITERATION_SCOPE = 1 << 2; private final int flags; @@ -264,4 +264,9 @@ JoinPredecessor setLocalVariableConversionChanged(final LexicalContext lc, final LocalVariableConversion conversion) { return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); } + + @Override + public boolean hasPerIterationScope() { + return (flags & PER_ITERATION_SCOPE) != 0; + } } diff -r c6c53c5adc51 -r 56f6161c3e55 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LexicalContext.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LexicalContext.java Thu Nov 27 17:14:01 2014 +0400 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LexicalContext.java Thu Nov 27 16:42:53 2014 +0100 @@ -597,6 +597,20 @@ throw new AssertionError(target + " was expected in lexical context " + LexicalContext.this + " but wasn't"); } + /** + * Checks whether the current context is inside a switch statement without explicit blocks (curly braces). + * @return true if in unprotected switch statement + */ + public boolean inUnprotectedSwitchContext() { + for (int i = sp; i > 0; i--) { + final LexicalContextNode next = stack[i]; + if (next instanceof Block) { + return stack[i - 1] instanceof SwitchNode; + } + } + return false; + } + @Override public String toString() { final StringBuffer sb = new StringBuffer(); diff -r c6c53c5adc51 -r 56f6161c3e55 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LoopNode.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LoopNode.java Thu Nov 27 17:14:01 2014 +0400 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LoopNode.java Thu Nov 27 16:42:53 2014 +0100 @@ -177,4 +177,10 @@ * @return new loop node if changed otherwise the same */ public abstract LoopNode setControlFlowEscapes(final LexicalContext lc, final boolean controlFlowEscapes); + + /** + * Does this loop have a LET declaration and hence require a per-iteration scope? + * @return true if a per-iteration scope is required. + */ + public abstract boolean hasPerIterationScope(); } diff -r c6c53c5adc51 -r 56f6161c3e55 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/VarNode.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/VarNode.java Thu Nov 27 17:14:01 2014 +0400 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/VarNode.java Thu Nov 27 16:42:53 2014 +0100 @@ -45,19 +45,16 @@ /** Is this a var statement (as opposed to a "var" in a for loop statement) */ private final int flags; - /** Flag that determines if this function node is a statement */ - public static final int IS_STATEMENT = 1 << 0; - /** Flag for ES6 LET declaration */ - public static final int IS_LET = 1 << 1; + public static final int IS_LET = 1 << 0; /** Flag for ES6 CONST declaration */ - public static final int IS_CONST = 1 << 2; + public static final int IS_CONST = 1 << 1; /** Flag that determines if this is the last function declaration in a function * This is used to micro optimize the placement of return value assignments for * a program node */ - public static final int IS_LAST_FUNCTION_DECLARATION = 1 << 3; + public static final int IS_LAST_FUNCTION_DECLARATION = 1 << 2; /** * Constructor @@ -69,7 +66,7 @@ * @param init init node or null if just a declaration */ public VarNode(final int lineNumber, final long token, final int finish, final IdentNode name, final Expression init) { - this(lineNumber, token, finish, name, init, IS_STATEMENT); + this(lineNumber, token, finish, name, init, 0); } private VarNode(final VarNode varNode, final IdentNode name, final Expression init, final int flags) { @@ -260,14 +257,6 @@ } /** - * Returns true if this is a var statement (as opposed to a var initializer in a for loop). - * @return true if this is a var statement (as opposed to a var initializer in a for loop). - */ - public boolean isStatement() { - return (flags & IS_STATEMENT) != 0; - } - - /** * Returns true if this is a function declaration. * @return true if this is a function declaration. */ diff -r c6c53c5adc51 -r 56f6161c3e55 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/WhileNode.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/WhileNode.java Thu Nov 27 17:14:01 2014 +0400 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/WhileNode.java Thu Nov 27 16:42:53 2014 +0100 @@ -150,4 +150,9 @@ } return test == null; } + + @Override + public boolean hasPerIterationScope() { + return false; + } } diff -r c6c53c5adc51 -r 56f6161c3e55 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java Thu Nov 27 17:14:01 2014 +0400 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java Thu Nov 27 16:42:53 2014 +0100 @@ -554,7 +554,7 @@ // Set up new block. Captures first token. final ParserContextBlockNode newBlock = newBlock(); try { - statement(); + statement(false, false, true); } finally { restoreBlock(newBlock); } @@ -770,7 +770,7 @@ try { // Get the next element. - statement(true, allowPropertyFunction); + statement(true, allowPropertyFunction, false); allowPropertyFunction = false; // check for directive prologues @@ -860,13 +860,15 @@ * Parse any of the basic statement types. */ private void statement() { - statement(false, false); + statement(false, false, false); } /** * @param topLevel does this statement occur at the "top level" of a script or a function? + * @param allowPropertyFunction allow property "get" and "set" functions? + * @param singleStatement are we in a single statement context? */ - private void statement(final boolean topLevel, final boolean allowPropertyFunction) { + private void statement(final boolean topLevel, final boolean allowPropertyFunction, final boolean singleStatement) { if (type == FUNCTION) { // As per spec (ECMA section 12), function declarations as arbitrary statement // is not "portable". Implementation can issue a warning or disallow the same. @@ -930,6 +932,9 @@ break; default: if (useBlockScope() && (type == LET || type == CONST)) { + if (singleStatement) { + throw error(AbstractParser.message("expected.stmt", type.getName() + " declaration"), token); + } variableStatement(type, true); break; } @@ -1055,7 +1060,7 @@ next(); final List vars = new ArrayList<>(); - int varFlags = VarNode.IS_STATEMENT; + int varFlags = 0; if (varType == LET) { varFlags |= VarNode.IS_LET; } else if (varType == CONST) { @@ -1200,7 +1205,6 @@ final int startLine = start; final ParserContextBlockNode outer = useBlockScope() ? newBlock() : null; - // Create FOR node, capturing FOR token. final ParserContextLoopNode forNode = new ParserContextLoopNode(); lc.push(forNode); @@ -1228,19 +1232,22 @@ switch (type) { case VAR: - // Var statements captured in for outer block. + // Var declaration captured in for outer block. vars = variableStatement(type, false); break; case SEMICOLON: break; default: if (useBlockScope() && (type == LET || type == CONST)) { - // LET/CONST captured in container block created above. + if (type == LET) { + flags |= ForNode.PER_ITERATION_SCOPE; + } + // LET/CONST declaration captured in container block created above. vars = variableStatement(type, false); break; } if (env._const_as_var && type == CONST) { - // Var statements captured in for outer block. + // Var declaration captured in for outer block. vars = variableStatement(TokenType.VAR, false); break; } @@ -1316,21 +1323,22 @@ body = getStatement(); } finally { lc.pop(forNode); - if (vars != null) { - for (final VarNode var : vars) { - appendStatement(var); - } - } - if (body != null) { - appendStatement(new ForNode(forLine, forToken, body.getFinish(), body, (forNode.getFlags() | flags), init, test, modify)); + } + + if (vars != null) { + for (final VarNode var : vars) { + appendStatement(var); } - if (outer != null) { - restoreBlock(outer); - appendStatement(new BlockStatement(startLine, new Block( - outer.getToken(), - body.getFinish(), - outer.getStatements()))); - } + } + if (body != null) { + appendStatement(new ForNode(forLine, forToken, body.getFinish(), body, (forNode.getFlags() | flags), init, test, modify)); + } + if (outer != null) { + restoreBlock(outer); + appendStatement(new BlockStatement(startLine, new Block( + outer.getToken(), + body.getFinish(), + outer.getStatements()))); } } @@ -1364,9 +1372,10 @@ body = getStatement(); } finally { lc.pop(whileNode); - if (body != null){ - appendStatement(new WhileNode(whileLine, whileToken, body.getFinish(), false, test, body)); - } + } + + if (body != null) { + appendStatement(new WhileNode(whileLine, whileToken, body.getFinish(), false, test, body)); } } @@ -1408,8 +1417,9 @@ } } finally { lc.pop(doWhileNode); - appendStatement(new WhileNode(doLine, doToken, finish, true, test, body)); } + + appendStatement(new WhileNode(doLine, doToken, finish, true, test, body)); } /** @@ -1607,17 +1617,12 @@ throw error(AbstractParser.message("strict.no.with"), withToken); } - Expression expression = null; - Block body = null; - try { - expect(LPAREN); - expression = expression(); - expect(RPAREN); - body = getStatement(); - } finally { - appendStatement(new WithNode(withLine, withToken, finish, expression, body)); - } - + expect(LPAREN); + final Expression expression = expression(); + expect(RPAREN); + final Block body = getStatement(); + + appendStatement(new WithNode(withLine, withToken, finish, expression, body)); } /** @@ -1706,8 +1711,9 @@ next(); } finally { lc.pop(switchNode); - appendStatement(new SwitchNode(switchLine, switchToken, finish, expression, cases, defaultCase)); } + + appendStatement(new SwitchNode(switchLine, switchToken, finish, expression, cases, defaultCase)); } /** @@ -1738,10 +1744,9 @@ } finally { assert lc.peek() instanceof ParserContextLabelNode; lc.pop(labelNode); - if (ident != null){ - appendStatement(new LabelNode(line, labelToken, finish, ident.getName(), body)); - } } + + appendStatement(new LabelNode(line, labelToken, finish, ident.getName(), body)); } /** @@ -2725,12 +2730,9 @@ functionBody); if (isStatement) { - int varFlags = VarNode.IS_STATEMENT; - if (!topLevel && useBlockScope()) { - // mark ES6 block functions as lexically scoped - varFlags |= VarNode.IS_LET; - } - final VarNode varNode = new VarNode(functionLine, functionToken, finish, name, function, varFlags); + // mark ES6 block functions as lexically scoped + final int varFlags = (topLevel || !useBlockScope()) ? 0 : VarNode.IS_LET; + final VarNode varNode = new VarNode(functionLine, functionToken, finish, name, function, varFlags); if (topLevel) { functionDeclarations.add(varNode); } else if (useBlockScope()) { diff -r c6c53c5adc51 -r 56f6161c3e55 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextSwitchNode.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextSwitchNode.java Thu Nov 27 17:14:01 2014 +0400 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextSwitchNode.java Thu Nov 27 16:42:53 2014 +0100 @@ -25,7 +25,7 @@ package jdk.nashorn.internal.parser; /** - * A ParserContextNode that represents a SwithcNode that is currently being parsed + * A ParserContextNode that represents a SwitchNode that is currently being parsed */ class ParserContextSwitchNode extends ParserContextBaseNode implements ParserContextBreakableNode { diff -r c6c53c5adc51 -r 56f6161c3e55 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java Thu Nov 27 17:14:01 2014 +0400 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java Thu Nov 27 16:42:53 2014 +0100 @@ -46,6 +46,8 @@ import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid; import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndex; import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex; +import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.isScopeFlag; +import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.isStrictFlag; import static jdk.nashorn.internal.runtime.linker.NashornGuards.explicitInstanceOfCheck; import java.lang.invoke.MethodHandle; @@ -98,7 +100,7 @@ * */ -public abstract class ScriptObject implements PropertyAccess { +public abstract class ScriptObject implements PropertyAccess, Cloneable { /** __proto__ special property name inside object literals. ES6 draft. */ public static final String PROTO_PROPERTY_NAME = "__proto__"; @@ -2202,6 +2204,9 @@ if (find != null) { if (!find.getProperty().isWritable() && !NashornCallSiteDescriptor.isDeclaration(desc)) { + if (NashornCallSiteDescriptor.isScope(desc) && find.getProperty().isLexicalBinding()) { + throw typeError("assign.constant", name); // Overwriting ES6 const should throw also in non-strict mode. + } // Existing, non-writable property return createEmptySetMethod(desc, explicitInstanceOfCheck, "property.not.writable", true); } @@ -3103,7 +3108,7 @@ private boolean doesNotHaveEnsureLength(final long longIndex, final long oldLength, final int callSiteFlags) { if (longIndex >= oldLength) { if (!isExtensible()) { - if (NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)) { + if (isStrictFlag(callSiteFlags)) { throw typeError("object.non.extensible", JSType.toString(longIndex), ScriptRuntime.safeToString(this)); } return true; @@ -3127,7 +3132,7 @@ final long oldLength = getArray().length(); final long longIndex = ArrayIndex.toLongIndex(index); if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) { - final boolean strict = NashornCallSiteDescriptor.isStrictFlag(callSiteFlags); + final boolean strict = isStrictFlag(callSiteFlags); setArray(getArray().set(index, value, strict)); doesNotHaveEnsureDelete(longIndex, oldLength, strict); } @@ -3137,7 +3142,7 @@ final long oldLength = getArray().length(); final long longIndex = ArrayIndex.toLongIndex(index); if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) { - final boolean strict = NashornCallSiteDescriptor.isStrictFlag(callSiteFlags); + final boolean strict = isStrictFlag(callSiteFlags); setArray(getArray().set(index, value, strict)); doesNotHaveEnsureDelete(longIndex, oldLength, strict); } @@ -3147,7 +3152,7 @@ final long oldLength = getArray().length(); final long longIndex = ArrayIndex.toLongIndex(index); if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) { - final boolean strict = NashornCallSiteDescriptor.isStrictFlag(callSiteFlags); + final boolean strict = isStrictFlag(callSiteFlags); setArray(getArray().set(index, value, strict)); doesNotHaveEnsureDelete(longIndex, oldLength, strict); } @@ -3157,7 +3162,7 @@ final long oldLength = getArray().length(); final long longIndex = ArrayIndex.toLongIndex(index); if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) { - final boolean strict = NashornCallSiteDescriptor.isStrictFlag(callSiteFlags); + final boolean strict = isStrictFlag(callSiteFlags); setArray(getArray().set(index, value, strict)); doesNotHaveEnsureDelete(longIndex, oldLength, strict); } @@ -3178,7 +3183,7 @@ invalidateGlobalConstant(key); if (f != null && f.isInherited() && !(f.getProperty() instanceof UserAccessorProperty)) { - final boolean isScope = NashornCallSiteDescriptor.isScopeFlag(callSiteFlags); + final boolean isScope = isScopeFlag(callSiteFlags); // If the start object of the find is not this object it means the property was found inside a // 'with' statement expression (see WithObject.findProperty()). In this case we forward the 'set' // to the 'with' object. @@ -3199,16 +3204,19 @@ if (f != null) { if (!f.getProperty().isWritable()) { - if (NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)) { + if (isScopeFlag(callSiteFlags) && f.getProperty().isLexicalBinding()) { + throw typeError("assign.constant", key); // Overwriting ES6 const should throw also in non-strict mode. + } + if (isStrictFlag(callSiteFlags)) { throw typeError("property.not.writable", key, ScriptRuntime.safeToString(this)); } return; } - f.setValue(value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)); + f.setValue(value, isStrictFlag(callSiteFlags)); } else if (!isExtensible()) { - if (NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)) { + if (isStrictFlag(callSiteFlags)) { throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this)); } } else { @@ -3235,7 +3243,7 @@ if (isValidArrayIndex(index)) { final ArrayData data = getArray(); if (data.has(index)) { - setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); + setArray(data.set(index, value, isStrictFlag(callSiteFlags))); } else { doesNotHave(index, value, callSiteFlags); } @@ -3255,7 +3263,7 @@ if (isValidArrayIndex(index)) { final ArrayData data = getArray(); if (data.has(index)) { - setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); + setArray(data.set(index, value, isStrictFlag(callSiteFlags))); } else { doesNotHave(index, value, callSiteFlags); } @@ -3275,7 +3283,7 @@ if (isValidArrayIndex(index)) { final ArrayData data = getArray(); if (data.has(index)) { - setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); + setArray(data.set(index, value, isStrictFlag(callSiteFlags))); } else { doesNotHave(index, value, callSiteFlags); } @@ -3295,7 +3303,7 @@ if (isValidArrayIndex(index)) { final ArrayData data = getArray(); if (data.has(index)) { - setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); + setArray(data.set(index, value, isStrictFlag(callSiteFlags))); } else { doesNotHave(index, value, callSiteFlags); } @@ -3314,7 +3322,7 @@ if (isValidArrayIndex(index)) { final ArrayData data = getArray(); if (data.has(index)) { - setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); + setArray(data.set(index, value, isStrictFlag(callSiteFlags))); } else { doesNotHave(index, value, callSiteFlags); } @@ -3333,7 +3341,7 @@ if (isValidArrayIndex(index)) { final ArrayData data = getArray(); if (data.has(index)) { - setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); + setArray(data.set(index, value, isStrictFlag(callSiteFlags))); } else { doesNotHave(index, value, callSiteFlags); } @@ -3352,7 +3360,7 @@ if (isValidArrayIndex(index)) { final ArrayData data = getArray(); if (data.has(index)) { - setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); + setArray(data.set(index, value, isStrictFlag(callSiteFlags))); } else { doesNotHave(index, value, callSiteFlags); } @@ -3371,7 +3379,7 @@ if (isValidArrayIndex(index)) { final ArrayData data = getArray(); if (data.has(index)) { - setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); + setArray(data.set(index, value, isStrictFlag(callSiteFlags))); } else { doesNotHave(index, value, callSiteFlags); } @@ -3390,7 +3398,7 @@ if (isValidArrayIndex(index)) { final ArrayData data = getArray(); if (data.has(index)) { - setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); + setArray(data.set(index, value, isStrictFlag(callSiteFlags))); } else { doesNotHave(index, value, callSiteFlags); } @@ -3409,7 +3417,7 @@ if (isValidArrayIndex(index)) { final ArrayData data = getArray(); if (data.has(index)) { - setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); + setArray(data.set(index, value, isStrictFlag(callSiteFlags))); } else { doesNotHave(index, value, callSiteFlags); } @@ -3428,7 +3436,7 @@ if (isValidArrayIndex(index)) { final ArrayData data = getArray(); if (data.has(index)) { - setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); + setArray(data.set(index, value, isStrictFlag(callSiteFlags))); } else { doesNotHave(index, value, callSiteFlags); } @@ -3447,7 +3455,7 @@ if (isValidArrayIndex(index)) { final ArrayData data = getArray(); if (data.has(index)) { - setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); + setArray(data.set(index, value, isStrictFlag(callSiteFlags))); } else { doesNotHave(index, value, callSiteFlags); } @@ -3465,7 +3473,7 @@ if (isValidArrayIndex(index)) { if (getArray().has(index)) { final ArrayData data = getArray(); - setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); + setArray(data.set(index, value, isStrictFlag(callSiteFlags))); } else { doesNotHave(index, value, callSiteFlags); } @@ -3483,7 +3491,7 @@ if (isValidArrayIndex(index)) { final ArrayData data = getArray(); if (data.has(index)) { - setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); + setArray(data.set(index, value, isStrictFlag(callSiteFlags))); } else { doesNotHave(index, value, callSiteFlags); } @@ -3502,7 +3510,7 @@ if (isValidArrayIndex(index)) { final ArrayData data = getArray(); if (data.has(index)) { - setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); + setArray(data.set(index, value, isStrictFlag(callSiteFlags))); } else { doesNotHave(index, value, callSiteFlags); } @@ -3521,7 +3529,7 @@ if (isValidArrayIndex(index)) { final ArrayData data = getArray(); if (data.has(index)) { - setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); + setArray(data.set(index, value, isStrictFlag(callSiteFlags))); } else { doesNotHave(index, value, callSiteFlags); } @@ -3686,6 +3694,29 @@ } /** + * Return a shallow copy of this ScriptObject. + * @return a shallow copy. + */ + public final ScriptObject copy() { + try { + return clone(); + } catch (final CloneNotSupportedException e) { + throw new RuntimeException(e); + } + } + + @Override + protected ScriptObject clone() throws CloneNotSupportedException { + final ScriptObject clone = (ScriptObject) super.clone(); + if (objectSpill != null) { + clone.objectSpill = objectSpill.clone(); + clone.primitiveSpill = primitiveSpill.clone(); + } + clone.arrayData = arrayData.copy(); + return clone; + } + + /** * Make a new UserAccessorProperty property. getter and setter functions are stored in * this ScriptObject and slot values are used in property object. * diff -r c6c53c5adc51 -r 56f6161c3e55 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/ArrayData.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/ArrayData.java Thu Nov 27 17:14:01 2014 +0400 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/ArrayData.java Thu Nov 27 16:42:53 2014 +0100 @@ -61,9 +61,9 @@ /** * Length of the array data. Not necessarily length of the wrapped array. * This is private to ensure that no one in a subclass is able to touch the length - * without going through {@link setLength}. This is used to implement + * without going through {@link #setLength}. This is used to implement * {@link LengthNotWritableFilter}s, ensuring that there are no ways past - * a {@link setLength} function replaced by a nop + * a {@link #setLength} function replaced by a nop */ private long length; @@ -79,11 +79,7 @@ */ private static class UntouchedArrayData extends ContinuousArrayData { private UntouchedArrayData() { - this(0); - } - - private UntouchedArrayData(final int length) { - super(length); + super(0); } private ArrayData toRealArrayData() { @@ -100,7 +96,8 @@ @Override public ContinuousArrayData copy() { - return new UntouchedArrayData((int)length()); + assert length() == 0; + return this; } @Override @@ -246,7 +243,7 @@ public Class getBoxedElementType() { return Integer.class; } - }; + } /** * Constructor diff -r c6c53c5adc51 -r 56f6161c3e55 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/Messages.properties --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/Messages.properties Thu Nov 27 17:14:01 2014 +0400 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/Messages.properties Thu Nov 27 16:42:53 2014 +0100 @@ -116,6 +116,7 @@ type.error.cannot.convert.to.interface=object {0} cannot be converted to {1} due to "{2}" type.error.array.reduce.invalid.init=invalid initialValue for Array.prototype.reduce type.error.array.reduceright.invalid.init=invalid initialValue for Array.prototype.reduceRight +type.error.assign.constant=Assignment to constant "{0}" type.error.cannot.get.default.string=Cannot get default string value type.error.cannot.get.default.number=Cannot get default number value type.error.cant.apply.with.to.null=Cannot apply "with" to null @@ -166,6 +167,7 @@ syntax.error.strict.cant.delete=cannot delete "{0}" in strict mode syntax.error.redeclare.variable=Variable "{0}" has already been declared syntax.error.assign.constant=Assignment to constant "{0}" +syntax.error.unprotected.switch.declaration=Unsupported {0} declaration in unprotected switch statement io.error.cant.write=cannot write "{0}" config.error.no.dest=no destination directory supplied diff -r c6c53c5adc51 -r 56f6161c3e55 nashorn/test/script/basic/es6/for-let.js --- a/nashorn/test/script/basic/es6/for-let.js Thu Nov 27 17:14:01 2014 +0400 +++ b/nashorn/test/script/basic/es6/for-let.js Thu Nov 27 16:42:53 2014 +0100 @@ -39,3 +39,40 @@ } catch (e) { print(e); } + +let a = []; + +for (let i = 0; i < 10; i++) { + a.push(function() { print(i); }); +} + +a.forEach(function(f) { f(); }); + +a = []; + +for (let i = 0; i < 10; i++) { + if (i == 5) { + i = "foo"; + } + a.push(function() { print(i); }); +} + +a.forEach(function(f) { f(); }); + +try { + print(i); +} catch (e) { + print(e); +} + +a = []; + +for (let i = 0; i < 20; i++) { + if (i % 2 == 1) { + i += 2; + continue; + } + a.push(function() { print(i); }); +} + +a.forEach(function(f) { f(); }); diff -r c6c53c5adc51 -r 56f6161c3e55 nashorn/test/script/basic/es6/for-let.js.EXPECTED --- a/nashorn/test/script/basic/es6/for-let.js.EXPECTED Thu Nov 27 17:14:01 2014 +0400 +++ b/nashorn/test/script/basic/es6/for-let.js.EXPECTED Thu Nov 27 16:42:53 2014 +0100 @@ -9,3 +9,25 @@ 8 9 ReferenceError: "i" is not defined +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +0 +1 +2 +3 +4 +foo +ReferenceError: "i" is not defined +0 +4 +8 +12 +16 diff -r c6c53c5adc51 -r 56f6161c3e55 nashorn/test/script/basic/es6/let-const-statement-context.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/basic/es6/let-const-statement-context.js Thu Nov 27 16:42:53 2014 +0100 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8057980: let & const: remaining issues with lexical scoping + * + * @test + * @run + * @option --language=es6 + */ + +function tryEval(s) { + try { + eval(s); + } catch (e) { + print(String(e).replace(/\\/g, "/")); + } +} + +tryEval('if (true) let x = 1;'); +tryEval('if (true) const x = 1;'); +tryEval('while (true) let x = 1;'); +tryEval('while (true) const x = 1;'); +tryEval('for (;;) let x = 1;'); +tryEval('for (;;) const x = 1;'); +tryEval('do let x = 1; while (true);'); +tryEval('do const x = 1; while (true);'); +tryEval('with (y) const x = 1;'); +tryEval('with (y) let x = 1;'); diff -r c6c53c5adc51 -r 56f6161c3e55 nashorn/test/script/basic/es6/let-const-statement-context.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/basic/es6/let-const-statement-context.js.EXPECTED Thu Nov 27 16:42:53 2014 +0100 @@ -0,0 +1,30 @@ +SyntaxError: test/script/basic/es6/let-const-statement-context.js#34:8:1:10 Expected statement but found let declaration +if (true) let x = 1; + ^ +SyntaxError: test/script/basic/es6/let-const-statement-context.js#34:8:1:10 Expected statement but found const declaration +if (true) const x = 1; + ^ +SyntaxError: test/script/basic/es6/let-const-statement-context.js#34:8:1:13 Expected statement but found let declaration +while (true) let x = 1; + ^ +SyntaxError: test/script/basic/es6/let-const-statement-context.js#34:8:1:13 Expected statement but found const declaration +while (true) const x = 1; + ^ +SyntaxError: test/script/basic/es6/let-const-statement-context.js#34:8:1:9 Expected statement but found let declaration +for (;;) let x = 1; + ^ +SyntaxError: test/script/basic/es6/let-const-statement-context.js#34:8:1:9 Expected statement but found const declaration +for (;;) const x = 1; + ^ +SyntaxError: test/script/basic/es6/let-const-statement-context.js#34:8:1:3 Expected statement but found let declaration +do let x = 1; while (true); + ^ +SyntaxError: test/script/basic/es6/let-const-statement-context.js#34:8:1:3 Expected statement but found const declaration +do const x = 1; while (true); + ^ +SyntaxError: test/script/basic/es6/let-const-statement-context.js#34:8:1:9 Expected statement but found const declaration +with (y) const x = 1; + ^ +SyntaxError: test/script/basic/es6/let-const-statement-context.js#34:8:1:9 Expected statement but found let declaration +with (y) let x = 1; + ^ diff -r c6c53c5adc51 -r 56f6161c3e55 nashorn/test/script/basic/es6/let-const-switch.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/basic/es6/let-const-switch.js Thu Nov 27 16:42:53 2014 +0100 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8057980: let & const: remaining issues with lexical scoping + * + * @test + * @run + * @option --language=es6 + */ + +function tryEval(s) { + try { + eval(s); + } catch (e) { + print(String(e).replace(/\\/g, "/")); + } +} + +tryEval('var x = 0; switch (x) { case 0: { let x = 1; print(x); } case 1: { let x = 2; print(x); }} print(x);'); +tryEval('var x = 0; switch (x) { case 0: { const x = 1; print(x); } case 1: { const x = 2; print(x); }} print(x);'); + +// TODO: the following should not throw +tryEval('switch (x) { case 0: let x = 1; }'); +tryEval('switch (x) { case 0: const x = 1; }'); diff -r c6c53c5adc51 -r 56f6161c3e55 nashorn/test/script/basic/es6/let-const-switch.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/basic/es6/let-const-switch.js.EXPECTED Thu Nov 27 16:42:53 2014 +0100 @@ -0,0 +1,12 @@ +1 +2 +0 +1 +2 +0 +SyntaxError: test/script/basic/es6/let-const-switch.js#34:8:1:25 Unsupported let declaration in unprotected switch statement +switch (x) { case 0: let x = 1; } + ^ +SyntaxError: test/script/basic/es6/let-const-switch.js#34:8:1:27 Unsupported const declaration in unprotected switch statement +switch (x) { case 0: const x = 1; } + ^ diff -r c6c53c5adc51 -r 56f6161c3e55 nashorn/test/script/basic/es6/let-load.js --- a/nashorn/test/script/basic/es6/let-load.js Thu Nov 27 17:14:01 2014 +0400 +++ b/nashorn/test/script/basic/es6/let-load.js Thu Nov 27 16:42:53 2014 +0100 @@ -40,17 +40,8 @@ } print("imported var: " + a); -try { - print("imported let: " + b); -} catch (e) { - print(e); -} - -try { - print("imported const: " + c); -} catch (e) { - print(e); -} +print("imported let: " + b); +print("imported const: " + c); top(); @@ -60,4 +51,10 @@ print(e); } +try { + c = "foo"; +} catch (e) { + print(e); +} + diff -r c6c53c5adc51 -r 56f6161c3e55 nashorn/test/script/basic/es6/let-load.js.EXPECTED --- a/nashorn/test/script/basic/es6/let-load.js.EXPECTED Thu Nov 27 17:14:01 2014 +0400 +++ b/nashorn/test/script/basic/es6/let-load.js.EXPECTED Thu Nov 27 16:42:53 2014 +0100 @@ -6,3 +6,4 @@ imported const: 3 top level function ReferenceError: "block" is not defined +TypeError: Assignment to constant "c" diff -r c6c53c5adc51 -r 56f6161c3e55 nashorn/test/script/basic/es6/let_const_closure.js.EXPECTED --- a/nashorn/test/script/basic/es6/let_const_closure.js.EXPECTED Thu Nov 27 17:14:01 2014 +0400 +++ b/nashorn/test/script/basic/es6/let_const_closure.js.EXPECTED Thu Nov 27 16:42:53 2014 +0100 @@ -5,9 +5,9 @@ test test test -3 -3 -3 0 1 2 +0 +1 +2 diff -r c6c53c5adc51 -r 56f6161c3e55 nashorn/test/script/basic/es6/lexical-toplevel.js.EXPECTED --- a/nashorn/test/script/basic/es6/lexical-toplevel.js.EXPECTED Thu Nov 27 17:14:01 2014 +0400 +++ b/nashorn/test/script/basic/es6/lexical-toplevel.js.EXPECTED Thu Nov 27 16:42:53 2014 +0100 @@ -13,6 +13,7 @@ false true true +TypeError: Assignment to constant "CONST" VAR LETLET CONST @@ -28,3 +29,4 @@ false true true +TypeError: Assignment to constant "CONST"