Commit a3c7e968 authored by Shu-yu Guo's avatar Shu-yu Guo Committed by Commit Bot

[class] Fix private name scope chain

Expressions in class heritage position do not have access to the
inheriting class's private names, only its lexical bindings. The parser
currently uses the same scope chain for both.

This CL makes scopes in class heritage position skip their outer class
when resolving private names. Whether a scope needs to skip is kept as a
bit on various scope-related data structures.

See implementation doc at
https://docs.google.com/document/d/1d3o_SQqcICxfjLMw53OOaiIQux0ppNHQJnjZHtCQLwA

Bug: v8:9177
Change-Id: I77e491a9d4a261131274f12ddf052af7ac31a921
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1769486
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Reviewed-by: 's avatarToon Verwaest <verwaest@chromium.org>
Reviewed-by: 's avatarMathias Bynens <mathias@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63586}
parent 9a9ea230
......@@ -293,6 +293,10 @@ bool FunctionLiteral::requires_brand_initialization() const {
return outer->AsClassScope()->brand() != nullptr;
}
bool FunctionLiteral::private_name_lookup_skips_outer_class() const {
return scope()->private_name_lookup_skips_outer_class();
}
ObjectLiteralProperty::ObjectLiteralProperty(Expression* key, Expression* value,
Kind kind, bool is_computed_name)
: LiteralProperty(key, value, is_computed_name),
......
......@@ -2350,6 +2350,8 @@ class FunctionLiteral final : public Expression {
bool requires_brand_initialization() const;
bool private_name_lookup_skips_outer_class() const;
ProducedPreparseData* produced_preparse_data() const {
return produced_preparse_data_;
}
......
This diff is collapsed.
......@@ -360,6 +360,9 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
bool is_class_scope() const { return scope_type_ == CLASS_SCOPE; }
bool inner_scope_calls_eval() const { return inner_scope_calls_eval_; }
bool private_name_lookup_skips_outer_class() const {
return private_name_lookup_skips_outer_class_;
}
bool IsAsmModule() const;
// Returns true if this scope or any inner scopes that might be eagerly
// compiled are asm modules.
......@@ -464,10 +467,6 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
// sloppy eval call. One if this->sloppy_eval_can_extend_vars().
int ContextChainLengthUntilOutermostSloppyEval() const;
// Find the closest class scope in the current scope and outer scopes. If no
// class scope is found, nullptr will be returned.
ClassScope* GetClassScope();
// Find the first function, script, eval or (declaration) block scope. This is
// the scope where var declarations will be hoisted to in the implementation.
DeclarationScope* GetDeclarationScope();
......@@ -712,7 +711,8 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
// This scope's declarations might not be executed in order (e.g., switch).
bool scope_nonlinear_ : 1;
bool is_hidden_ : 1;
// Temporary workaround that allows masking of 'this' in debug-evalute scopes.
// Temporary workaround that allows masking of 'this' in debug-evaluate
// scopes.
bool is_debug_evaluate_scope_ : 1;
// True if one of the inner scopes or the scope itself calls eval.
......@@ -722,6 +722,11 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
// True if it holds 'var' declarations.
bool is_declaration_scope_ : 1;
// True if the outer scope is a class scope and should be skipped when
// resolving private names, i.e. if the scope is in a class heritage
// expression.
bool private_name_lookup_skips_outer_class_ : 1;
bool must_use_preparsed_scope_data_ : 1;
};
......@@ -1080,6 +1085,11 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
GetReceiverScope()->receiver()->ForceContextAllocation();
}
bool needs_private_name_context_chain_recalc() const {
return needs_private_name_context_chain_recalc_;
}
void RecordNeedsPrivateNameContextChainRecalc();
private:
V8_INLINE void AllocateParameter(Variable* var, int index);
......@@ -1097,6 +1107,12 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
void SetDefaults();
// Recalculate the private name context chain from the existing skip bit in
// preparation for AllocateScopeInfos. Because the private name scope is
// implemented with a skip bit for scopes in heritage position, that bit may
// need to be recomputed due scopes that do not need contexts.
void RecalcPrivateNameContextChain();
bool has_simple_parameters_ : 1;
// This scope contains an "use asm" annotation.
bool is_asm_module_ : 1;
......@@ -1118,6 +1134,7 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
bool has_checked_syntax_ : 1;
bool has_this_reference_ : 1;
bool has_this_declaration_ : 1;
bool needs_private_name_context_chain_recalc_ : 1;
// If the scope is a function scope, this is the function kind.
const FunctionKind function_kind_;
......@@ -1223,13 +1240,22 @@ class V8_EXPORT_PRIVATE ClassScope : public Scope {
ClassScope(Zone* zone, AstValueFactory* ast_value_factory,
Handle<ScopeInfo> scope_info);
struct HeritageParsingScope {
explicit HeritageParsingScope(ClassScope* class_scope)
: class_scope_(class_scope) {
class_scope_->SetIsParsingHeritage(true);
}
~HeritageParsingScope() { class_scope_->SetIsParsingHeritage(false); }
private:
ClassScope* class_scope_;
};
// Declare a private name in the private name map and add it to the
// local variables of this scope.
Variable* DeclarePrivateName(const AstRawString* name, VariableMode mode,
bool* was_added);
void AddUnresolvedPrivateName(VariableProxy* proxy);
// Try resolving all unresolved private names found in the current scope.
// Called from DeclarationScope::AllocateVariables() when reparsing a
// method to generate code or when eval() is called to access private names.
......@@ -1261,11 +1287,17 @@ class V8_EXPORT_PRIVATE ClassScope : public Scope {
Variable* DeclareBrandVariable(AstValueFactory* ast_value_factory,
int class_token_pos);
Variable* brand() {
return rare_data_ == nullptr ? nullptr : rare_data_->brand;
return GetRareData() == nullptr ? nullptr : GetRareData()->brand;
}
V8_INLINE bool IsParsingHeritage() {
return rare_data_and_is_parsing_heritage_.GetPayload();
}
private:
friend class Scope;
friend class PrivateNameScopeIterator;
// Find the private name declared in the private name map first,
// if it cannot be found there, try scope info if there is any.
// Returns nullptr if it cannot be found.
......@@ -1283,14 +1315,44 @@ class V8_EXPORT_PRIVATE ClassScope : public Scope {
Variable* brand = nullptr;
};
V8_INLINE RareData* GetRareData() {
return rare_data_and_is_parsing_heritage_.GetPointer();
}
V8_INLINE RareData* EnsureRareData() {
if (rare_data_ == nullptr) {
rare_data_ = new (zone_) RareData(zone_);
if (GetRareData() == nullptr) {
rare_data_and_is_parsing_heritage_.SetPointer(new (zone_)
RareData(zone_));
}
return rare_data_;
return GetRareData();
}
V8_INLINE void SetIsParsingHeritage(bool v) {
rare_data_and_is_parsing_heritage_.SetPayload(v);
}
RareData* rare_data_ = nullptr;
PointerWithPayload<RareData, bool, 1> rare_data_and_is_parsing_heritage_;
};
// Iterate over the private name scope chain. The iteration proceeds from the
// innermost private name scope outwards.
class PrivateNameScopeIterator {
public:
explicit PrivateNameScopeIterator(Scope* start);
bool Done() const { return current_scope_ == nullptr; }
void Next();
// Add an unresolved private name to the current scope.
void AddUnresolvedPrivateName(VariableProxy* proxy);
ClassScope* GetScope() const {
DCHECK(!Done());
return current_scope_->AsClassScope();
}
private:
bool skipped_any_scopes_ = false;
Scope* start_scope_;
Scope* current_scope_;
};
} // namespace internal
......
......@@ -5354,6 +5354,8 @@ void SharedFunctionInfo::InitFromFunctionLiteral(
Scope* outer_scope = lit->scope()->GetOuterScopeWithContext();
if (outer_scope) {
shared_info->set_outer_scope_info(*outer_scope->scope_info());
shared_info->set_private_name_lookup_skips_outer_class(
lit->scope()->private_name_lookup_skips_outer_class());
}
}
......
......@@ -146,6 +146,7 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone, Scope* scope,
? scope->AsDeclarationScope()->num_parameters()
: 0;
const bool has_outer_scope_info = !outer_scope.is_null();
const int length = kVariablePartIndex + 2 * context_local_count +
(has_receiver ? 1 : 0) +
(has_function_name ? kFunctionNameEntries : 0) +
......@@ -196,7 +197,9 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone, Scope* scope,
HasOuterScopeInfoField::encode(has_outer_scope_info) |
IsDebugEvaluateScopeField::encode(scope->is_debug_evaluate_scope()) |
ForceContextAllocationField::encode(
scope->ForceContextForLanguageMode());
scope->ForceContextForLanguageMode()) |
PrivateNameLookupSkipsOuterClassField::encode(
scope->private_name_lookup_skips_outer_class());
scope_info.SetFlags(flags);
scope_info.SetParameterCount(parameter_count);
......@@ -366,7 +369,9 @@ Handle<ScopeInfo> ScopeInfo::CreateForWithScope(
IsAsmModuleField::encode(false) | HasSimpleParametersField::encode(true) |
FunctionKindField::encode(kNormalFunction) |
HasOuterScopeInfoField::encode(has_outer_scope_info) |
IsDebugEvaluateScopeField::encode(false);
IsDebugEvaluateScopeField::encode(false) |
ForceContextAllocationField::encode(false) |
PrivateNameLookupSkipsOuterClassField::encode(false);
scope_info->SetFlags(flags);
scope_info->SetParameterCount(0);
......@@ -431,7 +436,9 @@ Handle<ScopeInfo> ScopeInfo::CreateForBootstrapping(Isolate* isolate,
IsAsmModuleField::encode(false) | HasSimpleParametersField::encode(true) |
FunctionKindField::encode(FunctionKind::kNormalFunction) |
HasOuterScopeInfoField::encode(false) |
IsDebugEvaluateScopeField::encode(false);
IsDebugEvaluateScopeField::encode(false) |
ForceContextAllocationField::encode(false) |
PrivateNameLookupSkipsOuterClassField::encode(false);
scope_info->SetFlags(flags);
scope_info->SetParameterCount(parameter_count);
scope_info->SetContextLocalCount(context_local_count);
......@@ -608,6 +615,11 @@ void ScopeInfo::SetIsDebugEvaluateScope() {
}
}
bool ScopeInfo::PrivateNameLookupSkipsOuterClass() const {
if (length() == 0) return false;
return PrivateNameLookupSkipsOuterClassField::decode(Flags());
}
bool ScopeInfo::HasContext() const { return ContextLength() > 0; }
Object ScopeInfo::FunctionName() const {
......
......@@ -176,6 +176,10 @@ class ScopeInfo : public FixedArray {
// Return the outer ScopeInfo if present.
ScopeInfo OuterScopeInfo() const;
// Returns true if this ScopeInfo was created for a scope that skips the
// closest outer class when resolving private names.
bool PrivateNameLookupSkipsOuterClass() const;
#ifdef DEBUG
bool Equals(ScopeInfo other) const;
#endif
......@@ -240,6 +244,8 @@ class ScopeInfo : public FixedArray {
using HasOuterScopeInfoField = FunctionKindField::Next<bool, 1>;
using IsDebugEvaluateScopeField = HasOuterScopeInfoField::Next<bool, 1>;
using ForceContextAllocationField = IsDebugEvaluateScopeField::Next<bool, 1>;
using PrivateNameLookupSkipsOuterClassField =
ForceContextAllocationField::Next<bool, 1>;
STATIC_ASSERT(kLastFunctionKind <= FunctionKindField::kMax);
......
......@@ -212,6 +212,9 @@ BIT_FIELD_ACCESSORS(SharedFunctionInfo, flags,
BIT_FIELD_ACCESSORS(SharedFunctionInfo, flags,
is_safe_to_skip_arguments_adaptor,
SharedFunctionInfo::IsSafeToSkipArgumentsAdaptorBit)
BIT_FIELD_ACCESSORS(SharedFunctionInfo, flags,
private_name_lookup_skips_outer_class,
SharedFunctionInfo::PrivateNameLookupSkipsOuterClassBit)
bool SharedFunctionInfo::optimization_disabled() const {
return disable_optimization_reason() != BailoutReason::kNoReason;
......
......@@ -448,6 +448,10 @@ class SharedFunctionInfo : public HeapObject {
// Indicates that the function has been reported for binary code coverage.
DECL_BOOLEAN_ACCESSORS(has_reported_binary_coverage)
// Indicates that the private name lookups inside the function skips the
// closest outer class scope.
DECL_BOOLEAN_ACCESSORS(private_name_lookup_skips_outer_class)
inline FunctionKind kind() const;
// Defines the index in a native context of closure's map instantiated using
......@@ -649,7 +653,8 @@ class SharedFunctionInfo : public HeapObject {
V(HasReportedBinaryCoverageBit, bool, 1, _) \
V(IsTopLevelBit, bool, 1, _) \
V(IsOneshotIIFEOrPropertiesAreFinalBit, bool, 1, _) \
V(IsSafeToSkipArgumentsAdaptorBit, bool, 1, _)
V(IsSafeToSkipArgumentsAdaptorBit, bool, 1, _) \
V(PrivateNameLookupSkipsOuterClassBit, bool, 1, _)
DEFINE_BIT_FIELDS(FLAGS_BIT_FIELDS)
#undef FLAGS_BIT_FIELDS
......
......@@ -1577,16 +1577,17 @@ ParserBase<Impl>::ParsePropertyOrPrivatePropertyName() {
//
// Here, we check if this is a new private name reference in a top
// level function and throw an error if so.
ClassScope* class_scope = scope()->GetClassScope();
PrivateNameScopeIterator private_name_scope_iter(scope());
// Parse the identifier so that we can display it in the error message
name = impl()->GetIdentifier();
if (class_scope == nullptr) {
if (private_name_scope_iter.Done()) {
impl()->ReportMessageAt(Scanner::Location(pos, pos + 1),
MessageTemplate::kInvalidPrivateFieldResolution,
impl()->GetRawNameFromIdentifier(name));
return impl()->FailureExpression();
}
key = impl()->ExpressionFromPrivateName(class_scope, name, pos);
key =
impl()->ExpressionFromPrivateName(&private_name_scope_iter, name, pos);
} else {
ReportUnexpectedToken(next);
return impl()->FailureExpression();
......@@ -4360,6 +4361,7 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseClassLiteral(
scope()->set_start_position(end_position());
if (Check(Token::EXTENDS)) {
ClassScope::HeritageParsingScope heritage(class_scope);
FuncNameInferrerState fni_state(&fni_);
ExpressionParsingScope scope(impl());
class_info.extends = ParseLeftHandSideExpression();
......
......@@ -705,8 +705,17 @@ FunctionLiteral* Parser::ParseFunction(Isolate* isolate, ParseInfo* info,
info->set_function_name(ast_value_factory()->GetString(name));
scanner_.Initialize();
FunctionLiteral* result =
DoParseFunction(isolate, info, info->function_name());
FunctionLiteral* result;
if (V8_UNLIKELY(shared_info->private_name_lookup_skips_outer_class() &&
original_scope_->is_class_scope())) {
// If the function skips the outer class and the outer scope is a class, the
// function is in heritage position. Otherwise the function scope's skip bit
// will be correctly inherited from the outer scope.
ClassScope::HeritageParsingScope heritage(original_scope_->AsClassScope());
result = DoParseFunction(isolate, info, info->function_name());
} else {
result = DoParseFunction(isolate, info, info->function_name());
}
MaybeResetCharacterStream(info, result);
MaybeProcessSourceRanges(info, result, stack_limit_);
if (result != nullptr) {
......@@ -2493,10 +2502,10 @@ bool Parser::SkipFunction(const AstRawString* function_name, FunctionKind kind,
bookmark.Set(function_scope->start_position());
UnresolvedList::Iterator unresolved_private_tail;
ClassScope* closest_class_scope = function_scope->GetClassScope();
if (closest_class_scope != nullptr) {
PrivateNameScopeIterator private_name_scope_iter(function_scope);
if (!private_name_scope_iter.Done()) {
unresolved_private_tail =
closest_class_scope->GetUnresolvedPrivateNameTail();
private_name_scope_iter.GetScope()->GetUnresolvedPrivateNameTail();
}
// With no cached data, we partially parse the function, without building an
......@@ -2520,8 +2529,8 @@ bool Parser::SkipFunction(const AstRawString* function_name, FunctionKind kind,
// the state before preparsing. The caller may then fully parse the function
// to identify the actual error.
bookmark.Apply();
if (closest_class_scope != nullptr) {
closest_class_scope->ResetUnresolvedPrivateNameTail(
if (!private_name_scope_iter.Done()) {
private_name_scope_iter.GetScope()->ResetUnresolvedPrivateNameTail(
unresolved_private_tail);
}
function_scope->ResetAfterPreparsing(ast_value_factory_, true);
......@@ -2542,8 +2551,8 @@ bool Parser::SkipFunction(const AstRawString* function_name, FunctionKind kind,
*num_parameters = logger->num_parameters();
*function_length = logger->function_length();
SkipFunctionLiterals(logger->num_inner_functions());
if (closest_class_scope != nullptr) {
closest_class_scope->MigrateUnresolvedPrivateNameTail(
if (!private_name_scope_iter.Done()) {
private_name_scope_iter.GetScope()->MigrateUnresolvedPrivateNameTail(
factory(), unresolved_private_tail);
}
function_scope->AnalyzePartially(this, factory());
......
......@@ -778,12 +778,12 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
Expression* ExpressionFromLiteral(Token::Value token, int pos);
V8_INLINE VariableProxy* ExpressionFromPrivateName(ClassScope* class_scope,
const AstRawString* name,
int start_position) {
V8_INLINE VariableProxy* ExpressionFromPrivateName(
PrivateNameScopeIterator* private_name_scope, const AstRawString* name,
int start_position) {
VariableProxy* proxy = factory()->ast_node_factory()->NewVariableProxy(
name, NORMAL_VARIABLE, start_position);
class_scope->AddUnresolvedPrivateName(proxy);
private_name_scope->AddUnresolvedPrivateName(proxy);
return proxy;
}
......
......@@ -24,6 +24,8 @@ namespace {
using ScopeSloppyEvalCanExtendVarsField = BitField8<bool, 0, 1>;
using InnerScopeCallsEvalField =
ScopeSloppyEvalCanExtendVarsField::Next<bool, 1>;
using NeedsPrivateNameContextChainRecalcField =
InnerScopeCallsEvalField::Next<bool, 1>;
using VariableMaybeAssignedField = BitField8<bool, 0, 1>;
using VariableContextAllocatedField = VariableMaybeAssignedField::Next<bool, 1>;
......@@ -322,7 +324,7 @@ void PreparseDataBuilder::SaveScopeAllocationData(DeclarationScope* scope,
if (SaveDataForSkippableFunction(builder)) num_inner_with_data_++;
}
// Don't save imcoplete scope information when bailed out.
// Don't save incomplete scope information when bailed out.
if (!bailed_out_) {
#ifdef DEBUG
// function data items, kSkippableMinFunctionDataSize each.
......@@ -352,13 +354,17 @@ void PreparseDataBuilder::SaveDataForScope(Scope* scope) {
byte_data_.WriteUint8(scope->scope_type());
#endif
uint8_t eval =
uint8_t eval_and_private_recalc =
ScopeSloppyEvalCanExtendVarsField::encode(
scope->is_declaration_scope() &&
scope->AsDeclarationScope()->sloppy_eval_can_extend_vars()) |
InnerScopeCallsEvalField::encode(scope->inner_scope_calls_eval());
InnerScopeCallsEvalField::encode(scope->inner_scope_calls_eval()) |
NeedsPrivateNameContextChainRecalcField::encode(
scope->is_function_scope() &&
scope->AsDeclarationScope()
->needs_private_name_context_chain_recalc());
byte_data_.Reserve(kUint8Size);
byte_data_.WriteUint8(eval);
byte_data_.WriteUint8(eval_and_private_recalc);
if (scope->is_function_scope()) {
Variable* function = scope->AsDeclarationScope()->function_var();
......@@ -599,9 +605,17 @@ void BaseConsumedPreparseData<Data>::RestoreDataForScope(Scope* scope) {
DCHECK_EQ(scope_data_->ReadUint8(), scope->scope_type());
CHECK(scope_data_->HasRemainingBytes(ByteData::kUint8Size));
uint32_t eval = scope_data_->ReadUint8();
if (ScopeSloppyEvalCanExtendVarsField::decode(eval)) scope->RecordEvalCall();
if (InnerScopeCallsEvalField::decode(eval)) scope->RecordInnerScopeEvalCall();
uint32_t eval_and_private_recalc = scope_data_->ReadUint8();
if (ScopeSloppyEvalCanExtendVarsField::decode(eval_and_private_recalc)) {
scope->RecordEvalCall();
}
if (InnerScopeCallsEvalField::decode(eval_and_private_recalc)) {
scope->RecordInnerScopeEvalCall();
}
if (NeedsPrivateNameContextChainRecalcField::decode(
eval_and_private_recalc)) {
scope->AsDeclarationScope()->RecordNeedsPrivateNameContextChainRecalc();
}
if (scope->is_function_scope()) {
Variable* function = scope->AsDeclarationScope()->function_var();
......
......@@ -1595,12 +1595,12 @@ class PreParser : public ParserBase<PreParser> {
return PreParserExpression::StringLiteral();
}
PreParserExpression ExpressionFromPrivateName(ClassScope* class_scope,
const PreParserIdentifier& name,
int start_position) {
PreParserExpression ExpressionFromPrivateName(
PrivateNameScopeIterator* private_name_scope,
const PreParserIdentifier& name, int start_position) {
VariableProxy* proxy = factory()->ast_node_factory()->NewVariableProxy(
name.string_, NORMAL_VARIABLE, start_position);
class_scope->AddUnresolvedPrivateName(proxy);
private_name_scope->AddUnresolvedPrivateName(proxy);
return PreParserExpression::FromIdentifier(name);
}
......
// Copyright 2019 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.
{
let heritageFn;
class O {
#f = "O.#f";
static C = class C extends (heritageFn = function () {
return class D {
exfil(obj) { return obj.#f; }
exfilEval(obj) { return eval("obj.#f"); }
};
}) {
#f = "C.#f";
};
}
const o = new O;
const c = new O.C;
const D = heritageFn();
const d = new D;
assertEquals(d.exfil(o), "O.#f");
assertEquals(d.exfilEval(o), "O.#f");
assertThrows(() => d.exfil(c), TypeError);
assertThrows(() => d.exfilEval(c), TypeError);
}
// Early errors
assertThrows(() => eval("new class extends " +
"(class { m() { let x = this.#f; } }) " +
"{ #f }"), SyntaxError);
assertThrows(() => eval("new class extends this.#foo { #foo }"), SyntaxError);
// Runtime errors
{
// Test private name context chain recalc.
let heritageFn;
class O {
#f = "O.#f";
static C = class C extends (heritageFn = function () {
return class D { exfil(obj) { return obj.#f; } }
}) {
#f = "C.#f";
};
}
const o = new O;
const c = new O.C;
const D = heritageFn();
const d = new D;
assertEquals(d.exfil(o), "O.#f");
assertThrows(() => d.exfil(c), TypeError);
}
{
// Test private name context chain recalc with nested closures with context.
let heritageFn;
class O {
#f = "O.#f";
static C = class C extends (heritageFn = function () {
let forceContext = 1;
return () => {
assertEquals(forceContext, 1);
return class D { exfil(obj) { return obj.#f; } }
};
}) {
#f = "C.#f";
};
}
const o = new O;
const c = new O.C;
const D = heritageFn()();
const d = new D;
assertEquals(d.exfil(o), "O.#f");
assertThrows(() => d.exfil(c), TypeError);
}
{
// Test private name context chain recalc where skipped class has no context.
let heritageFn;
class O {
#f = "O.#f";
static C = class C0 extends (class C1 extends (heritageFn = function (obj) {
if (obj) { return obj.#f; }
}) {}) {
#f = "C0.#f"
}
}
const o = new O;
const c = new O.C;
assertEquals(heritageFn(o), "O.#f");
assertThrows(() => heritageFn(c), TypeError);
}
{
// Test private name context chain recalc where skipping function has no
// context.
let heritageFn;
class O {
#f = "O.#f";
static C = class C extends (heritageFn = function () {
return (obj) => { return obj.#f; }
}) {
#f = "C.#f";
}
}
const o = new O;
const c = new O.C;
assertEquals(heritageFn()(o), "O.#f");
assertThrows(() => heritageFn()(c), TypeError);
}
{
// Test private name context chain recalc where neither skipped class nor
// skipping function has contexts.
let heritageFn;
class O {
#f = "O.#f";
static C = class C0 extends (class C1 extends (heritageFn = function () {
return (obj) => { return obj.#f; }
}) {}) {
#f = "C0.#f";
}
}
const o = new O;
const c = new O.C;
assertEquals(heritageFn()(o), "O.#f");
assertThrows(() => heritageFn()(c), TypeError);
}
......@@ -484,16 +484,6 @@
# https://bugs.chromium.org/p/v8/issues/detail?id=9049
'language/comments/hashbang/use-strict': [SKIP],
# https://bugs.chromium.org/p/v8/issues/detail?id=9229
'language/expressions/class/elements/syntax/early-errors/grammar-private-environment-on-class-heritage': [FAIL],
'language/expressions/class/elements/syntax/early-errors/grammar-private-environment-on-class-heritage-chained-usage': [FAIL],
'language/expressions/class/elements/syntax/early-errors/grammar-private-environment-on-class-heritage-function-expression': [FAIL],
'language/expressions/class/elements/syntax/early-errors/grammar-private-environment-on-class-heritage-recursive': [FAIL],
'language/statements/class/elements/syntax/early-errors/grammar-private-environment-on-class-heritage': [FAIL],
'language/statements/class/elements/syntax/early-errors/grammar-private-environment-on-class-heritage-chained-usage': [FAIL],
'language/statements/class/elements/syntax/early-errors/grammar-private-environment-on-class-heritage-function-expression': [FAIL],
'language/statements/class/elements/syntax/early-errors/grammar-private-environment-on-class-heritage-recursive': [FAIL],
# https://bugs.chromium.org/p/v8/issues/detail?id=8179
#
# These tests require exception handling support which is currently
......
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