Commit f4a747b7 authored by Suraj Sharma's avatar Suraj Sharma Committed by Commit Bot

[parser] Skip TDZ Checks in more cases of let and const

The parser can now skip TDZ checks for cases when a reference is in,
or nested in, a scope that's both a sibling of the declaration and
created by a function expression.

Bug: v8:7331
Change-Id: Ia9748b5a8faa3037873efe5081837f5d0aa74115
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1542042
Commit-Queue: Suraj Sharma <surshar@microsoft.com>
Reviewed-by: 's avatarToon Verwaest <verwaest@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60853}
parent 02703a09
...@@ -116,8 +116,10 @@ DeclarationScope::DeclarationScope(Zone* zone, ...@@ -116,8 +116,10 @@ DeclarationScope::DeclarationScope(Zone* zone,
DeclarationScope::DeclarationScope(Zone* zone, Scope* outer_scope, DeclarationScope::DeclarationScope(Zone* zone, Scope* outer_scope,
ScopeType scope_type, ScopeType scope_type,
FunctionKind function_kind) FunctionKind function_kind,
NormalFunctionType type)
: Scope(zone, outer_scope, scope_type), : Scope(zone, outer_scope, scope_type),
is_function_expression_(type == NormalFunctionType::kExpression),
function_kind_(function_kind), function_kind_(function_kind),
params_(4, zone) { params_(4, zone) {
DCHECK_NE(scope_type, SCRIPT_SCOPE); DCHECK_NE(scope_type, SCRIPT_SCOPE);
...@@ -1949,12 +1951,13 @@ void UpdateNeedsHoleCheck(Variable* var, VariableProxy* proxy, Scope* scope) { ...@@ -1949,12 +1951,13 @@ void UpdateNeedsHoleCheck(Variable* var, VariableProxy* proxy, Scope* scope) {
} }
// Check if the binding really needs an initialization check. The check // Check if the binding really needs an initialization check. The check
// can be skipped in the following situation: we have a VariableMode::kLet or // can be skipped in the following situation:
// VariableMode::kConst binding, both the Variable and the VariableProxy have // 1. We have a VariableMode::kLet or VariableMode::kConst binding.
// the same declaration scope (i.e. they are both in global code, in the same // 2. The outermost closure scope inner to the scope in which the variable
// function or in the same eval code), the VariableProxy is in the source // is declared is not hoisted.
// physically located after the initializer of the variable, and that the // 3. The VariableProxy is in the source physically located after the
// initializer cannot be skipped due to a nonlinear scope. // initializer of the variable.
// 4. The initializer does not contain a nonlinear scope.
// //
// The condition on the closure scopes is a conservative check for // The condition on the closure scopes is a conservative check for
// nested functions that access a binding and are called before the // nested functions that access a binding and are called before the
...@@ -1966,8 +1969,26 @@ void UpdateNeedsHoleCheck(Variable* var, VariableProxy* proxy, Scope* scope) { ...@@ -1966,8 +1969,26 @@ void UpdateNeedsHoleCheck(Variable* var, VariableProxy* proxy, Scope* scope) {
// switch (1) { case 0: let x = 2; case 1: f(x); } // switch (1) { case 0: let x = 2; case 1: f(x); }
// The scope of the variable needs to be checked, in case the use is // The scope of the variable needs to be checked, in case the use is
// in a sub-block which may be linear. // in a sub-block which may be linear.
if (var->scope()->GetClosureScope() != scope->GetClosureScope()) { DeclarationScope* target = scope->GetClosureScope();
return SetNeedsHoleCheck(var, proxy); DeclarationScope* var_closure = var->scope()->GetClosureScope();
if (target != var_closure) {
// If the Variable doesn't have a valid source position the check
// cannot be skipped.
if (var->initializer_position() == kNoSourcePosition) {
return SetNeedsHoleCheck(var, proxy);
}
DeclarationScope* test_scope = target->outer_scope()->GetClosureScope();
while (test_scope != var_closure) {
target = test_scope;
test_scope = test_scope->outer_scope()->GetClosureScope();
}
// If the target scope is not hoisted Hole Check can be skipped.
if (target->is_hoisted_declaration_scope()) {
return SetNeedsHoleCheck(var, proxy);
}
} }
// We should always have valid source positions. // We should always have valid source positions.
......
...@@ -724,7 +724,8 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) { ...@@ -724,7 +724,8 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
class V8_EXPORT_PRIVATE DeclarationScope : public Scope { class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
public: public:
DeclarationScope(Zone* zone, Scope* outer_scope, ScopeType scope_type, DeclarationScope(Zone* zone, Scope* outer_scope, ScopeType scope_type,
FunctionKind function_kind = kNormalFunction); FunctionKind function_kind = kNormalFunction,
NormalFunctionType type = NormalFunctionType::kExpression);
DeclarationScope(Zone* zone, ScopeType scope_type, DeclarationScope(Zone* zone, ScopeType scope_type,
Handle<ScopeInfo> scope_info); Handle<ScopeInfo> scope_info);
// Creates a script scope. // Creates a script scope.
...@@ -736,6 +737,10 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope { ...@@ -736,6 +737,10 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
return is_function_scope() && IsArrowFunction(function_kind_); return is_function_scope() && IsArrowFunction(function_kind_);
} }
bool is_hoisted_declaration_scope() const {
return !is_function_expression_ && !is_arrow_scope() && !is_eval_scope();
}
// Inform the scope that the corresponding code uses "super". // Inform the scope that the corresponding code uses "super".
void RecordSuperPropertyUsage() { void RecordSuperPropertyUsage() {
DCHECK(IsConciseMethod(function_kind()) || DCHECK(IsConciseMethod(function_kind()) ||
...@@ -1075,6 +1080,7 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope { ...@@ -1075,6 +1080,7 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
bool has_checked_syntax_ : 1; bool has_checked_syntax_ : 1;
bool has_this_reference_ : 1; bool has_this_reference_ : 1;
bool has_this_declaration_ : 1; bool has_this_declaration_ : 1;
bool is_function_expression_ : 1;
// If the scope is a function scope, this is the function kind. // If the scope is a function scope, this is the function kind.
const FunctionKind function_kind_; const FunctionKind function_kind_;
......
...@@ -11,6 +11,8 @@ ...@@ -11,6 +11,8 @@
namespace v8 { namespace v8 {
namespace internal { namespace internal {
enum class NormalFunctionType { kDeclaration, kExpression };
enum FunctionKind : uint8_t { enum FunctionKind : uint8_t {
// BEGIN constructable functions // BEGIN constructable functions
kNormalFunction, kNormalFunction,
......
...@@ -680,12 +680,13 @@ class ParserBase { ...@@ -680,12 +680,13 @@ class ParserBase {
// Creates a function scope that always allocates in zone(). The function // Creates a function scope that always allocates in zone(). The function
// scope itself is either allocated in zone() or in target_zone if one is // scope itself is either allocated in zone() or in target_zone if one is
// passed in. // passed in.
DeclarationScope* NewFunctionScope(FunctionKind kind, DeclarationScope* NewFunctionScope(
Zone* parse_zone = nullptr) const { FunctionKind kind, Zone* parse_zone = nullptr,
NormalFunctionType type = NormalFunctionType::kDeclaration) const {
DCHECK(ast_value_factory()); DCHECK(ast_value_factory());
if (parse_zone == nullptr) parse_zone = zone(); if (parse_zone == nullptr) parse_zone = zone();
DeclarationScope* result = new (zone()) DeclarationScope* result = new (zone())
DeclarationScope(parse_zone, scope(), FUNCTION_SCOPE, kind); DeclarationScope(parse_zone, scope(), FUNCTION_SCOPE, kind, type);
// Record presence of an inner function scope // Record presence of an inner function scope
function_state_->RecordFunctionOrEvalCall(); function_state_->RecordFunctionOrEvalCall();
......
...@@ -2375,7 +2375,12 @@ FunctionLiteral* Parser::ParseFunctionLiteral( ...@@ -2375,7 +2375,12 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
// This Scope lives in the main zone. We'll migrate data into that zone later. // This Scope lives in the main zone. We'll migrate data into that zone later.
Zone* parse_zone = should_preparse ? &preparser_zone_ : zone(); Zone* parse_zone = should_preparse ? &preparser_zone_ : zone();
DeclarationScope* scope = NewFunctionScope(kind, parse_zone); NormalFunctionType type =
(function_type != FunctionLiteral::kDeclaration &&
function_type != FunctionLiteral::kAccessorOrMethod)
? NormalFunctionType::kExpression
: NormalFunctionType::kDeclaration;
DeclarationScope* scope = NewFunctionScope(kind, parse_zone, type);
SetLanguageMode(scope, language_mode); SetLanguageMode(scope, language_mode);
#ifdef DEBUG #ifdef DEBUG
scope->SetScopeName(function_name); scope->SetScopeName(function_name);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment