8073707: const re-assignment should not reported as a early error

Reviewed-by: sundar, attila
This commit is contained in:
Hannes Wallnöfer 2015-02-27 14:33:47 +01:00
parent ffd434caa1
commit 74c88b0515
7 changed files with 122 additions and 103 deletions

View File

@ -712,19 +712,8 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
return definingFn == function;
}
private void checkConstAssignment(final IdentNode ident) {
// Check for reassignment of constant
final Symbol symbol = ident.getSymbol();
if (symbol.isConst()) {
throwParserException(ECMAErrors.getMessage("syntax.error.assign.constant", symbol.getName()), ident);
}
}
@Override
public Node leaveBinaryNode(final BinaryNode binaryNode) {
if (binaryNode.isAssignment() && binaryNode.lhs() instanceof IdentNode) {
checkConstAssignment((IdentNode) binaryNode.lhs());
}
switch (binaryNode.tokenType()) {
case ASSIGN:
return leaveASSIGN(binaryNode);
@ -751,9 +740,6 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
@Override
public Node leaveUnaryNode(final UnaryNode unaryNode) {
if (unaryNode.isAssignment() && unaryNode.getExpression() instanceof IdentNode) {
checkConstAssignment((IdentNode) unaryNode.getExpression());
}
switch (unaryNode.tokenType()) {
case DELETE:
return leaveDELETE(unaryNode);

View File

@ -343,8 +343,14 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
// This is called the temporal dead zone (TDZ). See https://gist.github.com/rwaldron/f0807a758aa03bcdd58a
private void checkTemporalDeadZone(final IdentNode identNode) {
if (identNode.isDead()) {
method.load(identNode.getSymbol().getName());
method.invoke(ScriptRuntime.THROW_REFERENCE_ERROR);
method.load(identNode.getSymbol().getName()).invoke(ScriptRuntime.THROW_REFERENCE_ERROR);
}
}
// Runtime check for assignment to ES6 const
private void checkAssignTarget(final Expression expression) {
if (expression instanceof IdentNode && ((IdentNode)expression).getSymbol().isConst()) {
method.load(((IdentNode)expression).getSymbol().getName()).invoke(ScriptRuntime.THROW_CONST_TYPE_ERROR);
}
}
@ -787,72 +793,84 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
@Override
public boolean enterASSIGN(final BinaryNode binaryNode) {
checkAssignTarget(binaryNode.lhs());
loadASSIGN(binaryNode);
return false;
}
@Override
public boolean enterASSIGN_ADD(final BinaryNode binaryNode) {
checkAssignTarget(binaryNode.lhs());
loadASSIGN_ADD(binaryNode);
return false;
}
@Override
public boolean enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
checkAssignTarget(binaryNode.lhs());
loadASSIGN_BIT_AND(binaryNode);
return false;
}
@Override
public boolean enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
checkAssignTarget(binaryNode.lhs());
loadASSIGN_BIT_OR(binaryNode);
return false;
}
@Override
public boolean enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
checkAssignTarget(binaryNode.lhs());
loadASSIGN_BIT_XOR(binaryNode);
return false;
}
@Override
public boolean enterASSIGN_DIV(final BinaryNode binaryNode) {
checkAssignTarget(binaryNode.lhs());
loadASSIGN_DIV(binaryNode);
return false;
}
@Override
public boolean enterASSIGN_MOD(final BinaryNode binaryNode) {
checkAssignTarget(binaryNode.lhs());
loadASSIGN_MOD(binaryNode);
return false;
}
@Override
public boolean enterASSIGN_MUL(final BinaryNode binaryNode) {
checkAssignTarget(binaryNode.lhs());
loadASSIGN_MUL(binaryNode);
return false;
}
@Override
public boolean enterASSIGN_SAR(final BinaryNode binaryNode) {
checkAssignTarget(binaryNode.lhs());
loadASSIGN_SAR(binaryNode);
return false;
}
@Override
public boolean enterASSIGN_SHL(final BinaryNode binaryNode) {
checkAssignTarget(binaryNode.lhs());
loadASSIGN_SHL(binaryNode);
return false;
}
@Override
public boolean enterASSIGN_SHR(final BinaryNode binaryNode) {
checkAssignTarget(binaryNode.lhs());
loadASSIGN_SHR(binaryNode);
return false;
}
@Override
public boolean enterASSIGN_SUB(final BinaryNode binaryNode) {
checkAssignTarget(binaryNode.lhs());
loadASSIGN_SUB(binaryNode);
return false;
}
@ -1062,6 +1080,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
@Override
public boolean enterDECINC(final UnaryNode unaryNode) {
checkAssignTarget(unaryNode.getExpression());
loadDECINC(unaryNode);
return false;
}

View File

@ -114,6 +114,11 @@ public final class ScriptRuntime {
*/
public static final Call THROW_REFERENCE_ERROR = staticCall(MethodHandles.lookup(), ScriptRuntime.class, "throwReferenceError", void.class, String.class);
/**
* Throws a reference error for an undefined variable.
*/
public static final Call THROW_CONST_TYPE_ERROR = staticCall(MethodHandles.lookup(), ScriptRuntime.class, "throwConstTypeError", void.class, String.class);
/**
* Used to invalidate builtin names, e.g "Function" mapping to all properties in Function.prototype and Function.prototype itself.
*/
@ -402,6 +407,15 @@ public final class ScriptRuntime {
throw referenceError("not.defined", name);
}
/**
* Throws a type error for an assignment to a const.
*
* @param name the const name
*/
public static void throwConstTypeError(final String name) {
throw typeError("assign.constant", name);
}
/**
* Call a script function as a constructor with given args.
*

View File

@ -167,7 +167,6 @@ reference.error.cant.be.used.as.lhs="{0}" can not be used as the left-hand side
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}"

View File

@ -31,144 +31,147 @@
"use strict";
try {
eval('"use strict";\n' +
'const x = 2;\n' +
'x = 1;\n');
const x = 2;
x = 1;
fail("const assignment didn't throw");
} catch (e) {
print(e.name);
print(e);
}
try {
eval('"use strict";\n' +
'const x = 2;\n' +
'x++;\n');
const x = 2;
x++;
fail("const assignment didn't throw");
} catch (e) {
print(e.name);
print(e);
}
try {
eval('"use strict";\n' +
'const x = 2;\n' +
'x--;\n');
const x = 2;
x--;
fail("const assignment didn't throw");
} catch (e) {
print(e.name);
print(e);
}
try {
eval('"use strict";\n' +
'const x = 2;\n' +
'++x;\n');
const x = 2;
++x;
fail("const assignment didn't throw");
} catch (e) {
print(e.name);
print(e);
}
try {
eval('"use strict";\n' +
'const x = 2;\n' +
'--x;\n');
const x = 2;
--x;
fail("const assignment didn't throw");
} catch (e) {
print(e.name);
print(e);
}
try {
eval('"use strict";\n' +
'const x = 2;\n' +
'x += 1;\n');
const x = 2;
x += 1;
fail("const assignment didn't throw");
} catch (e) {
print(e.name);
print(e);
}
try {
eval('"use strict";\n' +
'const x = 2;\n' +
'x *= 1;\n');
const x = 2;
x *= 1;
fail("const assignment didn't throw");
} catch (e) {
print(e.name);
print(e);
}
try {
eval('"use strict";\n' +
'const x = 2;\n' +
'x /= 1;\n');
const x = 2;
x /= 1;
fail("const assignment didn't throw");
} catch (e) {
print(e.name);
print(e);
}
try {
eval('"use strict";\n' +
'const x = 2;\n' +
'x %= 1;\n');
const x = 2;
x %= 1;
fail("const assignment didn't throw");
} catch (e) {
print(e.name);
print(e);
}
try {
eval('"use strict";\n' +
'const x = 2;\n' +
'x |= 1;\n');
const x = 2;
x |= 1;
fail("const assignment didn't throw");
} catch (e) {
print(e.name);
print(e);
}
try {
eval('"use strict";\n' +
'const x = 2;\n' +
'x &= 1;\n');
const x = 2;
x &= 1;
fail("const assignment didn't throw");
} catch (e) {
print(e.name);
print(e);
}
try {
eval('"use strict";\n' +
'const x = 2;\n' +
'x ^= 1;\n');
const x = 2;
x ^= 1;
fail("const assignment didn't throw");
} catch (e) {
print(e.name);
print(e);
}
try {
eval('"use strict";\n' +
'const x = 2;\n' +
'x <<= 1;\n');
const x = 2;
x <<= 1;
fail("const assignment didn't throw");
} catch (e) {
print(e.name);
print(e);
}
try {
eval('"use strict";\n' +
'const x = 2;\n' +
'x >>= 1;\n');
const x = 2;
x >>= 1;
fail("const assignment didn't throw");
} catch (e) {
print(e.name);
print(e);
}
try {
eval('"use strict";\n' +
'const x = 2;\n' +
'x >>>= 1;\n');
const x = 2;
x >>>= 1;
fail("const assignment didn't throw");
} catch (e) {
print(e.name);
print(e);
}
try {
eval('"use strict";\n' +
'const x = 2;\n' +
'delete x;\n');
const x = 2;
delete x;
fail("const assignment didn't throw");
} catch (e) {
print(e.name);
print(e);
}
const c = 1;
try {
c = 2;
fail("const assignment didn't throw");
} catch (e) {
print(e);
}
(function() {
try {
c = 2;
fail("const assignment didn't throw");
} catch (e) {
print(e);
}
})();

View File

@ -1,16 +1,18 @@
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
SyntaxError
TypeError: Assignment to constant "x"
TypeError: Assignment to constant "x"
TypeError: Assignment to constant "x"
TypeError: Assignment to constant "x"
TypeError: Assignment to constant "x"
TypeError: Assignment to constant "x"
TypeError: Assignment to constant "x"
TypeError: Assignment to constant "x"
TypeError: Assignment to constant "x"
TypeError: Assignment to constant "x"
TypeError: Assignment to constant "x"
TypeError: Assignment to constant "x"
TypeError: Assignment to constant "x"
TypeError: Assignment to constant "x"
TypeError: Assignment to constant "x"
SyntaxError: cannot delete "x" in strict mode
TypeError: Assignment to constant "c"
TypeError: Assignment to constant "c"

View File

@ -1,8 +1,4 @@
ReferenceError: "a" is not defined
SyntaxError: test/script/basic/es6/let_const_reuse.js#35:9<eval>:3:8 Assignment to constant "a"
a--
^
SyntaxError: test/script/basic/es6/let_const_reuse.js#35:9<eval>:3:8 Assignment to constant "a"
a--
^
TypeError: Assignment to constant "a"
TypeError: Assignment to constant "a"
ReferenceError: "a" is not defined