8057980: let & const: remaining issues with lexical scoping
Reviewed-by: lagergren, attila
--- 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<LexicalContext>(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);
--- 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);
--- 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;
}
--- 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;
+ }
}
--- 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();
--- 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();
}
--- 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.
*/
--- 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;
+ }
}
--- 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<VarNode> 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()) {
--- 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 {
--- 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 @@
* </ul>
*/
-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.
*
--- 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
--- 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
--- 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(); });
--- 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
--- /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;');
--- /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<eval>: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<eval>: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<eval>: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<eval>: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<eval>:1:9 Expected statement but found let declaration
+for (;;) let x = 1;
+ ^
+SyntaxError: test/script/basic/es6/let-const-statement-context.js#34:8<eval>:1:9 Expected statement but found const declaration
+for (;;) const x = 1;
+ ^
+SyntaxError: test/script/basic/es6/let-const-statement-context.js#34:8<eval>: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<eval>: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<eval>: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<eval>:1:9 Expected statement but found let declaration
+with (y) let x = 1;
+ ^
--- /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; }');
--- /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<eval>: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<eval>:1:27 Unsupported const declaration in unprotected switch statement
+switch (x) { case 0: const x = 1; }
+ ^
--- 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);
+}
+
--- 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"
--- 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
--- 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"