Statically check for assignments to const in harmony mode.
The ES.next draft rev 4 in section 11.13 reads: It is a Syntax Error if the AssignmentExpression is contained in extended code and the LeftHandSideExpression is an Identifier that does not statically resolve to a declarative environment record binding or if the resolved binding is an immutable binding. This CL adds corresponding static checks for the immutable binding case. TEST=mjsunit/harmony/block-const-assign Review URL: http://codereview.chromium.org/8688007 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10156 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
993d650f15
commit
08b4262512
@ -70,6 +70,7 @@ VariableProxy::VariableProxy(Isolate* isolate, Variable* var)
|
||||
var_(NULL), // Will be set by the call to BindTo.
|
||||
is_this_(var->is_this()),
|
||||
is_trivial_(false),
|
||||
is_lvalue_(false),
|
||||
position_(RelocInfo::kNoPosition) {
|
||||
BindTo(var);
|
||||
}
|
||||
@ -84,6 +85,7 @@ VariableProxy::VariableProxy(Isolate* isolate,
|
||||
var_(NULL),
|
||||
is_this_(is_this),
|
||||
is_trivial_(false),
|
||||
is_lvalue_(false),
|
||||
position_(position) {
|
||||
// Names must be canonicalized for fast equality checks.
|
||||
ASSERT(name->IsSymbol());
|
||||
|
@ -1159,12 +1159,17 @@ class VariableProxy: public Expression {
|
||||
|
||||
bool IsArguments() { return var_ != NULL && var_->is_arguments(); }
|
||||
|
||||
bool IsLValue() {
|
||||
return is_lvalue_;
|
||||
}
|
||||
|
||||
Handle<String> name() const { return name_; }
|
||||
Variable* var() const { return var_; }
|
||||
bool is_this() const { return is_this_; }
|
||||
int position() const { return position_; }
|
||||
|
||||
void MarkAsTrivial() { is_trivial_ = true; }
|
||||
void MarkAsLValue() { is_lvalue_ = true; }
|
||||
|
||||
// Bind this proxy to the variable var.
|
||||
void BindTo(Variable* var);
|
||||
@ -1174,6 +1179,9 @@ class VariableProxy: public Expression {
|
||||
Variable* var_; // resolved variable, or NULL
|
||||
bool is_this_;
|
||||
bool is_trivial_;
|
||||
// True if this variable proxy is being used in an assignment
|
||||
// or with a increment/decrement operator.
|
||||
bool is_lvalue_;
|
||||
int position_;
|
||||
};
|
||||
|
||||
|
@ -398,7 +398,7 @@ static Handle<SharedFunctionInfo> MakeFunctionInfo(CompilationInfo* info) {
|
||||
FunctionLiteral* lit = info->function();
|
||||
LiveEditFunctionTracker live_edit_tracker(isolate, lit);
|
||||
if (!MakeCode(info)) {
|
||||
isolate->StackOverflow();
|
||||
if (!isolate->has_pending_exception()) isolate->StackOverflow();
|
||||
return Handle<SharedFunctionInfo>::null();
|
||||
}
|
||||
|
||||
|
@ -246,6 +246,7 @@ function FormatMessage(message) {
|
||||
"unprotected_const", ["Illegal const declaration in unprotected statement context."],
|
||||
"cant_prevent_ext_external_array_elements", ["Cannot prevent extension of an object with external array elements"],
|
||||
"redef_external_array_element", ["Cannot redefine a property of an object with external array elements"],
|
||||
"harmony_const_assign", ["Assignment to constant variable."],
|
||||
];
|
||||
var messages = { __proto__ : null };
|
||||
for (var i = 0; i < messagesDictionary.length; i += 2) {
|
||||
|
@ -2693,6 +2693,7 @@ Expression* Parser::ParseAssignmentExpression(bool accept_IN, bool* ok) {
|
||||
// Assignment to eval or arguments is disallowed in strict mode.
|
||||
CheckStrictModeLValue(expression, "strict_lhs_assignment", CHECK_OK);
|
||||
}
|
||||
MarkAsLValue(expression);
|
||||
|
||||
Token::Value op = Next(); // Get assignment operator.
|
||||
int pos = scanner().location().beg_pos;
|
||||
@ -2926,6 +2927,7 @@ Expression* Parser::ParseUnaryExpression(bool* ok) {
|
||||
// Prefix expression operand in strict mode may not be eval or arguments.
|
||||
CheckStrictModeLValue(expression, "strict_lhs_prefix", CHECK_OK);
|
||||
}
|
||||
MarkAsLValue(expression);
|
||||
|
||||
int position = scanner().location().beg_pos;
|
||||
return new(zone()) CountOperation(isolate(),
|
||||
@ -2961,6 +2963,7 @@ Expression* Parser::ParsePostfixExpression(bool* ok) {
|
||||
// Postfix expression operand in strict mode may not be eval or arguments.
|
||||
CheckStrictModeLValue(expression, "strict_lhs_prefix", CHECK_OK);
|
||||
}
|
||||
MarkAsLValue(expression);
|
||||
|
||||
Token::Value next = Next();
|
||||
int position = scanner().location().beg_pos;
|
||||
@ -4479,6 +4482,15 @@ Handle<String> Parser::ParseIdentifierName(bool* ok) {
|
||||
}
|
||||
|
||||
|
||||
void Parser::MarkAsLValue(Expression* expression) {
|
||||
VariableProxy* proxy = expression != NULL
|
||||
? expression->AsVariableProxy()
|
||||
: NULL;
|
||||
|
||||
if (proxy != NULL) proxy->MarkAsLValue();
|
||||
}
|
||||
|
||||
|
||||
// Checks LHS expression for assignment and prefix/postfix increment/decrement
|
||||
// in strict mode.
|
||||
void Parser::CheckStrictModeLValue(Expression* expression,
|
||||
|
@ -661,6 +661,11 @@ class Parser {
|
||||
bool* is_set,
|
||||
bool* ok);
|
||||
|
||||
// Determine if the expression is a variable proxy and mark it as being used
|
||||
// in an assignment or with a increment/decrement operator. This is currently
|
||||
// used on for the statically checking assignments to harmony const bindings.
|
||||
void MarkAsLValue(Expression* expression);
|
||||
|
||||
// Strict mode validation of LValue expressions
|
||||
void CheckStrictModeLValue(Expression* expression,
|
||||
const char* error,
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
#include "bootstrapper.h"
|
||||
#include "compiler.h"
|
||||
#include "messages.h"
|
||||
#include "scopeinfo.h"
|
||||
|
||||
#include "allocation-inl.h"
|
||||
@ -284,8 +285,25 @@ bool Scope::Analyze(CompilationInfo* info) {
|
||||
}
|
||||
#endif
|
||||
|
||||
if (FLAG_harmony_scoping) {
|
||||
VariableProxy* proxy = scope->CheckAssignmentToConst();
|
||||
if (proxy != NULL) {
|
||||
// Found an assignment to const. Throw a syntax error.
|
||||
MessageLocation location(info->script(),
|
||||
proxy->position(),
|
||||
proxy->position());
|
||||
Isolate* isolate = info->isolate();
|
||||
Factory* factory = isolate->factory();
|
||||
Handle<JSArray> array = factory->NewJSArray(0);
|
||||
Handle<Object> result =
|
||||
factory->NewSyntaxError("harmony_const_assign", array);
|
||||
isolate->Throw(*result, &location);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
info->SetScope(scope);
|
||||
return true; // Can not fail.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -554,6 +572,29 @@ Declaration* Scope::CheckConflictingVarDeclarations() {
|
||||
}
|
||||
|
||||
|
||||
VariableProxy* Scope::CheckAssignmentToConst() {
|
||||
// Check this scope.
|
||||
if (is_extended_mode()) {
|
||||
for (int i = 0; i < unresolved_.length(); i++) {
|
||||
ASSERT(unresolved_[i]->var() != NULL);
|
||||
if (unresolved_[i]->var()->is_const_mode() &&
|
||||
unresolved_[i]->IsLValue()) {
|
||||
return unresolved_[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check inner scopes.
|
||||
for (int i = 0; i < inner_scopes_.length(); i++) {
|
||||
VariableProxy* proxy = inner_scopes_[i]->CheckAssignmentToConst();
|
||||
if (proxy != NULL) return proxy;
|
||||
}
|
||||
|
||||
// No assignments to const found.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void Scope::CollectStackAndContextLocals(ZoneList<Variable*>* stack_locals,
|
||||
ZoneList<Variable*>* context_locals) {
|
||||
ASSERT(stack_locals != NULL);
|
||||
|
@ -187,6 +187,11 @@ class Scope: public ZoneObject {
|
||||
// scope over a let binding of the same name.
|
||||
Declaration* CheckConflictingVarDeclarations();
|
||||
|
||||
// For harmony block scoping mode: Check if the scope has variable proxies
|
||||
// that are used as lvalues and point to const variables. Assumes that scopes
|
||||
// have been analyzed and variables been resolved.
|
||||
VariableProxy* CheckAssignmentToConst();
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Scope-specific info.
|
||||
|
||||
|
131
test/mjsunit/harmony/block-const-assign.js
Normal file
131
test/mjsunit/harmony/block-const-assign.js
Normal file
@ -0,0 +1,131 @@
|
||||
// Copyright 2011 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Flags: --harmony-scoping
|
||||
|
||||
// Test that we throw early syntax errors in harmony mode
|
||||
// when using an immutable binding in an assigment or with
|
||||
// prefix/postfix decrement/increment operators.
|
||||
// TODO(ES6): properly activate extended mode
|
||||
"use strict";
|
||||
|
||||
|
||||
// Function local const.
|
||||
function constDecl0(use) {
|
||||
return "(function() { const constvar = 1; " + use + "; });";
|
||||
}
|
||||
|
||||
|
||||
function constDecl1(use) {
|
||||
return "(function() { " + use + "; const constvar = 1; });";
|
||||
}
|
||||
|
||||
|
||||
// Function local const, assign from eval.
|
||||
function constDecl2(use) {
|
||||
use = "eval('(function() { " + use + " })')";
|
||||
return "(function() { const constvar = 1; " + use + "; })();";
|
||||
}
|
||||
|
||||
|
||||
function constDecl3(use) {
|
||||
use = "eval('(function() { " + use + " })')";
|
||||
return "(function() { " + use + "; const constvar = 1; })();";
|
||||
}
|
||||
|
||||
|
||||
// Block local const.
|
||||
function constDecl4(use) {
|
||||
return "(function() { { const constvar = 1; " + use + "; } });";
|
||||
}
|
||||
|
||||
|
||||
function constDecl5(use) {
|
||||
return "(function() { { " + use + "; const constvar = 1; } });";
|
||||
}
|
||||
|
||||
|
||||
// Block local const, assign from eval.
|
||||
function constDecl6(use) {
|
||||
use = "eval('(function() {" + use + "})')";
|
||||
return "(function() { { const constvar = 1; " + use + "; } })();";
|
||||
}
|
||||
|
||||
|
||||
function constDecl7(use) {
|
||||
use = "eval('(function() {" + use + "})')";
|
||||
return "(function() { { " + use + "; const constvar = 1; } })();";
|
||||
}
|
||||
|
||||
|
||||
// Function expression name.
|
||||
function constDecl8(use) {
|
||||
return "(function constvar() { " + use + "; });";
|
||||
}
|
||||
|
||||
|
||||
// Function expression name, assign from eval.
|
||||
function constDecl9(use) {
|
||||
use = "eval('(function(){" + use + "})')";
|
||||
return "(function constvar() { " + use + "; })();";
|
||||
}
|
||||
|
||||
let decls = [ constDecl0,
|
||||
constDecl1,
|
||||
constDecl2,
|
||||
constDecl3,
|
||||
constDecl4,
|
||||
constDecl5,
|
||||
constDecl6,
|
||||
constDecl7,
|
||||
constDecl8,
|
||||
constDecl9
|
||||
];
|
||||
let uses = [ 'constvar = 1;',
|
||||
'constvar += 1;',
|
||||
'++constvar;',
|
||||
'constvar++;'
|
||||
];
|
||||
|
||||
function Test(d,u) {
|
||||
'use strict';
|
||||
try {
|
||||
print(d(u));
|
||||
eval(d(u));
|
||||
} catch (e) {
|
||||
assertInstanceof(e, SyntaxError);
|
||||
assertTrue(e.toString().indexOf("Assignment to constant variable") >= 0);
|
||||
return;
|
||||
}
|
||||
assertUnreachable();
|
||||
}
|
||||
|
||||
for (var d = 0; d < decls.length; ++d) {
|
||||
for (var u = 0; u < uses.length; ++u) {
|
||||
Test(decls[d], uses[u]);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user