Commit 3f2b5017 authored by Toon Verwaest's avatar Toon Verwaest Committed by Commit Bot

[parser] Handle 'this' with a special ThisExpression rather than VariableProxy

"this" is a very common expression. By using a single ThisExpression object
we can both avoid allocating many unnecessary VariableProxies and specialize
the resolution of this since we know where it's declared up-front. This also
avoids having to special-case "this" reference handling in the paths that would
behave differently for "this" than for regular references; e.g., with-scopes.

The tricky pieces are due to DebugEvaluate and this/super() used as default
parameters of arrow functions. In the former case we replace the WITH_SCOPE
with FUNCTION_SCOPE so that we make sure that "this" is intercepted, and still
rely on regular dynamic variable lookup. Arrow functions are dealt with by
marking "this" use in ArrowHeadParsingScopes. If the parenthesized expression
ends up being an arrow function, we force context allocate on the outer scope
(and mark "has_this_reference" on the FUNCTION_SCOPE so DebugEvaluate in the
arrow function can expose "this").

The CL also removes the now unused ThisFunction AST node.

Change-Id: I0ca38ab92ff58c2f731e07db2fbe91df901681ef
Reviewed-on: https://chromium-review.googlesource.com/c/1448313Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Reviewed-by: 's avatarRoss McIlroy <rmcilroy@chromium.org>
Commit-Queue: Toon Verwaest <verwaest@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59393}
parent 66ddc07b
......@@ -468,7 +468,7 @@ void AstTraversalVisitor<Subclass>::VisitCompareOperation(
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitThisFunction(ThisFunction* expr) {
void AstTraversalVisitor<Subclass>::VisitThisExpression(ThisExpression* expr) {
PROCESS_EXPRESSION(expr);
}
......@@ -556,7 +556,6 @@ template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitSuperPropertyReference(
SuperPropertyReference* expr) {
PROCESS_EXPRESSION(expr);
RECURSE_EXPRESSION(VisitVariableProxy(expr->this_var()));
RECURSE_EXPRESSION(Visit(expr->home_object()));
}
......@@ -564,7 +563,6 @@ template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitSuperCallReference(
SuperCallReference* expr) {
PROCESS_EXPRESSION(expr);
RECURSE_EXPRESSION(VisitVariableProxy(expr->this_var()));
RECURSE_EXPRESSION(VisitVariableProxy(expr->new_target_var()));
RECURSE_EXPRESSION(VisitVariableProxy(expr->this_function_var()));
}
......
......@@ -175,8 +175,8 @@ VariableProxy::VariableProxy(Variable* var, int start_position)
: Expression(start_position, kVariableProxy),
raw_name_(var->raw_name()),
next_unresolved_(nullptr) {
bit_field_ |= IsThisField::encode(var->is_this()) |
IsAssignedField::encode(false) |
DCHECK(!var->is_this());
bit_field_ |= IsAssignedField::encode(false) |
IsResolvedField::encode(false) |
HoleCheckModeField::encode(HoleCheckMode::kElided);
BindTo(var);
......@@ -191,7 +191,7 @@ VariableProxy::VariableProxy(const VariableProxy* copy_from)
}
void VariableProxy::BindTo(Variable* var) {
DCHECK((is_this() && var->is_this()) || raw_name() == var->raw_name());
DCHECK_EQ(raw_name(), var->raw_name());
set_var(var);
set_is_resolved();
var->set_is_used();
......
......@@ -100,7 +100,7 @@ namespace internal {
V(SuperCallReference) \
V(SuperPropertyReference) \
V(TemplateLiteral) \
V(ThisFunction) \
V(ThisExpression) \
V(Throw) \
V(UnaryOperation) \
V(VariableProxy) \
......@@ -1516,11 +1516,15 @@ class ArrayLiteral final : public AggregateLiteral {
enum class HoleCheckMode { kRequired, kElided };
class ThisExpression final : public Expression {
private:
friend class AstNodeFactory;
ThisExpression() : Expression(kNoSourcePosition, kThisExpression) {}
};
class VariableProxy final : public Expression {
public:
bool IsValidReferenceExpression() const {
return !is_this() && !is_new_target();
}
bool IsValidReferenceExpression() const { return !is_new_target(); }
Handle<String> name() const { return raw_name()->string(); }
const AstRawString* raw_name() const {
......@@ -1541,8 +1545,6 @@ class VariableProxy final : public Expression {
return Scanner::Location(position(), position() + raw_name()->length());
}
bool is_this() const { return IsThisField::decode(bit_field_); }
bool is_assigned() const { return IsAssignedField::decode(bit_field_); }
void set_is_assigned() {
bit_field_ = IsAssignedField::update(bit_field_, true);
......@@ -1615,8 +1617,8 @@ class VariableProxy final : public Expression {
: Expression(start_position, kVariableProxy),
raw_name_(name),
next_unresolved_(nullptr) {
bit_field_ |= IsThisField::encode(variable_kind == THIS_VARIABLE) |
IsAssignedField::encode(false) |
DCHECK_NE(THIS_VARIABLE, variable_kind);
bit_field_ |= IsAssignedField::encode(false) |
IsResolvedField::encode(false) |
IsRemovedFromUnresolvedField::encode(false) |
HoleCheckModeField::encode(HoleCheckMode::kElided);
......@@ -1624,9 +1626,8 @@ class VariableProxy final : public Expression {
explicit VariableProxy(const VariableProxy* copy_from);
class IsThisField : public BitField<bool, Expression::kNextBitFieldIndex, 1> {
};
class IsAssignedField : public BitField<bool, IsThisField::kNext, 1> {};
class IsAssignedField
: public BitField<bool, Expression::kNextBitFieldIndex, 1> {};
class IsResolvedField : public BitField<bool, IsAssignedField::kNext, 1> {};
class IsRemovedFromUnresolvedField
: public BitField<bool, IsResolvedField::kNext, 1> {};
......@@ -2571,56 +2572,41 @@ class NativeFunctionLiteral final : public Expression {
};
class ThisFunction final : public Expression {
private:
friend class AstNodeFactory;
explicit ThisFunction(int pos) : Expression(pos, kThisFunction) {}
};
class SuperPropertyReference final : public Expression {
public:
VariableProxy* this_var() const { return this_var_; }
Expression* home_object() const { return home_object_; }
private:
friend class AstNodeFactory;
SuperPropertyReference(VariableProxy* this_var, Expression* home_object,
int pos)
: Expression(pos, kSuperPropertyReference),
this_var_(this_var),
home_object_(home_object) {
DCHECK(this_var->is_this());
// We take in ThisExpression* only as a proof that it was accessed.
SuperPropertyReference(Expression* home_object, int pos)
: Expression(pos, kSuperPropertyReference), home_object_(home_object) {
DCHECK(home_object->IsProperty());
}
VariableProxy* this_var_;
Expression* home_object_;
};
class SuperCallReference final : public Expression {
public:
VariableProxy* this_var() const { return this_var_; }
VariableProxy* new_target_var() const { return new_target_var_; }
VariableProxy* this_function_var() const { return this_function_var_; }
private:
friend class AstNodeFactory;
SuperCallReference(VariableProxy* this_var, VariableProxy* new_target_var,
// We take in ThisExpression* only as a proof that it was accessed.
SuperCallReference(VariableProxy* new_target_var,
VariableProxy* this_function_var, int pos)
: Expression(pos, kSuperCallReference),
this_var_(this_var),
new_target_var_(new_target_var),
this_function_var_(this_function_var) {
DCHECK(this_var->is_this());
DCHECK(new_target_var->raw_name()->IsOneByteEqualTo(".new.target"));
DCHECK(this_function_var->raw_name()->IsOneByteEqualTo(".this_function"));
}
VariableProxy* this_var_;
VariableProxy* new_target_var_;
VariableProxy* this_function_var_;
};
......@@ -2802,6 +2788,7 @@ class AstNodeFactory final {
: zone_(zone),
ast_value_factory_(ast_value_factory),
empty_statement_(new (zone) class EmptyStatement()),
this_expression_(new (zone) class ThisExpression()),
failure_expression_(new (zone) class FailureExpression()) {}
AstNodeFactory* ast_node_factory() { return this; }
......@@ -2956,6 +2943,10 @@ class AstNodeFactory final {
return empty_statement_;
}
class ThisExpression* ThisExpression() {
return this_expression_;
}
class FailureExpression* FailureExpression() {
return failure_expression_;
}
......@@ -3266,22 +3257,16 @@ class AstNodeFactory final {
return new (zone_) DoExpression(block, result, pos);
}
ThisFunction* NewThisFunction(int pos) {
return new (zone_) ThisFunction(pos);
}
SuperPropertyReference* NewSuperPropertyReference(VariableProxy* this_var,
Expression* home_object,
SuperPropertyReference* NewSuperPropertyReference(Expression* home_object,
int pos) {
return new (zone_) SuperPropertyReference(this_var, home_object, pos);
return new (zone_) SuperPropertyReference(home_object, pos);
}
SuperCallReference* NewSuperCallReference(VariableProxy* this_var,
VariableProxy* new_target_var,
SuperCallReference* NewSuperCallReference(VariableProxy* new_target_var,
VariableProxy* this_function_var,
int pos) {
return new (zone_)
SuperCallReference(this_var, new_target_var, this_function_var, pos);
SuperCallReference(new_target_var, this_function_var, pos);
}
EmptyParentheses* NewEmptyParentheses(int pos) {
......@@ -3319,6 +3304,7 @@ class AstNodeFactory final {
Zone* zone_;
AstValueFactory* ast_value_factory_;
class EmptyStatement* empty_statement_;
class ThisExpression* this_expression_;
class FailureExpression* failure_expression_;
};
......
......@@ -500,8 +500,7 @@ void CallPrinter::VisitImportCallExpression(ImportCallExpression* node) {
Print(")");
}
void CallPrinter::VisitThisFunction(ThisFunction* node) {}
void CallPrinter::VisitThisExpression(ThisExpression* node) { Print("this"); }
void CallPrinter::VisitSuperPropertyReference(SuperPropertyReference* node) {}
......@@ -1387,11 +1386,10 @@ void AstPrinter::VisitImportCallExpression(ImportCallExpression* node) {
Visit(node->argument());
}
void AstPrinter::VisitThisFunction(ThisFunction* node) {
IndentedScope indent(this, "THIS-FUNCTION", node->position());
void AstPrinter::VisitThisExpression(ThisExpression* node) {
IndentedScope indent(this, "THIS-EXPRESSION", node->position());
}
void AstPrinter::VisitSuperPropertyReference(SuperPropertyReference* node) {
IndentedScope indent(this, "SUPER-PROPERTY-REFERENCE", node->position());
}
......
......@@ -110,10 +110,8 @@ DeclarationScope::DeclarationScope(Zone* zone,
: Scope(zone), function_kind_(kNormalFunction), params_(4, zone) {
DCHECK_EQ(scope_type_, SCRIPT_SCOPE);
SetDefaults();
// Make sure that if we don't find the global 'this', it won't be declared as
// a regular dynamic global by predeclaring it with the right variable kind.
DeclareDynamicGlobal(ast_value_factory->this_string(), THIS_VARIABLE, this);
receiver_ = DeclareDynamicGlobal(ast_value_factory->this_string(),
THIS_VARIABLE, this);
}
DeclarationScope::DeclarationScope(Zone* zone, Scope* outer_scope,
......@@ -241,6 +239,9 @@ void DeclarationScope::SetDefaults() {
has_arguments_parameter_ = false;
scope_uses_super_property_ = false;
has_checked_syntax_ = false;
has_this_reference_ = false;
has_this_declaration_ =
(is_function_scope() && !is_arrow_scope()) || is_module_scope();
has_rest_ = false;
receiver_ = nullptr;
new_target_ = nullptr;
......@@ -333,15 +334,16 @@ Scope* Scope::DeserializeScopeChain(Isolate* isolate, Zone* zone,
Scope* outer_scope = nullptr;
while (!scope_info.is_null()) {
if (scope_info->scope_type() == WITH_SCOPE) {
// For scope analysis, debug-evaluate is equivalent to a with scope.
outer_scope =
new (zone) Scope(zone, WITH_SCOPE, handle(scope_info, isolate));
// TODO(yangguo): Remove once debug-evaluate properly keeps track of the
// function scope in which we are evaluating.
if (scope_info->IsDebugEvaluateScope()) {
outer_scope = new (zone)
DeclarationScope(zone, FUNCTION_SCOPE, handle(scope_info, isolate));
outer_scope->set_is_debug_evaluate_scope();
} else {
// For scope analysis, debug-evaluate is equivalent to a with scope.
outer_scope =
new (zone) Scope(zone, WITH_SCOPE, handle(scope_info, isolate));
}
} else if (scope_info->scope_type() == SCRIPT_SCOPE) {
// If we reach a script scope, it's the outermost scope. Install the
// scope info of this script context onto the existing script scope to
......@@ -571,20 +573,16 @@ bool DeclarationScope::Analyze(ParseInfo* info) {
}
void DeclarationScope::DeclareThis(AstValueFactory* ast_value_factory) {
DCHECK(!already_resolved_);
DCHECK(is_declaration_scope());
DCHECK(has_this_declaration());
bool derived_constructor = IsDerivedConstructor(function_kind_);
bool was_added;
Variable* var =
Declare(zone(), ast_value_factory->this_string(),
derived_constructor ? VariableMode::kConst : VariableMode::kVar,
THIS_VARIABLE,
derived_constructor ? kNeedsInitialization : kCreatedInitialized,
kNotAssigned, &was_added);
DCHECK(was_added);
receiver_ = var;
receiver_ = new (zone())
Variable(this, ast_value_factory->this_string(),
derived_constructor ? VariableMode::kConst : VariableMode::kVar,
THIS_VARIABLE,
derived_constructor ? kNeedsInitialization : kCreatedInitialized,
kNotAssigned);
}
void DeclarationScope::DeclareArguments(AstValueFactory* ast_value_factory) {
......@@ -819,18 +817,14 @@ Variable* Scope::LookupInScopeInfo(const AstRawString* name, Scope* cache) {
return cache->variables_.Lookup(name);
}
VariableKind kind = NORMAL_VARIABLE;
if (location == VariableLocation::CONTEXT &&
index == scope_info_->ReceiverContextSlotIndex()) {
kind = THIS_VARIABLE;
if (!is_module_scope()) {
DCHECK_NE(index, scope_info_->ReceiverContextSlotIndex());
}
// TODO(marja, rossberg): Correctly declare FUNCTION, CLASS, NEW_TARGET, and
// ARGUMENTS bindings as their corresponding VariableKind.
bool was_added;
Variable* var =
cache->variables_.Declare(zone(), this, name, mode, kind, init_flag,
maybe_assigned_flag, &was_added);
cache->variables_.Declare(zone(), this, name, mode, NORMAL_VARIABLE,
init_flag, maybe_assigned_flag, &was_added);
DCHECK(was_added);
var->AllocateTo(location, index);
return var;
......@@ -1152,6 +1146,21 @@ const AstRawString* Scope::FindVariableDeclaredIn(Scope* scope,
return nullptr;
}
void DeclarationScope::DeserializeReceiver(AstValueFactory* ast_value_factory) {
if (is_script_scope()) {
DCHECK_NOT_NULL(receiver_);
return;
}
DCHECK(has_this_declaration());
DeclareThis(ast_value_factory);
if (is_debug_evaluate_scope()) {
receiver_->AllocateTo(VariableLocation::LOOKUP, -1);
} else {
receiver_->AllocateTo(VariableLocation::CONTEXT,
scope_info_->ReceiverContextSlotIndex());
}
}
bool DeclarationScope::AllocateVariables(ParseInfo* info) {
// Module variables must be allocated before variable resolution
// to ensure that UpdateNeedsHoleCheck() can detect import variables.
......@@ -1168,6 +1177,21 @@ bool DeclarationScope::AllocateVariables(ParseInfo* info) {
return true;
}
bool Scope::HasThisReference() const {
if (is_declaration_scope() && AsDeclarationScope()->has_this_reference()) {
return true;
}
for (Scope* scope = inner_scope_; scope != nullptr; scope = scope->sibling_) {
if (!scope->is_declaration_scope() ||
!scope->AsDeclarationScope()->has_this_declaration()) {
if (scope->HasThisReference()) return true;
}
}
return false;
}
bool Scope::AllowsLazyParsingWithoutUnresolvedVariables(
const Scope* outer) const {
// If none of the outer scopes need to decide whether to context allocate
......@@ -1260,9 +1284,9 @@ bool Scope::ShouldBanArguments() {
DeclarationScope* Scope::GetReceiverScope() {
Scope* scope = this;
while (!scope->is_script_scope() &&
(!scope->is_function_scope() ||
scope->AsDeclarationScope()->is_arrow_scope())) {
while (!scope->is_declaration_scope() ||
(!scope->is_script_scope() &&
!scope->AsDeclarationScope()->has_this_declaration())) {
scope = scope->outer_scope();
}
return scope->AsDeclarationScope();
......@@ -1805,16 +1829,6 @@ template Variable* Scope::Lookup<Scope::kDeserializedScope>(
VariableProxy* proxy, Scope* scope, Scope* outer_scope_end,
Scope* entry_point, bool force_context_allocation);
namespace {
bool CanBeShadowed(Scope* scope, Variable* var) {
if (var == nullptr) return false;
// "this" can't be shadowed by "eval"-introduced bindings or by "with" scopes.
// TODO(wingo): There are other variables in this category; add them.
return !var->is_this();
}
}; // namespace
Variable* Scope::LookupWith(VariableProxy* proxy, Scope* scope,
Scope* outer_scope_end, Scope* entry_point,
bool force_context_allocation) {
......@@ -1827,7 +1841,7 @@ Variable* Scope::LookupWith(VariableProxy* proxy, Scope* scope,
: Lookup<kDeserializedScope>(proxy, scope->outer_scope_,
outer_scope_end, entry_point);
if (!CanBeShadowed(scope, var)) return var;
if (var == nullptr) return var;
// The current scope is a with scope, so the variable binding can not be
// statically resolved. However, note that it was necessary to do a lookup
......@@ -1861,7 +1875,7 @@ Variable* Scope::LookupSloppyEval(VariableProxy* proxy, Scope* scope,
nullptr, force_context_allocation)
: Lookup<kDeserializedScope>(proxy, scope->outer_scope_,
outer_scope_end, entry);
if (!CanBeShadowed(scope, var)) return var;
if (var == nullptr) return var;
// A variable binding may have been found in an outer scope, but the current
// scope makes a sloppy 'eval' call, so the found variable may not be the
......@@ -1951,12 +1965,6 @@ void UpdateNeedsHoleCheck(Variable* var, VariableProxy* proxy, Scope* scope) {
return SetNeedsHoleCheck(var, proxy);
}
if (var->is_this()) {
DCHECK(IsDerivedConstructor(scope->GetClosureScope()->function_kind()));
// TODO(littledan): implement 'this' hole check elimination.
return SetNeedsHoleCheck(var, proxy);
}
// We should always have valid source positions.
DCHECK_NE(var->initializer_position(), kNoSourcePosition);
DCHECK_NE(proxy->position(), kNoSourcePosition);
......@@ -2039,7 +2047,7 @@ bool Scope::MustAllocate(Variable* var) {
// Give var a read/write use if there is a chance it might be accessed
// via an eval() call. This is only possible if the variable has a
// visible name.
if ((var->is_this() || !var->raw_name()->IsEmpty()) &&
if (!var->raw_name()->IsEmpty() &&
(inner_scope_calls_eval_ || is_catch_scope() || is_script_scope())) {
var->set_is_used();
if (inner_scope_calls_eval_) var->set_maybe_assigned();
......
......@@ -474,6 +474,8 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
// Find the innermost outer scope that needs a context.
Scope* GetOuterScopeWithContext();
bool HasThisReference() const;
// Analyze() must have been called once to create the ScopeInfo.
Handle<ScopeInfo> scope_info() const {
DCHECK(!scope_info_.is_null());
......@@ -754,6 +756,8 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
return var;
}
void DeserializeReceiver(AstValueFactory* ast_value_factory);
#ifdef DEBUG
void set_is_being_lazily_parsed(bool is_being_lazily_parsed) {
is_being_lazily_parsed_ = is_being_lazily_parsed;
......@@ -837,17 +841,12 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
// The variable corresponding to the 'this' value.
Variable* receiver() {
DCHECK(has_this_declaration());
DCHECK(has_this_declaration() || is_script_scope());
DCHECK_NOT_NULL(receiver_);
return receiver_;
}
// TODO(wingo): Add a GLOBAL_SCOPE scope type which will lexically allocate
// "this" (and no other variable) on the native context. Script scopes then
// will not have a "this" declaration.
bool has_this_declaration() const {
return (is_function_scope() && !is_arrow_scope()) || is_module_scope();
}
bool has_this_declaration() const { return has_this_declaration_; }
// The variable corresponding to the 'new.target' value.
Variable* new_target_var() { return new_target_; }
......@@ -1019,6 +1018,13 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
return preparse_data_builder_;
}
void set_has_this_reference() { has_this_reference_ = true; }
bool has_this_reference() const { return has_this_reference_; }
void UsesThis() {
set_has_this_reference();
GetReceiverScope()->receiver()->ForceContextAllocation();
}
private:
V8_INLINE void AllocateParameter(Variable* var, int index);
......@@ -1055,12 +1061,14 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
bool is_skipped_function_ : 1;
bool has_inferred_function_name_ : 1;
bool has_checked_syntax_ : 1;
int num_parameters_ = 0;
bool has_this_reference_ : 1;
bool has_this_declaration_ : 1;
// If the scope is a function scope, this is the function kind.
const FunctionKind function_kind_;
int num_parameters_ = 0;
// Parameter list in source order.
ZonePtrList<Variable> params_;
// Map of function names to lists of functions defined in sloppy blocks
......
......@@ -59,7 +59,7 @@ class Variable final : public ZoneObject {
return ForceContextAllocationField::decode(bit_field_);
}
void ForceContextAllocation() {
DCHECK(IsUnallocated() || IsContextSlot() ||
DCHECK(IsUnallocated() || IsContextSlot() || IsLookupSlot() ||
location() == VariableLocation::MODULE);
bit_field_ = ForceContextAllocationField::update(bit_field_, true);
}
......
......@@ -155,6 +155,11 @@ void ScopeIterator::TryParseAndRetrieveScopes(ScopeIterator::Option option) {
DCHECK(non_locals_.is_null());
non_locals_ = info_->literal()->scope()->CollectNonLocals(
isolate_, info_, StringSet::New(isolate_));
if (!closure_scope_->has_this_declaration() &&
closure_scope_->HasThisReference()) {
non_locals_ = StringSet::Add(isolate_, non_locals_,
isolate_->factory()->this_string());
}
}
CHECK(DeclarationScope::Analyze(info_));
......@@ -304,7 +309,8 @@ ScopeIterator::ScopeType ScopeIterator::Type() const {
switch (current_scope_->scope_type()) {
case FUNCTION_SCOPE:
DCHECK_IMPLIES(current_scope_->NeedsContext(),
context_->IsFunctionContext());
context_->IsFunctionContext() ||
context_->IsDebugEvaluateContext());
return ScopeTypeLocal;
case MODULE_SCOPE:
DCHECK_IMPLIES(current_scope_->NeedsContext(),
......@@ -316,9 +322,8 @@ ScopeIterator::ScopeType ScopeIterator::Type() const {
context_->IsScriptContext() || context_->IsNativeContext());
return ScopeTypeScript;
case WITH_SCOPE:
DCHECK_IMPLIES(
current_scope_->NeedsContext(),
context_->IsWithContext() || context_->IsDebugEvaluateContext());
DCHECK_IMPLIES(current_scope_->NeedsContext(),
context_->IsWithContext());
return ScopeTypeWith;
case CATCH_SCOPE:
DCHECK(context_->IsCatchContext());
......@@ -340,7 +345,8 @@ ScopeIterator::ScopeType ScopeIterator::Type() const {
// fake it.
return seen_script_scope_ ? ScopeTypeGlobal : ScopeTypeScript;
}
if (context_->IsFunctionContext() || context_->IsEvalContext()) {
if (context_->IsFunctionContext() || context_->IsEvalContext() ||
context_->IsDebugEvaluateContext()) {
return ScopeTypeClosure;
}
if (context_->IsCatchContext()) {
......@@ -355,7 +361,7 @@ ScopeIterator::ScopeType ScopeIterator::Type() const {
if (context_->IsScriptContext()) {
return ScopeTypeScript;
}
DCHECK(context_->IsWithContext() || context_->IsDebugEvaluateContext());
DCHECK(context_->IsWithContext());
return ScopeTypeWith;
}
......@@ -605,15 +611,20 @@ bool ScopeIterator::VisitContextLocals(const Visitor& visitor,
}
bool ScopeIterator::VisitLocals(const Visitor& visitor, Mode mode) const {
for (Variable* var : *current_scope_->locals()) {
if (var->is_this()) {
// Only collect "this" for DebugEvaluate. The debugger will manually add
// "this" in a different way, and if we'd add it here as well, it shows up
// twice.
if (mode == Mode::ALL) continue;
} else if (ScopeInfo::VariableIsSynthetic(*var->name())) {
continue;
if (mode == Mode::STACK && current_scope_->is_declaration_scope() &&
current_scope_->AsDeclarationScope()->has_this_declaration()) {
Handle<Object> receiver = frame_inspector_ == nullptr
? handle(generator_->receiver(), isolate_)
: frame_inspector_->GetReceiver();
if (receiver->IsOptimizedOut(isolate_) || receiver->IsTheHole(isolate_)) {
receiver = isolate_->factory()->undefined_value();
}
if (visitor(isolate_->factory()->this_string(), receiver)) return true;
}
for (Variable* var : *current_scope_->locals()) {
DCHECK(!var->is_this());
if (ScopeInfo::VariableIsSynthetic(*var->name())) continue;
int index = var->index();
Handle<Object> value;
......@@ -623,31 +634,21 @@ bool ScopeIterator::VisitLocals(const Visitor& visitor, Mode mode) const {
break;
case VariableLocation::UNALLOCATED:
if (!var->is_this()) continue;
// No idea why this diverges...
value = frame_inspector_->GetReceiver();
break;
continue;
case VariableLocation::PARAMETER: {
if (frame_inspector_ == nullptr) {
// Get the variable from the suspended generator.
DCHECK(!generator_.is_null());
if (var->is_this()) {
value = handle(generator_->receiver(), isolate_);
} else {
FixedArray parameters_and_registers =
generator_->parameters_and_registers();
DCHECK_LT(index, parameters_and_registers->length());
value = handle(parameters_and_registers->get(index), isolate_);
}
FixedArray parameters_and_registers =
generator_->parameters_and_registers();
DCHECK_LT(index, parameters_and_registers->length());
value = handle(parameters_and_registers->get(index), isolate_);
} else {
value = var->is_this() ? frame_inspector_->GetReceiver()
: frame_inspector_->GetParameter(index);
value = frame_inspector_->GetParameter(index);
if (value->IsOptimizedOut(isolate_)) {
value = isolate_->factory()->undefined_value();
} else if (var->is_this() && value->IsTheHole(isolate_)) {
value = isolate_->factory()->undefined_value();
}
}
break;
......@@ -727,7 +728,7 @@ void ScopeIterator::VisitLocalScope(const Visitor& visitor, Mode mode) const {
// but don't force |this| to be context-allocated. Otherwise we'd find the
// wrong |this| value.
if (!closure_scope_->has_this_declaration() &&
!non_locals_->Has(isolate_, isolate_->factory()->this_string())) {
!closure_scope_->HasThisReference()) {
if (visitor(isolate_->factory()->this_string(),
isolate_->factory()->undefined_value()))
return;
......
......@@ -3108,7 +3108,8 @@ BytecodeGenerator::AssignmentLhsData BytecodeGenerator::PrepareAssignmentLhs(
register_allocator()->NewRegisterList(4);
SuperPropertyReference* super_property =
property->obj()->AsSuperPropertyReference();
VisitForRegisterValue(super_property->this_var(), super_property_args[0]);
BuildThisAccess();
builder()->StoreAccumulatorInRegister(super_property_args[0]);
VisitForRegisterValue(super_property->home_object(),
super_property_args[1]);
builder()
......@@ -3122,7 +3123,8 @@ BytecodeGenerator::AssignmentLhsData BytecodeGenerator::PrepareAssignmentLhs(
register_allocator()->NewRegisterList(4);
SuperPropertyReference* super_property =
property->obj()->AsSuperPropertyReference();
VisitForRegisterValue(super_property->this_var(), super_property_args[0]);
BuildThisAccess();
builder()->StoreAccumulatorInRegister(super_property_args[0]);
VisitForRegisterValue(super_property->home_object(),
super_property_args[1]);
VisitForRegisterValue(property->key(), super_property_args[2]);
......@@ -4165,7 +4167,8 @@ void BytecodeGenerator::VisitNamedSuperPropertyLoad(Property* property,
SuperPropertyReference* super_property =
property->obj()->AsSuperPropertyReference();
RegisterList args = register_allocator()->NewRegisterList(3);
VisitForRegisterValue(super_property->this_var(), args[0]);
BuildThisAccess();
builder()->StoreAccumulatorInRegister(args[0]);
VisitForRegisterValue(super_property->home_object(), args[1]);
builder()->SetExpressionPosition(property);
......@@ -4185,7 +4188,8 @@ void BytecodeGenerator::VisitKeyedSuperPropertyLoad(Property* property,
SuperPropertyReference* super_property =
property->obj()->AsSuperPropertyReference();
RegisterList args = register_allocator()->NewRegisterList(3);
VisitForRegisterValue(super_property->this_var(), args[0]);
BuildThisAccess();
builder()->StoreAccumulatorInRegister(args[0]);
VisitForRegisterValue(super_property->home_object(), args[1]);
VisitForRegisterValue(property->key(), args[2]);
......@@ -4453,8 +4457,8 @@ void BytecodeGenerator::VisitCallSuper(Call* expr) {
// Default constructors don't need have to do the assignment because
// 'this' isn't accessed in default constructors.
if (!IsDefaultConstructor(info()->literal()->kind())) {
BuildVariableAssignment(super->this_var()->var(), Token::INIT,
HoleCheckMode::kRequired);
Variable* var = closure_scope()->GetReceiverScope()->receiver();
BuildVariableAssignment(var, Token::INIT, HoleCheckMode::kRequired);
}
// The derived constructor has the correct bit set always, so we
......@@ -4584,7 +4588,7 @@ void BytecodeGenerator::VisitDelete(UnaryOperation* unary) {
Register object = VisitForRegisterValue(property->obj());
VisitForAccumulatorValue(property->key());
builder()->Delete(object, language_mode());
} else if (expr->IsVariableProxy() && !expr->AsVariableProxy()->is_this() &&
} else if (expr->IsVariableProxy() &&
!expr->AsVariableProxy()->is_new_target()) {
// Delete of an unqualified identifier is allowed in sloppy mode but is
// not allowed in strict mode.
......@@ -4667,7 +4671,8 @@ void BytecodeGenerator::VisitCountOperation(CountOperation* expr) {
RegisterList load_super_args = super_property_args.Truncate(3);
SuperPropertyReference* super_property =
property->obj()->AsSuperPropertyReference();
VisitForRegisterValue(super_property->this_var(), load_super_args[0]);
BuildThisAccess();
builder()->StoreAccumulatorInRegister(load_super_args[0]);
VisitForRegisterValue(super_property->home_object(), load_super_args[1]);
builder()
->LoadLiteral(property->key()->AsLiteral()->AsRawPropertyName())
......@@ -4680,7 +4685,8 @@ void BytecodeGenerator::VisitCountOperation(CountOperation* expr) {
RegisterList load_super_args = super_property_args.Truncate(3);
SuperPropertyReference* super_property =
property->obj()->AsSuperPropertyReference();
VisitForRegisterValue(super_property->this_var(), load_super_args[0]);
BuildThisAccess();
builder()->StoreAccumulatorInRegister(load_super_args[0]);
VisitForRegisterValue(super_property->home_object(), load_super_args[1]);
VisitForRegisterValue(property->key(), load_super_args[2]);
builder()->CallRuntime(Runtime::kLoadKeyedFromSuper, load_super_args);
......@@ -5134,8 +5140,19 @@ void BytecodeGenerator::VisitTemplateLiteral(TemplateLiteral* expr) {
}
}
void BytecodeGenerator::VisitThisFunction(ThisFunction* expr) {
builder()->LoadAccumulatorWithRegister(Register::function_closure());
void BytecodeGenerator::BuildThisAccess() {
DeclarationScope* receiver_scope = closure_scope()->GetReceiverScope();
Variable* var = receiver_scope->receiver();
// TODO(littledan): implement 'this' hole check elimination.
HoleCheckMode hole_check_mode =
IsDerivedConstructor(receiver_scope->function_kind())
? HoleCheckMode::kRequired
: HoleCheckMode::kElided;
BuildVariableLoad(var, hole_check_mode);
}
void BytecodeGenerator::VisitThisExpression(ThisExpression* expr) {
BuildThisAccess();
}
void BytecodeGenerator::VisitSuperCallReference(SuperCallReference* expr) {
......
......@@ -204,6 +204,8 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
void BuildAssignment(const AssignmentLhsData& data, Token::Value op,
LookupHoistingMode lookup_hoisting_mode);
void BuildThisAccess();
Expression* GetDestructuringDefaultValue(Expression** target);
void BuildDestructuringArrayAssignment(
ArrayLiteral* pattern, Token::Value op,
......
......@@ -80,11 +80,6 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone, Scope* scope,
break;
}
}
DCHECK(module_vars_count == 0 || scope->is_module_scope());
// Make sure we allocate the correct amount.
DCHECK_EQ(scope->ContextLocalCount(), context_local_count);
// Determine use and location of the "this" binding if it is present.
VariableAllocationInfo receiver_info;
if (scope->is_declaration_scope() &&
......@@ -94,6 +89,7 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone, Scope* scope,
receiver_info = UNUSED;
} else if (var->IsContextSlot()) {
receiver_info = CONTEXT;
context_local_count++;
} else {
DCHECK(var->IsParameter());
receiver_info = STACK;
......@@ -102,6 +98,11 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone, Scope* scope,
receiver_info = NONE;
}
DCHECK(module_vars_count == 0 || scope->is_module_scope());
// Make sure we allocate the correct amount.
DCHECK_EQ(scope->ContextLocalCount(), context_local_count);
const bool has_new_target =
scope->is_declaration_scope() &&
scope->AsDeclarationScope()->new_target_var() != nullptr;
......@@ -256,6 +257,22 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone, Scope* scope,
info = ParameterNumberField::update(info, i);
scope_info->set(info_index, Smi::FromInt(info));
}
// TODO(verwaest): Remove this unnecessary entry.
if (scope->AsDeclarationScope()->has_this_declaration()) {
Variable* var = scope->AsDeclarationScope()->receiver();
if (var->location() == VariableLocation::CONTEXT) {
int local_index = var->index() - Context::MIN_CONTEXT_SLOTS;
uint32_t info =
VariableModeField::encode(var->mode()) |
InitFlagField::encode(var->initialization_flag()) |
MaybeAssignedFlagField::encode(var->maybe_assigned()) |
ParameterNumberField::encode(ParameterNumberField::kMax);
scope_info->set(context_local_base + local_index, *var->name(), mode);
scope_info->set(context_local_info_base + local_index,
Smi::FromInt(info));
}
}
}
index += 2 * context_local_count;
......
......@@ -108,6 +108,16 @@ class ExpressionScope {
Report(loc, message);
}
void RecordThisUse() {
ExpressionScope* scope = this;
do {
if (scope->IsArrowHeadParsingScope()) {
scope->AsArrowHeadParsingScope()->RecordThisUse();
}
scope = scope->parent();
} while (scope != nullptr);
}
void RecordPatternError(const Scanner::Location& loc,
MessageTemplate message) {
// TODO(verwaest): Non-assigning expression?
......@@ -694,6 +704,7 @@ class ArrowHeadParsingScope : public ExpressionParsingScope<Types> {
for (auto declaration : *result->declarations()) {
declaration->var()->set_initializer_position(initializer_position);
}
if (uses_this_) result->UsesThis();
return result;
}
......@@ -705,6 +716,7 @@ class ArrowHeadParsingScope : public ExpressionParsingScope<Types> {
}
void RecordNonSimpleParameter() { has_simple_parameter_list_ = false; }
void RecordThisUse() { uses_this_ = true; }
private:
FunctionKind kind() const {
......@@ -716,6 +728,7 @@ class ArrowHeadParsingScope : public ExpressionParsingScope<Types> {
Scanner::Location declaration_error_location = Scanner::Location::invalid();
MessageTemplate declaration_error_message = MessageTemplate::kNone;
bool has_simple_parameter_list_ = true;
bool uses_this_ = false;
DISALLOW_COPY_AND_ASSIGN(ArrowHeadParsingScope);
};
......
......@@ -954,6 +954,26 @@ class ParserBase {
if (is_strict(language_mode)) parameters.ValidateStrictMode(impl());
}
// Needs to be called if the reference needs to be available from the current
// point. It causes the receiver to be context allocated if necessary.
// Returns the receiver variable that we're referencing.
V8_INLINE Variable* UseThis() {
DeclarationScope* closure_scope = scope()->GetClosureScope();
DeclarationScope* receiver_scope = closure_scope->GetReceiverScope();
Variable* var = receiver_scope->receiver();
var->set_is_used();
if (closure_scope == receiver_scope) {
// It's possible that we're parsing the head of an arrow function, in
// which case we haven't realized yet that closure_scope !=
// receiver_scope. Mark through the ExpressionScope for now.
expression_scope()->RecordThisUse();
} else {
closure_scope->set_has_this_reference();
var->ForceContextAllocation();
}
return var;
}
V8_INLINE IdentifierT ParseAndClassifyIdentifier(Token::Value token);
// Parses an identifier or a strict mode future reserved word. Allows passing
// in function_kind for the case of parsing the identifier in a function
......@@ -1661,7 +1681,7 @@ ParserBase<Impl>::ParsePrimaryExpression() {
switch (token) {
case Token::THIS: {
Consume(Token::THIS);
return impl()->ThisExpression(beg_pos);
return impl()->ThisExpression();
}
case Token::ASSIGN_DIV:
......@@ -3259,6 +3279,7 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseSuperExpression(
IsClassConstructor(kind)) {
if (Token::IsProperty(peek())) {
scope->RecordSuperPropertyUsage();
UseThis();
return impl()->NewSuperPropertyReference(pos);
}
// new super() is never allowed.
......@@ -3266,6 +3287,8 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseSuperExpression(
if (!is_new && peek() == Token::LPAREN && IsDerivedConstructor(kind)) {
// TODO(rossberg): This might not be the correct FunctionState for the
// method here.
expression_scope()->RecordThisUse();
UseThis()->set_maybe_assigned();
return impl()->NewSuperCallReference(pos);
}
}
......@@ -3841,8 +3864,10 @@ void ParserBase<Impl>::ParseFunctionBody(
}
if (IsDerivedConstructor(kind)) {
ExpressionParsingScope expression_scope(impl());
inner_body.Add(factory()->NewReturnStatement(impl()->ThisExpression(),
kNoSourcePosition));
expression_scope.ValidateExpression();
}
Expect(closing_token);
}
......@@ -4982,7 +5007,9 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseReturnStatement() {
ExpressionT return_value = impl()->NullExpression();
if (scanner()->HasLineTerminatorBeforeNext() || Token::IsAutoSemicolon(tok)) {
if (IsDerivedConstructor(function_state_->kind())) {
return_value = impl()->ThisExpression(loc.beg_pos);
ExpressionParsingScope expression_scope(impl());
return_value = impl()->ThisExpression();
expression_scope.ValidateExpression();
}
} else {
return_value = ParseExpression();
......
......@@ -285,8 +285,7 @@ Expression* Parser::NewSuperPropertyReference(int pos) {
AstSymbol::kHomeObjectSymbol, kNoSourcePosition);
Expression* home_object = factory()->NewProperty(
this_function_proxy, home_object_symbol_literal, pos);
return factory()->NewSuperPropertyReference(
ThisExpression(pos)->AsVariableProxy(), home_object, pos);
return factory()->NewSuperPropertyReference(home_object, pos);
}
Expression* Parser::NewSuperCallReference(int pos) {
......@@ -294,9 +293,8 @@ Expression* Parser::NewSuperCallReference(int pos) {
NewUnresolved(ast_value_factory()->new_target_string(), pos);
VariableProxy* this_function_proxy =
NewUnresolved(ast_value_factory()->this_function_string(), pos);
return factory()->NewSuperCallReference(
ThisExpression(pos)->AsVariableProxy(), new_target_proxy,
this_function_proxy, pos);
return factory()->NewSuperCallReference(new_target_proxy, this_function_proxy,
pos);
}
Expression* Parser::NewTargetExpression(int pos) {
......@@ -451,6 +449,10 @@ void Parser::DeserializeScopeChain(
original_scope_ = Scope::DeserializeScopeChain(
isolate, zone(), *outer_scope_info, info->script_scope(),
ast_value_factory(), mode);
if (info->is_eval() || IsArrowFunction(info->function_kind())) {
original_scope_->GetReceiverScope()->DeserializeReceiver(
ast_value_factory());
}
}
}
......@@ -1568,8 +1570,11 @@ Expression* Parser::RewriteReturn(Expression* return_value, int pos) {
factory()->NewUndefinedLiteral(kNoSourcePosition), pos);
// is_undefined ? this : temp
// We don't need to call UseThis() since it's guaranteed to be called
// for derived constructors after parsing the constructor in
// ParseFunctionBody.
return_value =
factory()->NewConditional(is_undefined, ThisExpression(pos),
factory()->NewConditional(is_undefined, factory()->ThisExpression(),
factory()->NewVariableProxy(temp), pos);
}
return return_value;
......@@ -3119,7 +3124,7 @@ Expression* Parser::SpreadCall(Expression* function,
if (function->IsProperty()) {
// Method calls
if (function->AsProperty()->IsSuperAccess()) {
Expression* home = ThisExpression(kNoSourcePosition);
Expression* home = ThisExpression();
args.Add(function);
args.Add(home);
} else {
......
......@@ -565,8 +565,7 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
V8_INLINE static bool IsThisProperty(Expression* expression) {
DCHECK_NOT_NULL(expression);
Property* property = expression->AsProperty();
return property != nullptr && property->obj()->IsVariableProxy() &&
property->obj()->AsVariableProxy()->is_this();
return property != nullptr && property->obj()->IsThisExpression();
}
// This returns true if the expression is an indentifier (wrapped
......@@ -574,8 +573,7 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
// has been converted to a variable proxy.
V8_INLINE static bool IsIdentifier(Expression* expression) {
VariableProxy* operand = expression->AsVariableProxy();
return operand != nullptr && !operand->is_this() &&
!operand->is_new_target();
return operand != nullptr && !operand->is_new_target();
}
V8_INLINE static const AstRawString* AsIdentifier(Expression* expression) {
......@@ -790,9 +788,9 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
return ast_value_factory()->GetOneByteString(string);
}
V8_INLINE Expression* ThisExpression(int pos = kNoSourcePosition) {
return NewUnresolved(ast_value_factory()->this_string(), pos,
THIS_VARIABLE);
class ThisExpression* ThisExpression() {
UseThis();
return factory()->ThisExpression();
}
Expression* NewSuperPropertyReference(int pos);
......
......@@ -1537,10 +1537,8 @@ class PreParser : public ParserBase<PreParser> {
return PreParserIdentifier::Default();
}
V8_INLINE PreParserExpression ThisExpression(int pos = kNoSourcePosition) {
scope()->NewUnresolved(factory()->ast_node_factory(),
ast_value_factory()->this_string(), pos,
THIS_VARIABLE);
V8_INLINE PreParserExpression ThisExpression() {
UseThis();
return PreParserExpression::This();
}
......@@ -1548,9 +1546,6 @@ class PreParser : public ParserBase<PreParser> {
scope()->NewUnresolved(factory()->ast_node_factory(),
ast_value_factory()->this_function_string(), pos,
NORMAL_VARIABLE);
scope()->NewUnresolved(factory()->ast_node_factory(),
ast_value_factory()->this_string(), pos,
THIS_VARIABLE);
return PreParserExpression::Default();
}
......@@ -1561,9 +1556,6 @@ class PreParser : public ParserBase<PreParser> {
scope()->NewUnresolved(factory()->ast_node_factory(),
ast_value_factory()->new_target_string(), pos,
NORMAL_VARIABLE);
scope()->NewUnresolved(factory()->ast_node_factory(),
ast_value_factory()->this_string(), pos,
THIS_VARIABLE);
return PreParserExpression::SuperCallReference();
}
......
......@@ -1101,8 +1101,7 @@ TEST(ScopeUsesArgumentsSuperThis) {
if ((source_data[i].expected & THIS) != 0) {
// Currently the is_used() flag is conservative; all variables in a
// script scope are marked as used.
CHECK(scope->LookupForTesting(info.ast_value_factory()->this_string())
->is_used());
CHECK(scope->GetReceiverScope()->receiver()->is_used());
}
if (is_sloppy(scope->language_mode())) {
CHECK_EQ((source_data[i].expected & EVAL) != 0,
......
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