Remove some initialization checks based on source positions.
This depends on http://codereview.chromium.org/8352039 and http://codereview.chromium.org/8423005 . Review URL: http://codereview.chromium.org/8422010 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@9870 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
58123bff56
commit
fd21937812
@ -1283,28 +1283,60 @@ void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) {
|
|||||||
Comment cmnt(masm_, var->IsContextSlot()
|
Comment cmnt(masm_, var->IsContextSlot()
|
||||||
? "Context variable"
|
? "Context variable"
|
||||||
: "Stack variable");
|
: "Stack variable");
|
||||||
if (!var->binding_needs_init()) {
|
if (var->binding_needs_init()) {
|
||||||
context()->Plug(var);
|
// var->scope() may be NULL when the proxy is located in eval code and
|
||||||
} else {
|
// refers to a potential outside binding. Currently those bindings are
|
||||||
// Let and const need a read barrier.
|
// always looked up dynamically, i.e. in that case
|
||||||
GetVar(r0, var);
|
// var->location() == LOOKUP.
|
||||||
__ CompareRoot(r0, Heap::kTheHoleValueRootIndex);
|
// always holds.
|
||||||
if (var->mode() == LET || var->mode() == CONST_HARMONY) {
|
ASSERT(var->scope() != NULL);
|
||||||
// Throw a reference error when using an uninitialized let/const
|
|
||||||
// binding in harmony mode.
|
// Check if the binding really needs an initialization check. The check
|
||||||
Label done;
|
// can be skipped in the following situation: we have a LET or CONST
|
||||||
__ b(ne, &done);
|
// binding in harmony mode, both the Variable and the VariableProxy have
|
||||||
__ mov(r0, Operand(var->name()));
|
// the same declaration scope (i.e. they are both in global code, in the
|
||||||
__ push(r0);
|
// same function or in the same eval code) and the VariableProxy is in
|
||||||
__ CallRuntime(Runtime::kThrowReferenceError, 1);
|
// the source physically located after the initializer of the variable.
|
||||||
__ bind(&done);
|
//
|
||||||
} else {
|
// We cannot skip any initialization checks for CONST in non-harmony
|
||||||
// Uninitalized const bindings outside of harmony mode are unholed.
|
// mode because const variables may be declared but never initialized:
|
||||||
ASSERT(var->mode() == CONST);
|
// if (false) { const x; }; var y = x;
|
||||||
__ LoadRoot(r0, Heap::kUndefinedValueRootIndex, eq);
|
//
|
||||||
|
// The condition on the declaration scopes is a conservative check for
|
||||||
|
// nested functions that access a binding and are called before the
|
||||||
|
// binding is initialized:
|
||||||
|
// function() { f(); let x = 1; function f() { x = 2; } }
|
||||||
|
//
|
||||||
|
// Check that we always have valid source position.
|
||||||
|
ASSERT(var->initializer_position() != RelocInfo::kNoPosition);
|
||||||
|
ASSERT(proxy->position() != RelocInfo::kNoPosition);
|
||||||
|
bool skip_init_check =
|
||||||
|
var->mode() != CONST &&
|
||||||
|
var->scope()->DeclarationScope() == scope()->DeclarationScope() &&
|
||||||
|
var->initializer_position() < proxy->position();
|
||||||
|
if (!skip_init_check) {
|
||||||
|
// Let and const need a read barrier.
|
||||||
|
GetVar(r0, var);
|
||||||
|
__ CompareRoot(r0, Heap::kTheHoleValueRootIndex);
|
||||||
|
if (var->mode() == LET || var->mode() == CONST_HARMONY) {
|
||||||
|
// Throw a reference error when using an uninitialized let/const
|
||||||
|
// binding in harmony mode.
|
||||||
|
Label done;
|
||||||
|
__ b(ne, &done);
|
||||||
|
__ mov(r0, Operand(var->name()));
|
||||||
|
__ push(r0);
|
||||||
|
__ CallRuntime(Runtime::kThrowReferenceError, 1);
|
||||||
|
__ bind(&done);
|
||||||
|
} else {
|
||||||
|
// Uninitalized const bindings outside of harmony mode are unholed.
|
||||||
|
ASSERT(var->mode() == CONST);
|
||||||
|
__ LoadRoot(r0, Heap::kUndefinedValueRootIndex, eq);
|
||||||
|
}
|
||||||
|
context()->Plug(r0);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
context()->Plug(r0);
|
|
||||||
}
|
}
|
||||||
|
context()->Plug(var);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1234,27 +1234,59 @@ void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) {
|
|||||||
Comment cmnt(masm_, var->IsContextSlot()
|
Comment cmnt(masm_, var->IsContextSlot()
|
||||||
? "Context variable"
|
? "Context variable"
|
||||||
: "Stack variable");
|
: "Stack variable");
|
||||||
if (!var->binding_needs_init()) {
|
if (var->binding_needs_init()) {
|
||||||
context()->Plug(var);
|
// var->scope() may be NULL when the proxy is located in eval code and
|
||||||
} else {
|
// refers to a potential outside binding. Currently those bindings are
|
||||||
// Let and const need a read barrier.
|
// always looked up dynamically, i.e. in that case
|
||||||
Label done;
|
// var->location() == LOOKUP.
|
||||||
GetVar(eax, var);
|
// always holds.
|
||||||
__ cmp(eax, isolate()->factory()->the_hole_value());
|
ASSERT(var->scope() != NULL);
|
||||||
__ j(not_equal, &done, Label::kNear);
|
|
||||||
if (var->mode() == LET || var->mode() == CONST_HARMONY) {
|
// Check if the binding really needs an initialization check. The check
|
||||||
// Throw a reference error when using an uninitialized let/const
|
// can be skipped in the following situation: we have a LET or CONST
|
||||||
// binding in harmony mode.
|
// binding in harmony mode, both the Variable and the VariableProxy have
|
||||||
__ push(Immediate(var->name()));
|
// the same declaration scope (i.e. they are both in global code, in the
|
||||||
__ CallRuntime(Runtime::kThrowReferenceError, 1);
|
// same function or in the same eval code) and the VariableProxy is in
|
||||||
} else {
|
// the source physically located after the initializer of the variable.
|
||||||
// Uninitalized const bindings outside of harmony mode are unholed.
|
//
|
||||||
ASSERT(var->mode() == CONST);
|
// We cannot skip any initialization checks for CONST in non-harmony
|
||||||
__ mov(eax, isolate()->factory()->undefined_value());
|
// mode because const variables may be declared but never initialized:
|
||||||
|
// if (false) { const x; }; var y = x;
|
||||||
|
//
|
||||||
|
// The condition on the declaration scopes is a conservative check for
|
||||||
|
// nested functions that access a binding and are called before the
|
||||||
|
// binding is initialized:
|
||||||
|
// function() { f(); let x = 1; function f() { x = 2; } }
|
||||||
|
//
|
||||||
|
// Check that we always have valid source position.
|
||||||
|
ASSERT(var->initializer_position() != RelocInfo::kNoPosition);
|
||||||
|
ASSERT(proxy->position() != RelocInfo::kNoPosition);
|
||||||
|
bool skip_init_check =
|
||||||
|
var->mode() != CONST &&
|
||||||
|
var->scope()->DeclarationScope() == scope()->DeclarationScope() &&
|
||||||
|
var->initializer_position() < proxy->position();
|
||||||
|
if (!skip_init_check) {
|
||||||
|
// Let and const need a read barrier.
|
||||||
|
Label done;
|
||||||
|
GetVar(eax, var);
|
||||||
|
__ cmp(eax, isolate()->factory()->the_hole_value());
|
||||||
|
__ j(not_equal, &done, Label::kNear);
|
||||||
|
if (var->mode() == LET || var->mode() == CONST_HARMONY) {
|
||||||
|
// Throw a reference error when using an uninitialized let/const
|
||||||
|
// binding in harmony mode.
|
||||||
|
__ push(Immediate(var->name()));
|
||||||
|
__ CallRuntime(Runtime::kThrowReferenceError, 1);
|
||||||
|
} else {
|
||||||
|
// Uninitalized const bindings outside of harmony mode are unholed.
|
||||||
|
ASSERT(var->mode() == CONST);
|
||||||
|
__ mov(eax, isolate()->factory()->undefined_value());
|
||||||
|
}
|
||||||
|
__ bind(&done);
|
||||||
|
context()->Plug(eax);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
__ bind(&done);
|
|
||||||
context()->Plug(eax);
|
|
||||||
}
|
}
|
||||||
|
context()->Plug(var);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1773,7 +1773,7 @@ Block* Parser::ParseVariableDeclarations(
|
|||||||
// For let/const declarations in harmony mode, we can also immediately
|
// For let/const declarations in harmony mode, we can also immediately
|
||||||
// pre-resolve the proxy because it resides in the same scope as the
|
// pre-resolve the proxy because it resides in the same scope as the
|
||||||
// declaration.
|
// declaration.
|
||||||
Declare(name, mode, NULL, mode != VAR, CHECK_OK);
|
VariableProxy* proxy = Declare(name, mode, NULL, mode != VAR, CHECK_OK);
|
||||||
nvars++;
|
nvars++;
|
||||||
if (declaration_scope->num_var_or_const() > kMaxNumFunctionLocals) {
|
if (declaration_scope->num_var_or_const() > kMaxNumFunctionLocals) {
|
||||||
ReportMessageAt(scanner().location(), "too_many_variables",
|
ReportMessageAt(scanner().location(), "too_many_variables",
|
||||||
@ -1828,6 +1828,11 @@ Block* Parser::ParseVariableDeclarations(
|
|||||||
if (decl_props != NULL) *decl_props = kHasInitializers;
|
if (decl_props != NULL) *decl_props = kHasInitializers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Record the end position of the initializer.
|
||||||
|
if (proxy->var() != NULL) {
|
||||||
|
proxy->var()->set_initializer_position(scanner().location().end_pos);
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure that 'const x' and 'let x' initialize 'x' to undefined.
|
// Make sure that 'const x' and 'let x' initialize 'x' to undefined.
|
||||||
if (value == NULL && needs_init) {
|
if (value == NULL && needs_init) {
|
||||||
value = GetLiteralUndefined();
|
value = GetLiteralUndefined();
|
||||||
|
@ -66,6 +66,7 @@ Variable::Variable(Scope* scope,
|
|||||||
kind_(kind),
|
kind_(kind),
|
||||||
location_(UNALLOCATED),
|
location_(UNALLOCATED),
|
||||||
index_(-1),
|
index_(-1),
|
||||||
|
initializer_position_(RelocInfo::kNoPosition),
|
||||||
local_if_not_shadowed_(NULL),
|
local_if_not_shadowed_(NULL),
|
||||||
is_valid_LHS_(is_valid_LHS),
|
is_valid_LHS_(is_valid_LHS),
|
||||||
is_accessed_from_inner_scope_(false),
|
is_accessed_from_inner_scope_(false),
|
||||||
|
@ -103,6 +103,9 @@ class Variable: public ZoneObject {
|
|||||||
bool is_used() { return is_used_; }
|
bool is_used() { return is_used_; }
|
||||||
void set_is_used(bool flag) { is_used_ = flag; }
|
void set_is_used(bool flag) { is_used_ = flag; }
|
||||||
|
|
||||||
|
int initializer_position() { return initializer_position_; }
|
||||||
|
void set_initializer_position(int pos) { initializer_position_ = pos; }
|
||||||
|
|
||||||
bool IsVariable(Handle<String> n) const {
|
bool IsVariable(Handle<String> n) const {
|
||||||
return !is_this() && name().is_identical_to(n);
|
return !is_this() && name().is_identical_to(n);
|
||||||
}
|
}
|
||||||
@ -163,6 +166,7 @@ class Variable: public ZoneObject {
|
|||||||
Kind kind_;
|
Kind kind_;
|
||||||
Location location_;
|
Location location_;
|
||||||
int index_;
|
int index_;
|
||||||
|
int initializer_position_;
|
||||||
|
|
||||||
// If this field is set, this variable references the stored locally bound
|
// If this field is set, this variable references the stored locally bound
|
||||||
// variable, but it might be shadowed by variable bindings introduced by
|
// variable, but it might be shadowed by variable bindings introduced by
|
||||||
|
@ -1240,27 +1240,59 @@ void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) {
|
|||||||
case Variable::LOCAL:
|
case Variable::LOCAL:
|
||||||
case Variable::CONTEXT: {
|
case Variable::CONTEXT: {
|
||||||
Comment cmnt(masm_, var->IsContextSlot() ? "Context slot" : "Stack slot");
|
Comment cmnt(masm_, var->IsContextSlot() ? "Context slot" : "Stack slot");
|
||||||
if (!var->binding_needs_init()) {
|
if (var->binding_needs_init()) {
|
||||||
context()->Plug(var);
|
// var->scope() may be NULL when the proxy is located in eval code and
|
||||||
} else {
|
// refers to a potential outside binding. Currently those bindings are
|
||||||
// Let and const need a read barrier.
|
// always looked up dynamically, i.e. in that case
|
||||||
Label done;
|
// var->location() == LOOKUP.
|
||||||
GetVar(rax, var);
|
// always holds.
|
||||||
__ CompareRoot(rax, Heap::kTheHoleValueRootIndex);
|
ASSERT(var->scope() != NULL);
|
||||||
__ j(not_equal, &done, Label::kNear);
|
|
||||||
if (var->mode() == LET || var->mode() == CONST_HARMONY) {
|
// Check if the binding really needs an initialization check. The check
|
||||||
// Throw a reference error when using an uninitialized let/const
|
// can be skipped in the following situation: we have a LET or CONST
|
||||||
// binding in harmony mode.
|
// binding in harmony mode, both the Variable and the VariableProxy have
|
||||||
__ Push(var->name());
|
// the same declaration scope (i.e. they are both in global code, in the
|
||||||
__ CallRuntime(Runtime::kThrowReferenceError, 1);
|
// same function or in the same eval code) and the VariableProxy is in
|
||||||
} else {
|
// the source physically located after the initializer of the variable.
|
||||||
// Uninitalized const bindings outside of harmony mode are unholed.
|
//
|
||||||
ASSERT(var->mode() == CONST);
|
// We cannot skip any initialization checks for CONST in non-harmony
|
||||||
__ LoadRoot(rax, Heap::kUndefinedValueRootIndex);
|
// mode because const variables may be declared but never initialized:
|
||||||
|
// if (false) { const x; }; var y = x;
|
||||||
|
//
|
||||||
|
// The condition on the declaration scopes is a conservative check for
|
||||||
|
// nested functions that access a binding and are called before the
|
||||||
|
// binding is initialized:
|
||||||
|
// function() { f(); let x = 1; function f() { x = 2; } }
|
||||||
|
//
|
||||||
|
// Check that we always have valid source position.
|
||||||
|
ASSERT(var->initializer_position() != RelocInfo::kNoPosition);
|
||||||
|
ASSERT(proxy->position() != RelocInfo::kNoPosition);
|
||||||
|
bool skip_init_check =
|
||||||
|
var->mode() != CONST &&
|
||||||
|
var->scope()->DeclarationScope() == scope()->DeclarationScope() &&
|
||||||
|
var->initializer_position() < proxy->position();
|
||||||
|
if (!skip_init_check) {
|
||||||
|
// Let and const need a read barrier.
|
||||||
|
Label done;
|
||||||
|
GetVar(rax, var);
|
||||||
|
__ CompareRoot(rax, Heap::kTheHoleValueRootIndex);
|
||||||
|
__ j(not_equal, &done, Label::kNear);
|
||||||
|
if (var->mode() == LET || var->mode() == CONST_HARMONY) {
|
||||||
|
// Throw a reference error when using an uninitialized let/const
|
||||||
|
// binding in harmony mode.
|
||||||
|
__ Push(var->name());
|
||||||
|
__ CallRuntime(Runtime::kThrowReferenceError, 1);
|
||||||
|
} else {
|
||||||
|
// Uninitalized const bindings outside of harmony mode are unholed.
|
||||||
|
ASSERT(var->mode() == CONST);
|
||||||
|
__ LoadRoot(rax, Heap::kUndefinedValueRootIndex);
|
||||||
|
}
|
||||||
|
__ bind(&done);
|
||||||
|
context()->Plug(rax);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
__ bind(&done);
|
|
||||||
context()->Plug(rax);
|
|
||||||
}
|
}
|
||||||
|
context()->Plug(var);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user