8057980: let & const: remaining issues with lexical scoping

Reviewed-by: lagergren, attila
This commit is contained in:
Hannes Wallnöfer 2014-11-27 16:42:53 +01:00
parent a0485e336d
commit 7b35db48f7
23 changed files with 391 additions and 128 deletions

View File

@ -189,7 +189,7 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
* @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 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
@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 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
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);

View File

@ -3264,6 +3264,13 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
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);

View File

@ -525,7 +525,7 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
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;
}

View File

@ -45,14 +45,14 @@ public final class ForNode extends LoopNode {
/** 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 @@ public final class ForNode extends LoopNode {
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;
}
}

View File

@ -597,6 +597,20 @@ public class LexicalContext {
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();

View File

@ -177,4 +177,10 @@ public abstract class LoopNode extends BreakableStatement {
* @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();
}

View File

@ -45,19 +45,16 @@ public final class VarNode extends Statement implements Assignment<IdentNode> {
/** 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 @@ public final class VarNode extends Statement implements Assignment<IdentNode> {
* @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) {
@ -259,14 +256,6 @@ public final class VarNode extends Statement implements Assignment<IdentNode> {
return setFlags(flags | flag);
}
/**
* 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.

View File

@ -150,4 +150,9 @@ public final class WhileNode extends LoopNode {
}
return test == null;
}
@Override
public boolean hasPerIterationScope() {
return false;
}
}

View File

@ -554,7 +554,7 @@ loop:
// Set up new block. Captures first token.
final ParserContextBlockNode newBlock = newBlock();
try {
statement();
statement(false, false, true);
} finally {
restoreBlock(newBlock);
}
@ -770,7 +770,7 @@ loop:
try {
// Get the next element.
statement(true, allowPropertyFunction);
statement(true, allowPropertyFunction, false);
allowPropertyFunction = false;
// check for directive prologues
@ -860,13 +860,15 @@ loop:
* 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 @@ loop:
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 @@ loop:
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 @@ loop:
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 @@ loop:
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,22 +1323,23 @@ loop:
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 (outer != null) {
restoreBlock(outer);
appendStatement(new BlockStatement(startLine, new Block(
outer.getToken(),
body.getFinish(),
outer.getStatements())));
}
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 (outer != null) {
restoreBlock(outer);
appendStatement(new BlockStatement(startLine, new Block(
outer.getToken(),
body.getFinish(),
outer.getStatements())));
}
}
/**
@ -1364,9 +1372,10 @@ loop:
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 @@ loop:
}
} 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 @@ loop:
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 @@ loop:
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 @@ loop:
} 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 @@ loop:
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()) {

View File

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

View File

@ -46,6 +46,8 @@ import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_
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 @@ import jdk.nashorn.internal.runtime.linker.NashornGuards;
* </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 @@ public abstract class ScriptObject implements PropertyAccess {
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 @@ public abstract class ScriptObject implements PropertyAccess {
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 @@ public abstract class ScriptObject implements PropertyAccess {
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 @@ public abstract class ScriptObject implements PropertyAccess {
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 @@ public abstract class ScriptObject implements PropertyAccess {
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 @@ public abstract class ScriptObject implements PropertyAccess {
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 @@ public abstract class ScriptObject implements PropertyAccess {
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 @@ public abstract class ScriptObject implements PropertyAccess {
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 @@ public abstract class ScriptObject implements PropertyAccess {
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 @@ public abstract class ScriptObject implements PropertyAccess {
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 @@ public abstract class ScriptObject implements PropertyAccess {
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 @@ public abstract class ScriptObject implements PropertyAccess {
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 @@ public abstract class ScriptObject implements PropertyAccess {
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 @@ public abstract class ScriptObject implements PropertyAccess {
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 @@ public abstract class ScriptObject implements PropertyAccess {
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 @@ public abstract class ScriptObject implements PropertyAccess {
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 @@ public abstract class ScriptObject implements PropertyAccess {
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 @@ public abstract class ScriptObject implements PropertyAccess {
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 @@ public abstract class ScriptObject implements PropertyAccess {
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 @@ public abstract class ScriptObject implements PropertyAccess {
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 @@ public abstract class ScriptObject implements PropertyAccess {
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 @@ public abstract class ScriptObject implements PropertyAccess {
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 @@ public abstract class ScriptObject implements PropertyAccess {
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 @@ public abstract class ScriptObject implements PropertyAccess {
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);
}
@ -3685,6 +3693,29 @@ public abstract class ScriptObject implements PropertyAccess {
return true;
}
/**
* 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.

View File

@ -61,9 +61,9 @@ public abstract class ArrayData {
/**
* 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 @@ public abstract class ArrayData {
*/
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 @@ public abstract class ArrayData {
@Override
public ContinuousArrayData copy() {
return new UntouchedArrayData((int)length());
assert length() == 0;
return this;
}
@Override
@ -246,7 +243,7 @@ public abstract class ArrayData {
public Class<?> getBoxedElementType() {
return Integer.class;
}
};
}
/**
* Constructor

View File

@ -116,6 +116,7 @@ type.error.instanceof.on.non.object=instanceof must be called with a javascript
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.invalid.json=Invalid JSON: {0}
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

View File

@ -39,3 +39,40 @@ try {
} 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(); });

View File

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

View File

@ -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;');

View File

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

View File

@ -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; }');

View File

@ -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; }
^

View File

@ -40,17 +40,8 @@ load(__DIR__ + "let-load-lib.js");
}
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 @@ try {
print(e);
}
try {
c = "foo";
} catch (e) {
print(e);
}

View File

@ -6,3 +6,4 @@ imported let: 2
imported const: 3
top level function
ReferenceError: "block" is not defined
TypeError: Assignment to constant "c"

View File

@ -5,9 +5,9 @@
test
test
test
3
3
3
0
1
2
0
1
2

View File

@ -13,6 +13,7 @@ false
false
true
true
TypeError: Assignment to constant "CONST"
VAR
LETLET
CONST
@ -28,3 +29,4 @@ false
false
true
true
TypeError: Assignment to constant "CONST"