Commit 1e6294d3 authored by Joyee Cheung's avatar Joyee Cheung Committed by V8 LUCI CQ

[class] initialize brand after super() in nested arrow function

Handle the case of nested super() by checking if the class scope
contains a private brand. In this case the ContextScope chain
is different from the actual context chain so this added back
the AddPrivateBrand() runtime function but with the additional
step of walking the context chain to get the correct class
context that will be stored as the value of the brand property
for the debugger.

Bug: v8:12354
Change-Id: Ieeb9b9d6372bfbb1a39c4c2dc9e9848e9109f02a
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3275137Reviewed-by: 's avatarShu-yu Guo <syg@chromium.org>
Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarMarja Hölttä <marja@chromium.org>
Commit-Queue: Joyee Cheung <joyee@igalia.com>
Cr-Commit-Position: refs/heads/main@{#79032}
parent a684b5df
......@@ -268,6 +268,14 @@ bool FunctionLiteral::private_name_lookup_skips_outer_class() const {
return scope()->private_name_lookup_skips_outer_class();
}
bool FunctionLiteral::class_scope_has_private_brand() const {
return scope()->class_scope_has_private_brand();
}
void FunctionLiteral::set_class_scope_has_private_brand(bool value) {
return scope()->set_class_scope_has_private_brand(value);
}
ObjectLiteralProperty::ObjectLiteralProperty(Expression* key, Expression* value,
Kind kind, bool is_computed_name)
: LiteralProperty(key, value, is_computed_name),
......
......@@ -2245,12 +2245,8 @@ class FunctionLiteral final : public Expression {
return HasStaticPrivateMethodsOrAccessorsField::decode(bit_field_);
}
void set_class_scope_has_private_brand(bool value) {
bit_field_ = ClassScopeHasPrivateBrandField::update(bit_field_, value);
}
bool class_scope_has_private_brand() const {
return ClassScopeHasPrivateBrandField::decode(bit_field_);
}
void set_class_scope_has_private_brand(bool value);
bool class_scope_has_private_brand() const;
bool private_name_lookup_skips_outer_class() const;
......@@ -2299,10 +2295,8 @@ class FunctionLiteral final : public Expression {
using HasDuplicateParameters = Pretenure::Next<bool, 1>;
using RequiresInstanceMembersInitializer =
HasDuplicateParameters::Next<bool, 1>;
using ClassScopeHasPrivateBrandField =
RequiresInstanceMembersInitializer::Next<bool, 1>;
using HasStaticPrivateMethodsOrAccessorsField =
ClassScopeHasPrivateBrandField::Next<bool, 1>;
RequiresInstanceMembersInitializer::Next<bool, 1>;
using HasBracesField = HasStaticPrivateMethodsOrAccessorsField::Next<bool, 1>;
using ShouldParallelCompileField = HasBracesField::Next<bool, 1>;
......
......@@ -197,7 +197,7 @@ ClassScope::ClassScope(IsolateT* isolate, Zone* zone,
: Scope(zone, CLASS_SCOPE, ast_value_factory, scope_info),
rare_data_and_is_parsing_heritage_(nullptr) {
set_language_mode(LanguageMode::kStrict);
if (scope_info->HasClassBrand()) {
if (scope_info->ClassScopeHasPrivateBrand()) {
Variable* brand =
LookupInScopeInfo(ast_value_factory->dot_brand_string(), this);
DCHECK_NOT_NULL(brand);
......@@ -280,6 +280,10 @@ DeclarationScope::DeclarationScope(Zone* zone, ScopeType scope_type,
DCHECK(!is_eval_scope());
sloppy_eval_can_extend_vars_ = true;
}
if (scope_info->ClassScopeHasPrivateBrand()) {
DCHECK(IsClassConstructor(function_kind()));
class_scope_has_private_brand_ = true;
}
}
Scope::Scope(Zone* zone, const AstRawString* catch_variable_name,
......@@ -327,6 +331,7 @@ void DeclarationScope::SetDefaults() {
was_lazily_parsed_ = false;
is_skipped_function_ = false;
preparse_data_builder_ = nullptr;
class_scope_has_private_brand_ = false;
#ifdef DEBUG
DeclarationScope* outer_declaration_scope =
outer_scope_ ? outer_scope_->GetDeclarationScope() : nullptr;
......@@ -1484,6 +1489,18 @@ DeclarationScope* Scope::GetReceiverScope() {
return scope->AsDeclarationScope();
}
DeclarationScope* Scope::GetConstructorScope() {
Scope* scope = this;
while (scope != nullptr && !scope->IsConstructorScope()) {
scope = scope->outer_scope();
}
if (scope == nullptr) {
return nullptr;
}
DCHECK(scope->IsConstructorScope());
return scope->AsDeclarationScope();
}
Scope* Scope::GetHomeObjectScope() {
Scope* scope = this;
while (scope != nullptr && !scope->is_home_object_scope()) {
......@@ -1551,6 +1568,11 @@ void Scope::ForEach(FunctionType callback) {
}
}
bool Scope::IsConstructorScope() const {
return is_declaration_scope() &&
IsClassConstructor(AsDeclarationScope()->function_kind());
}
bool Scope::IsOuterScopeOf(Scope* other) const {
Scope* scope = other;
while (scope) {
......@@ -1925,6 +1947,9 @@ void Scope::Print(int n) {
}
Indent(n1, "// ");
PrintF("%s\n", FunctionKind2String(scope->function_kind()));
if (scope->class_scope_has_private_brand()) {
Indent(n1, "// class scope has private brand\n");
}
}
if (num_stack_slots_ > 0) {
Indent(n1, "// ");
......
......@@ -459,6 +459,8 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
kDescend
};
bool IsConstructorScope() const;
// Check is this scope is an outer scope of the given scope.
bool IsOuterScopeOf(Scope* other) const;
......@@ -554,6 +556,11 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
// 'this' is bound, and what determines the function kind.
DeclarationScope* GetReceiverScope();
// Find the first constructor scope. Its outer scope is where the instance
// members that should be initialized right after super() is called
// are declared.
DeclarationScope* GetConstructorScope();
// Find the first class scope or object literal block scope. This is where
// 'super' is bound.
Scope* GetHomeObjectScope();
......@@ -1239,6 +1246,13 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
// to REPL_GLOBAL. Should only be called on REPL scripts.
void RewriteReplGlobalVariables();
void set_class_scope_has_private_brand(bool value) {
class_scope_has_private_brand_ = value;
}
bool class_scope_has_private_brand() const {
return class_scope_has_private_brand_;
}
private:
V8_INLINE void AllocateParameter(Variable* var, int index);
......@@ -1286,7 +1300,7 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
bool has_this_reference_ : 1;
bool has_this_declaration_ : 1;
bool needs_private_name_context_chain_recalc_ : 1;
bool class_scope_has_private_brand_ : 1;
// If the scope is a function scope, this is the function kind.
FunctionKind function_kind_;
......
......@@ -1594,6 +1594,9 @@ void SharedFunctionInfo::SharedFunctionInfoPrint(std::ostream& os) {
if (has_static_private_methods_or_accessors()) {
os << "\n - has_static_private_methods_or_accessors";
}
if (private_name_lookup_skips_outer_class()) {
os << "\n - private_name_lookup_skips_outer_class";
}
os << "\n - kind: " << kind();
os << "\n - syntax kind: " << syntax_kind();
os << "\n - function_map_index: " << function_map_index();
......@@ -2388,7 +2391,7 @@ void ScopeInfo::ScopeInfoPrint(std::ostream& os) {
if (HasReceiver()) {
os << "\n - receiver: " << ReceiverVariableBits::decode(flags);
}
if (HasClassBrand()) os << "\n - has class brand";
if (ClassScopeHasPrivateBrand()) os << "\n - class scope has private brand";
if (HasSavedClassVariable()) os << "\n - has saved class variable";
if (HasNewTarget()) os << "\n - needs new target";
if (HasFunctionName()) {
......@@ -2397,6 +2400,8 @@ void ScopeInfo::ScopeInfoPrint(std::ostream& os) {
}
if (IsAsmModule()) os << "\n - asm module";
if (HasSimpleParameters()) os << "\n - simple parameters";
if (PrivateNameLookupSkipsOuterClass())
os << "\n - private name lookup skips outer class";
os << "\n - function kind: " << function_kind();
if (HasOuterScopeInfo()) {
os << "\n - outer scope info: " << Brief(OuterScopeInfo());
......
......@@ -1451,7 +1451,9 @@ void BytecodeGenerator::GenerateBytecodeBody() {
// The derived constructor case is handled in VisitCallSuper.
if (IsBaseConstructor(function_kind())) {
if (literal->class_scope_has_private_brand()) {
BuildPrivateBrandInitialization(builder()->Receiver());
ClassScope* scope = info()->scope()->outer_scope()->AsClassScope();
DCHECK_NOT_NULL(scope->brand());
BuildPrivateBrandInitialization(builder()->Receiver(), scope->brand());
}
if (literal->requires_instance_members_initializer()) {
......@@ -2892,18 +2894,33 @@ void BytecodeGenerator::BuildInvalidPropertyAccess(MessageTemplate tmpl,
.Throw();
}
void BytecodeGenerator::BuildPrivateBrandInitialization(Register receiver) {
Variable* brand = info()->scope()->outer_scope()->AsClassScope()->brand();
void BytecodeGenerator::BuildPrivateBrandInitialization(Register receiver,
Variable* brand) {
BuildVariableLoad(brand, HoleCheckMode::kElided);
int depth = execution_context()->ContextChainDepth(brand->scope());
ContextScope* class_context = execution_context()->Previous(depth);
BuildVariableLoad(brand, HoleCheckMode::kElided);
Register brand_reg = register_allocator()->NewRegister();
FeedbackSlot slot = feedback_spec()->AddKeyedDefineOwnICSlot();
builder()
->StoreAccumulatorInRegister(brand_reg)
.LoadAccumulatorWithRegister(class_context->reg())
.DefineKeyedProperty(receiver, brand_reg, feedback_index(slot));
if (class_context) {
Register brand_reg = register_allocator()->NewRegister();
FeedbackSlot slot = feedback_spec()->AddKeyedDefineOwnICSlot();
builder()
->StoreAccumulatorInRegister(brand_reg)
.LoadAccumulatorWithRegister(class_context->reg())
.DefineKeyedProperty(receiver, brand_reg, feedback_index(slot));
} else {
// We are in the slow case where super() is called from a nested
// arrow function or a eval(), so the class scope context isn't
// tracked in a context register in the stack, and we have to
// walk the context chain from the runtime to find it.
DCHECK_NE(info()->literal()->scope()->outer_scope(), brand->scope());
RegisterList brand_args = register_allocator()->NewRegisterList(4);
builder()
->StoreAccumulatorInRegister(brand_args[1])
.MoveRegister(receiver, brand_args[0])
.MoveRegister(execution_context()->reg(), brand_args[2])
.LoadLiteral(Smi::FromInt(depth))
.StoreAccumulatorInRegister(brand_args[3])
.CallRuntime(Runtime::kAddPrivateBrand, brand_args);
}
}
void BytecodeGenerator::BuildInstanceMemberInitialization(Register constructor,
......@@ -5633,8 +5650,25 @@ void BytecodeGenerator::VisitCallSuper(Call* expr) {
Register instance = register_allocator()->NewRegister();
builder()->StoreAccumulatorInRegister(instance);
if (info()->literal()->class_scope_has_private_brand()) {
BuildPrivateBrandInitialization(instance);
// The constructor scope always needs ScopeInfo, so we are certain that
// the first constructor scope found in the outer scope chain is the
// scope that we are looking for for this super() call.
// Note that this doesn't necessarily mean that the constructor needs
// a context, if it doesn't this would get handled specially in
// BuildPrivateBrandInitialization().
DeclarationScope* constructor_scope = info()->scope()->GetConstructorScope();
// We can rely on the class_scope_has_private_brand bit to tell if the
// constructor needs private brand initialization, and if that's
// the case we are certain that its outer class scope requires a context to
// keep the brand variable, so we can just get the brand variable
// from the outer scope.
if (constructor_scope->class_scope_has_private_brand()) {
DCHECK(constructor_scope->outer_scope()->is_class_scope());
ClassScope* class_scope = constructor_scope->outer_scope()->AsClassScope();
DCHECK_NOT_NULL(class_scope->brand());
Variable* brand = class_scope->brand();
BuildPrivateBrandInitialization(instance, brand);
}
// The derived constructor has the correct bit set always, so we
......
......@@ -333,7 +333,7 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
void VisitClassLiteral(ClassLiteral* expr, Register name);
void VisitNewTargetVariable(Variable* variable);
void VisitThisFunctionVariable(Variable* variable);
void BuildPrivateBrandInitialization(Register receiver);
void BuildPrivateBrandInitialization(Register receiver, Variable* brand);
void BuildInstanceMemberInitialization(Register constructor,
Register instance);
void BuildGeneratorObjectVariableInitialization();
......
......@@ -138,9 +138,11 @@ Handle<ScopeInfo> ScopeInfo::Create(IsolateT* isolate, Zone* zone, Scope* scope,
function_name_info = VariableAllocationInfo::NONE;
}
const bool has_brand = scope->is_class_scope()
? scope->AsClassScope()->brand() != nullptr
: false;
const bool has_brand =
scope->is_class_scope()
? scope->AsClassScope()->brand() != nullptr
: scope->IsConstructorScope() &&
scope->AsDeclarationScope()->class_scope_has_private_brand();
const bool should_save_class_variable_index =
scope->is_class_scope()
? scope->AsClassScope()->should_save_class_variable_index()
......@@ -219,7 +221,7 @@ Handle<ScopeInfo> ScopeInfo::Create(IsolateT* isolate, Zone* zone, Scope* scope,
LanguageModeBit::encode(scope->language_mode()) |
DeclarationScopeBit::encode(scope->is_declaration_scope()) |
ReceiverVariableBits::encode(receiver_info) |
HasClassBrandBit::encode(has_brand) |
ClassScopeHasPrivateBrandBit::encode(has_brand) |
HasSavedClassVariableBit::encode(should_save_class_variable_index) |
HasNewTargetBit::encode(has_new_target) |
FunctionVariableBits::encode(function_name_info) |
......@@ -404,6 +406,7 @@ Handle<ScopeInfo> ScopeInfo::Create(IsolateT* isolate, Zone* zone, Scope* scope,
DCHECK_EQ(index, scope_info_handle->length());
DCHECK_EQ(parameter_count, scope_info_handle->ParameterCount());
DCHECK_EQ(scope->num_heap_slots(), scope_info_handle->ContextLength());
return scope_info_handle;
}
......@@ -432,7 +435,7 @@ Handle<ScopeInfo> ScopeInfo::CreateForWithScope(
LanguageModeBit::encode(LanguageMode::kSloppy) |
DeclarationScopeBit::encode(false) |
ReceiverVariableBits::encode(VariableAllocationInfo::NONE) |
HasClassBrandBit::encode(false) |
ClassScopeHasPrivateBrandBit::encode(false) |
HasSavedClassVariableBit::encode(false) | HasNewTargetBit::encode(false) |
FunctionVariableBits::encode(VariableAllocationInfo::NONE) |
IsAsmModuleBit::encode(false) | HasSimpleParametersBit::encode(true) |
......@@ -507,7 +510,7 @@ Handle<ScopeInfo> ScopeInfo::CreateForBootstrapping(Isolate* isolate,
DeclarationScopeBit::encode(true) |
ReceiverVariableBits::encode(is_script ? VariableAllocationInfo::CONTEXT
: VariableAllocationInfo::UNUSED) |
HasClassBrandBit::encode(false) |
ClassScopeHasPrivateBrandBit::encode(false) |
HasSavedClassVariableBit::encode(false) | HasNewTargetBit::encode(false) |
FunctionVariableBits::encode(is_empty_function
? VariableAllocationInfo::UNUSED
......@@ -717,8 +720,8 @@ bool ScopeInfo::HasAllocatedReceiver() const {
allocation == VariableAllocationInfo::CONTEXT;
}
bool ScopeInfo::HasClassBrand() const {
return HasClassBrandBit::decode(Flags());
bool ScopeInfo::ClassScopeHasPrivateBrand() const {
return ClassScopeHasPrivateBrandBit::decode(Flags());
}
bool ScopeInfo::HasSavedClassVariable() const {
......
......@@ -91,8 +91,11 @@ class ScopeInfo : public TorqueGeneratedScopeInfo<ScopeInfo, HeapObject> {
// or context-allocated?
bool HasAllocatedReceiver() const;
// Does this scope has class brand (for private methods)?
bool HasClassBrand() const;
// Does this scope has class brand (for private methods)? If it's a class
// scope, this indicates whether the class has a private brand. If it's a
// constructor scope, this indicates whther it needs to initialize the
// brand.
bool ClassScopeHasPrivateBrand() const;
// Does this scope contain a saved class variable for checking receivers of
// static private methods?
......
......@@ -57,7 +57,10 @@ bitfield struct ScopeFlags extends uint31 {
language_mode: LanguageMode: 1 bit;
declaration_scope: bool: 1 bit;
receiver_variable: VariableAllocationInfo: 2 bit;
has_class_brand: bool: 1 bit;
// In class scope, this indicates whether the class has a private brand.
// In constructor scope, this indicates whether the constructor needs
// private brand initialization.
class_scope_has_private_brand: bool: 1 bit;
has_saved_class_variable: bool: 1 bit;
has_new_target: bool: 1 bit;
// TODO(cbruni): Combine with function variable field when only storing the
......
......@@ -478,6 +478,43 @@ RUNTIME_FUNCTION(Runtime_AddDictionaryProperty) {
return *value;
}
RUNTIME_FUNCTION(Runtime_AddPrivateBrand) {
HandleScope scope(isolate);
DCHECK_EQ(args.length(), 4);
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, receiver, 0);
CONVERT_ARG_HANDLE_CHECKED(Symbol, brand, 1);
CONVERT_ARG_HANDLE_CHECKED(Context, context, 2);
CONVERT_ARG_HANDLE_CHECKED(Smi, depth_smi, 3);
DCHECK(brand->is_private_name());
LookupIterator it(isolate, receiver, brand, LookupIterator::OWN);
if (it.IsFound()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewTypeError(MessageTemplate::kInvalidPrivateBrandReinitialization,
brand));
}
PropertyAttributes attributes =
static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY);
// Look for the context in |depth| in the context chain to store it
// in the instance with the brand variable as key, which is needed by
// the debugger for retrieving names of private methods.
int depth = depth_smi->value();
DCHECK_GE(depth, 0);
for (; depth > 0; depth--) {
context =
handle(Context::cast(context->get(Context::PREVIOUS_INDEX)), isolate);
}
DCHECK_EQ(context->scope_info().scope_type(), ScopeType::CLASS_SCOPE);
CHECK(Object::AddDataProperty(&it, context, attributes, Just(kDontThrow),
StoreOrigin::kMaybeKeyed)
.FromJust());
return *receiver;
}
// ES6 section 19.1.2.2 Object.create ( O [ , Properties ] )
// TODO(verwaest): Support the common cases with precached map directly in
// an Object.create stub.
......
......@@ -286,6 +286,7 @@ namespace internal {
#define FOR_EACH_INTRINSIC_OBJECT(F, I) \
F(AddDictionaryProperty, 3, 1) \
F(AddPrivateBrand, 4, 1) \
F(AllocateHeapNumber, 0, 1) \
F(CollectTypeProfile, 3, 1) \
F(CompleteInobjectSlackTrackingForMap, 1, 1) \
......
......@@ -1478,7 +1478,7 @@ Handle<ScopeInfo> WebSnapshotDeserializer::CreateScopeInfo(
ScopeInfo::LanguageModeBit::encode(LanguageMode::kStrict) |
ScopeInfo::DeclarationScopeBit::encode(false) |
ScopeInfo::ReceiverVariableBits::encode(VariableAllocationInfo::NONE) |
ScopeInfo::HasClassBrandBit::encode(false) |
ScopeInfo::ClassScopeHasPrivateBrandBit::encode(false) |
ScopeInfo::HasSavedClassVariableBit::encode(false) |
ScopeInfo::HasNewTargetBit::encode(false) |
ScopeInfo::FunctionVariableBits::encode(VariableAllocationInfo::NONE) |
......
......@@ -143,3 +143,148 @@ constant pool: [
handlers: [
]
---
snippet: "
var test;
class F extends class {} {
#method() { }
constructor() {
(test = () => super())();
this.#method();
}
};
new F;
"
frame size: 9
parameter count: 1
bytecode array length: 58
bytecodes: [
/* 89 S> */ B(LdaImmutableCurrentContextSlot), U8(4),
B(GetSuperConstructor), R(1),
B(ThrowIfNotSuperConstructor), R(1),
B(Star0),
B(LdaImmutableCurrentContextSlot), U8(3),
/* 89 E> */ B(Construct), R(1), R(0), U8(0), U8(0),
B(Star2),
B(LdaCurrentContextSlot), U8(2),
B(ThrowSuperAlreadyCalledIfNotHole),
B(Ldar), R(2),
B(StaCurrentContextSlot), U8(2),
B(LdaImmutableContextSlot), R(context), U8(3), U8(1),
B(Star4),
B(LdaSmi), I8(1),
B(Star6),
B(Mov), R(2), R(3),
B(Mov), R(context), R(5),
B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(3), U8(4),
B(LdaNamedProperty), R(0), U8(0), U8(2),
B(JumpIfUndefined), U8(10),
B(Star8),
B(CallProperty0), R(8), R(2), U8(4),
B(Mov), R(2), R(7),
B(Ldar), R(2),
/* 96 S> */ B(Return),
]
constant pool: [
SYMBOL_TYPE,
]
handlers: [
]
---
snippet: "
var test;
class G extends class {} {
#method() { }
constructor() {
test = () => super();
test();
this.#method();
}
};
new G();
"
frame size: 9
parameter count: 1
bytecode array length: 58
bytecodes: [
/* 88 S> */ B(LdaImmutableCurrentContextSlot), U8(4),
B(GetSuperConstructor), R(1),
B(ThrowIfNotSuperConstructor), R(1),
B(Star0),
B(LdaImmutableCurrentContextSlot), U8(3),
/* 88 E> */ B(Construct), R(1), R(0), U8(0), U8(0),
B(Star2),
B(LdaCurrentContextSlot), U8(2),
B(ThrowSuperAlreadyCalledIfNotHole),
B(Ldar), R(2),
B(StaCurrentContextSlot), U8(2),
B(LdaImmutableContextSlot), R(context), U8(3), U8(1),
B(Star4),
B(LdaSmi), I8(1),
B(Star6),
B(Mov), R(2), R(3),
B(Mov), R(context), R(5),
B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(3), U8(4),
B(LdaNamedProperty), R(0), U8(0), U8(2),
B(JumpIfUndefined), U8(10),
B(Star8),
B(CallProperty0), R(8), R(2), U8(4),
B(Mov), R(2), R(7),
B(Ldar), R(2),
/* 95 S> */ B(Return),
]
constant pool: [
SYMBOL_TYPE,
]
handlers: [
]
---
snippet: "
var test;
class H extends class {} {
#method() { }
constructor(str) {
eval(str);
this.#method();
}
};
new test('test = () => super(); test()');
"
frame size: 9
parameter count: 1
bytecode array length: 58
bytecodes: [
/* 88 S> */ B(LdaImmutableCurrentContextSlot), U8(4),
B(GetSuperConstructor), R(1),
B(ThrowIfNotSuperConstructor), R(1),
B(Star0),
B(LdaImmutableCurrentContextSlot), U8(3),
/* 88 E> */ B(Construct), R(1), R(0), U8(0), U8(0),
B(Star2),
B(LdaCurrentContextSlot), U8(2),
B(ThrowSuperAlreadyCalledIfNotHole),
B(Ldar), R(2),
B(StaCurrentContextSlot), U8(2),
B(LdaImmutableContextSlot), R(context), U8(3), U8(1),
B(Star4),
B(LdaSmi), I8(1),
B(Star6),
B(Mov), R(2), R(3),
B(Mov), R(context), R(5),
B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(3), U8(4),
B(LdaNamedProperty), R(0), U8(0), U8(2),
B(JumpIfUndefined), U8(10),
B(Star8),
B(CallProperty0), R(8), R(2), U8(4),
B(Mov), R(2), R(7),
B(Ldar), R(2),
/* 95 S> */ B(Return),
]
constant pool: [
SYMBOL_TYPE,
]
handlers: [
]
......@@ -2604,7 +2604,38 @@ TEST(PrivateMethodAccess) {
"}\n"
"\n"
"var test = D;\n"
"new test;\n"};
"new test;\n",
"var test;\n"
"class F extends class {} {\n"
" #method() { }\n"
" constructor() {\n"
" (test = () => super())();\n"
" this.#method();\n"
" }\n"
"};\n"
"new F;\n",
"var test;\n"
"class G extends class {} {\n"
" #method() { }\n"
" constructor() {\n"
" test = () => super();\n"
" test();\n"
" this.#method();\n"
" }\n"
"};\n"
"new G();\n",
"var test;\n"
"class H extends class {} {\n"
" #method() { }\n"
" constructor(str) {\n"
" eval(str);\n"
" this.#method();\n"
" }\n"
"};\n"
"new test('test = () => super(); test()');\n"};
CHECK(CompareTexts(BuildActual(printer, snippets),
LoadGolden("PrivateMethodAccess.golden")));
......
Test getting private class methods from an instance that calls nested super()
Running test: testScopesPaused
properties after super() is called in IIFE
[
[0] : {
name : #b
value : {
className : Function
description : #b() {}
objectId : <objectId>
type : function
}
}
]
privateProperties after super() is called in arrow function
[
[0] : {
name : #b
value : {
className : Function
description : #b() {}
objectId : <objectId>
type : function
}
}
[1] : {
get : {
className : Function
description : get #c() {}
objectId : <objectId>
type : function
}
name : #c
}
]
privateProperties after super() is called in eval()
[
[0] : {
name : #b
value : {
className : Function
description : #b() {}
objectId : <objectId>
type : function
}
}
[1] : {
get : {
className : Function
description : get #c() {}
objectId : <objectId>
type : function
}
name : #c
}
[2] : {
name : #d
set : {
className : Function
description : set #d(val) {}
objectId : <objectId>
type : function
}
}
]
// Copyright 2021 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 { session, contextGroup, Protocol } = InspectorTest.start(
"Test getting private class methods from an instance that calls nested super()"
);
contextGroup.addScript(`
function run() {
class A {}
class B extends A {
#b() {}
constructor() {
(() => super())();
}
test() { debugger; }
};
(new B()).test();
class C extends B {
get #c() {}
constructor() {
const callSuper = () => super();
callSuper();
}
test() { debugger; }
};
(new C()).test();
class D extends C {
set #d(val) {}
constructor(str) {
eval(str);
}
test() { debugger; }
};
(new D('super();')).test();
}`);
InspectorTest.runAsyncTestSuite([
async function testScopesPaused() {
Protocol.Debugger.enable();
Protocol.Runtime.evaluate({ expression: "run()" });
let {
params: { callFrames }
} = await Protocol.Debugger.oncePaused(); // inside B constructor
let frame = callFrames[0];
let { result } = await Protocol.Runtime.getProperties({
objectId: frame.this.objectId
});
InspectorTest.log('properties after super() is called in IIFE');
InspectorTest.logMessage(result.privateProperties);
Protocol.Debugger.resume();
({ params: { callFrames } }
= await Protocol.Debugger.oncePaused()); // inside C constructor
frame = callFrames[0];
({ result } = await Protocol.Runtime.getProperties({
objectId: frame.this.objectId
}));
InspectorTest.log('privateProperties after super() is called in arrow function');
InspectorTest.logMessage(result.privateProperties);
Protocol.Debugger.resume();
({ params: { callFrames } }
= await Protocol.Debugger.oncePaused()); // inside D constructor
frame = callFrames[0];
({ result } = await Protocol.Runtime.getProperties({
objectId: frame.this.objectId
}));
InspectorTest.log('privateProperties after super() is called in eval()');
InspectorTest.logMessage(result.privateProperties);
Protocol.Debugger.resume();
Protocol.Debugger.disable();
}
]);
// 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.
"use strict";
// Tests that brand initialization works when super() is called in a nested
// arrow function or in eval().
// IIFE nested super().
{
class A extends class {} {
#method() { }
constructor() {
(() => super())();
}
test() { this.#method(); }
check() { return #method in this; }
}
const a = new A();
a.test();
assertTrue(a.check());
}
// Non-IIFE nested super().
{
class A extends class {} {
#method() { }
constructor() {
const callSuper = () => super();
callSuper();
}
test() { this.#method(); }
check() { return #method in this; }
}
const a = new A();
a.test();
assertTrue(a.check());
}
// Eval'ed nested super().
{
class A extends class {} {
#method() { }
constructor(str) {
eval(str);
}
test() { this.#method(); }
check() { return #method in this; }
}
const a = new A("super()");
a.test();
assertTrue(a.check());
}
// Test that private brands don't leak into class in heritage
// position with the class scope optimized away.
{
class A extends class B extends class {} {
constructor() { (() => super())(); }
static get B() { return B; }
} {
#method() {}
static run(obj) { obj.#method(); }
static get B() { return super.B; }
}
const b = new (A.B)();
assertThrows(() => A.run(b));
}
{
class C {
#c() { }
#field = 1;
static A = class A extends class B extends Object {
constructor() {
(() => super())();
}
field(obj) { return obj.#field; }
} {};
static run(obj) { obj.#c(); }
}
const a = new (C.A);
assertThrows(() => C.run(a));
const c = new C;
assertEquals(a.field(c), 1);
}
{
class C {
#c() { }
#field = 1;
static A = class A extends class B extends Object {
constructor() {
(() => {
eval("super()");
})();
}
field(obj) { return obj.#field; }
} {};
static run(obj) { obj.#c(); }
}
const a = new (C.A);
assertThrows(() => C.run(a));
const c = new C;
assertEquals(a.field(c), 1);
}
{
class C {
#c() { }
#field = 1;
static A = class A extends class B extends Object {
constructor() {
(() => {
{
super();
}
})();
}
field(obj) { return obj.#field; }
} {};
static run(obj) { obj.#c(); }
}
const a = new (C.A);
assertThrows(() => C.run(a));
const c = new C;
assertEquals(a.field(c), 1);
}
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