Commit 2afb952d authored by Shu-yu Guo's avatar Shu-yu Guo Committed by V8 LUCI CQ

[parser] Fix scope of super properties in heritage position

super.property accesses in heritage positions like `class C extends
super.property` should resolve super in the current scope, not C's
class scope.

Bug: chromium:1282096
Change-Id: I7ef815bc02cfff35a2898ef9f39b133d1114046c
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3400150Reviewed-by: 's avatarMarja Hölttä <marja@chromium.org>
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Cr-Commit-Position: refs/heads/main@{#78687}
parent 447af864
......@@ -2673,6 +2673,49 @@ int Scope::ContextLocalCount() const {
(is_function_var_in_context ? 1 : 0);
}
VariableProxy* Scope::NewHomeObjectVariableProxy(AstNodeFactory* factory,
const AstRawString* name,
int start_pos) {
// VariableProxies of the home object cannot be resolved like a normal
// variable. Consider the case of a super.property usage in heritage position:
//
// class C extends super.foo { m() { super.bar(); } }
//
// The super.foo property access is logically nested under C's class scope,
// which also has a home object due to its own method m's usage of
// super.bar(). However, super.foo must resolve super in C's outer scope.
//
// Because of the above, home object VariableProxies are always made directly
// on the Scope that needs the home object instead of the innermost scope.
DCHECK(needs_home_object());
if (!scope_info_.is_null()) {
// This is a lazy compile, so the home object's context slot is already
// known.
Variable* home_object = variables_.Lookup(name);
if (home_object == nullptr) {
VariableLookupResult lookup_result;
int index = ScopeInfo::ContextSlotIndex(*scope_info_, *name->string(),
&lookup_result);
DCHECK_GE(index, 0);
bool was_added;
home_object = variables_.Declare(zone(), this, name, lookup_result.mode,
NORMAL_VARIABLE, lookup_result.init_flag,
lookup_result.maybe_assigned_flag,
IsStaticFlag::kNotStatic, &was_added);
DCHECK(was_added);
home_object->AllocateTo(VariableLocation::CONTEXT, index);
}
return factory->NewVariableProxy(home_object, start_pos);
}
// This is not a lazy compile. Add the unresolved home object VariableProxy to
// the unresolved list of the home object scope, which is not necessarily the
// innermost scope.
VariableProxy* proxy =
factory->NewVariableProxy(name, NORMAL_VARIABLE, start_pos);
AddUnresolved(proxy);
return proxy;
}
bool IsComplementaryAccessorPair(VariableMode a, VariableMode b) {
switch (a) {
case VariableMode::kPrivateGetterOnly:
......
......@@ -605,6 +605,10 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
needs_home_object_ = true;
}
VariableProxy* NewHomeObjectVariableProxy(AstNodeFactory* factory,
const AstRawString* name,
int start_pos);
bool RemoveInnerScope(Scope* inner_scope) {
DCHECK_NOT_NULL(inner_scope);
if (inner_scope == inner_scope_) {
......@@ -863,7 +867,7 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
FunctionKind function_kind() const { return function_kind_; }
// Inform the scope that the corresponding code uses "super".
void RecordSuperPropertyUsage() {
Scope* RecordSuperPropertyUsage() {
DCHECK(IsConciseMethod(function_kind()) ||
IsAccessorFunction(function_kind()) ||
IsClassConstructor(function_kind()));
......@@ -871,6 +875,7 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
Scope* home_object_scope = GetHomeObjectScope();
DCHECK_NOT_NULL(home_object_scope);
home_object_scope->set_needs_home_object();
return home_object_scope;
}
bool uses_super_property() const { return uses_super_property_; }
......
......@@ -3769,9 +3769,9 @@ ParserBase<Impl>::ParseSuperExpression() {
impl()->ReportMessage(MessageTemplate::kOptionalChainingNoSuper);
return impl()->FailureExpression();
}
scope->RecordSuperPropertyUsage();
Scope* home_object_scope = scope->RecordSuperPropertyUsage();
UseThis();
return impl()->NewSuperPropertyReference(pos);
return impl()->NewSuperPropertyReference(home_object_scope, pos);
}
// super() is only allowed in derived constructor. new super() is never
// allowed; it's reported as an error by
......
......@@ -283,7 +283,8 @@ Expression* Parser::NewThrowError(Runtime::FunctionId id,
return factory()->NewThrow(call_constructor, pos);
}
Expression* Parser::NewSuperPropertyReference(int pos) {
Expression* Parser::NewSuperPropertyReference(Scope* home_object_scope,
int pos) {
const AstRawString* home_object_name;
if (IsStatic(scope()->GetReceiverScope()->function_kind())) {
home_object_name = ast_value_factory_->dot_static_home_object_string();
......@@ -291,7 +292,9 @@ Expression* Parser::NewSuperPropertyReference(int pos) {
home_object_name = ast_value_factory_->dot_home_object_string();
}
return factory()->NewSuperPropertyReference(
NewUnresolved(home_object_name, pos), pos);
home_object_scope->NewHomeObjectVariableProxy(factory(), home_object_name,
pos),
pos);
}
Expression* Parser::NewSuperCallReference(int pos) {
......
......@@ -791,7 +791,7 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
return factory()->NewThisExpression(pos);
}
Expression* NewSuperPropertyReference(int pos);
Expression* NewSuperPropertyReference(Scope* home_object_scope, int pos);
Expression* NewSuperCallReference(int pos);
Expression* NewTargetExpression(int pos);
Expression* ImportMetaExpression(int pos);
......
......@@ -1532,7 +1532,8 @@ class PreParser : public ParserBase<PreParser> {
return PreParserExpression::This();
}
V8_INLINE PreParserExpression NewSuperPropertyReference(int pos) {
V8_INLINE PreParserExpression
NewSuperPropertyReference(Scope* home_object_scope, int pos) {
return PreParserExpression::Default();
}
......
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
class A {
getValue() {
return 'A';
}
static extend() {
return class extends this {
getValue() {
return 'A.extend:' + super.getValue();
}
}
}
}
class B extends A {
getValue() {
return 'B:' + super.getValue();
}
static extend() {
return class extends super.extend() {
getValue() {
return 'B.extend:' + super.getValue();
}
}
}
static extend2() {
// Have 2 uses of super to test the Scope's cache.
let x = super.extend();
return class extends super.extend() {
getValue() {
return 'B.extend:' + super.getValue();
}
}
}
}
const C = B.extend();
const c = new C();
assertEquals(c.getValue(), 'B.extend:A.extend:B:A');
const C2 = B.extend2();
const c2 = new C2();
assertEquals(c2.getValue(), 'B.extend:A.extend:B:A');
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