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

Revert "[class] implement reparsing of class instance member initializers"

This reverts commit 91f08378.

Reason for revert: It's a fairly big change, and the clusterfuzz
found some bugs. Will reland with the fix after M98 branch point.

Original change's description:
> [class] implement reparsing of class instance member initializers
>
> Previously, since the source code for the synthetic class instance
> member initializer function was recorded as the span from the first
> initializer to the last initializer, there was no way to reparse the
> class and recompile the initializer function. It was working for
> most use cases because the code for the initializer function was
> generated eagarly and it was usually alive as long as the class was
> alive, so the initializer wouldn't normally be lazily parsed. This
> didn't work, however, when the class was snapshotted with
> v8::SnapshotCreator::FunctionCodeHandling::kClear,
> becuase then we needed to recompile the initializer when the class
> was instantiated. This patch implements the reparsing so that
> these classes can work with FunctionCodeHandling::kClear.
>
> This patch refactors ParserBase::ParseClassLiteral() so that we can
> reuse it for both parsing the class body normally and reparsing it
> to collect initializers. When reparsing the synthetic initializer
> function, we rewind the scanner to the beginning of the class, and
> parse the class body to collect the initializers. During the
> reparsing, field initializers are parsed with the full parser while
> methods of the class are pre-parsed.
>
> A few notable changes:
>
> - Extended the source range of the initializer function to cover the
>   entire class so that we can rewind the scanner to parse the class
>   body to collect initializers (previously, it starts from the first
>   field initializer and ends at the last initializer). This resulted
>   some expectation changes in the debugger tests, though the
>   initializers remain debuggable.
> - A temporary ClassScope is created during reparsing. After the class
>   is reparsed, we use the information from the ScopeInfo to update
>   the allocated indices of the variables in the ClassScope.
>
> Bug: v8:10704
> Change-Id: Ifb6431a1447d8844f2a548283d59158742fe9027
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2988830
> Reviewed-by: Leszek Swirski <leszeks@chromium.org>
> Reviewed-by: Toon Verwaest <verwaest@chromium.org>
> Commit-Queue: Joyee Cheung <joyee@igalia.com>
> Cr-Commit-Position: refs/heads/main@{#78299}

Bug: v8:10704
Change-Id: I039cb728ebf0ada438a8f26c7d2c2547dbe3bf2d
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3325328
Auto-Submit: Joyee Cheung <joyee@igalia.com>
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Reviewed-by: 's avatarMarja Hölttä <marja@chromium.org>
Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/main@{#78315}
parent e8ea622d
......@@ -521,15 +521,6 @@ template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
DeclarationScope* script_scope, AstValueFactory* ast_value_factory,
DeserializationMode deserialization_mode);
#ifdef DEBUG
bool Scope::IsReparsedMemberInitializerScope() const {
return is_declaration_scope() &&
IsClassMembersInitializerFunction(
AsDeclarationScope()->function_kind()) &&
outer_scope()->AsClassScope()->is_reparsed_class_scope();
}
#endif
DeclarationScope* Scope::AsDeclarationScope() {
DCHECK(is_declaration_scope());
return static_cast<DeclarationScope*>(this);
......@@ -673,10 +664,8 @@ bool DeclarationScope::Analyze(ParseInfo* info) {
// We are compiling one of four cases:
// 1) top-level code,
// 2) a function/eval/module on the top-level
// 4) a class member initializer function scope
// 3) 4 function/eval in a scope that was already resolved.
// 3) a function/eval in a scope that was already resolved.
DCHECK(scope->is_script_scope() || scope->outer_scope()->is_script_scope() ||
scope->IsReparsedMemberInitializerScope() ||
scope->outer_scope()->already_resolved_);
// The outer scope is never lazy.
......@@ -1904,8 +1893,6 @@ void Scope::Print(int n) {
if (scope->needs_private_name_context_chain_recalc()) {
Indent(n1, "// needs #-name context chain recalc\n");
}
Indent(n1, "// ");
PrintF("%s\n", FunctionKind2String(scope->function_kind()));
}
if (num_stack_slots_ > 0) {
Indent(n1, "// ");
......@@ -2695,55 +2682,6 @@ bool IsComplementaryAccessorPair(VariableMode a, VariableMode b) {
}
}
void ClassScope::ReplaceReparsedClassScope(Isolate* isolate,
AstValueFactory* ast_value_factory,
ClassScope* old_scope) {
DCHECK_EQ(outer_scope_, old_scope->outer_scope());
Scope* outer = outer_scope_;
outer->RemoveInnerScope(old_scope);
// The outer scope should only have this deserialized inner scope,
// otherwise we have to update the sibling scopes.
DCHECK_EQ(outer->inner_scope_, this);
DCHECK_NULL(sibling_);
DCHECK_NULL(old_scope->inner_scope_);
Handle<ScopeInfo> scope_info = old_scope->scope_info_;
DCHECK(!scope_info.is_null());
DCHECK(!scope_info->IsEmpty());
// Restore variable allocation results for context-allocated variables in
// the class scope from ScopeInfo, so that we don't need to run
// resolution and allocation on these variables again when generating
// code for the initializer function.
int context_local_count = scope_info->ContextLocalCount();
int context_header_length = scope_info->ContextHeaderLength();
DisallowGarbageCollection no_gc;
for (int i = 0; i < context_local_count; ++i) {
int slot_index = context_header_length + i;
DCHECK_LT(slot_index, scope_info->ContextLength());
String name = scope_info->ContextLocalName(i);
const AstRawString* string = ast_value_factory->GetString(
name, SharedStringAccessGuardIfNeeded(isolate));
Variable* var = nullptr;
var = string->IsPrivateName() ? LookupLocalPrivateName(string)
: LookupLocal(string);
DCHECK_NOT_NULL(var);
var->AllocateTo(VariableLocation::CONTEXT, slot_index);
}
scope_info_ = scope_info;
// Set this bit so that DelcarationScope::Analyze recognizes
// the reparsed instance member initializer scope.
#ifdef DEBUG
is_reparsed_class_scope_ = true;
#endif
}
Variable* ClassScope::DeclarePrivateName(const AstRawString* name,
VariableMode mode,
IsStaticFlag is_static_flag,
......
......@@ -424,9 +424,6 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
return num_heap_slots() > 0;
}
#ifdef DEBUG
bool IsReparsedMemberInitializerScope() const;
#endif
// Use Scope::ForEach for depth first traversal of scopes.
// Before:
// void Scope::VisitRecursively() {
......@@ -1477,13 +1474,6 @@ class V8_EXPORT_PRIVATE ClassScope : public Scope {
should_save_class_variable_index_ = true;
}
void ReplaceReparsedClassScope(Isolate* isolate,
AstValueFactory* ast_value_factory,
ClassScope* old_scope);
#ifdef DEBUG
bool is_reparsed_class_scope() const { return is_reparsed_class_scope_; }
#endif
private:
friend class Scope;
friend class PrivateNameScopeIterator;
......@@ -1529,9 +1519,6 @@ class V8_EXPORT_PRIVATE ClassScope : public Scope {
// This is only maintained during reparsing, restored from the
// preparsed data.
bool should_save_class_variable_index_ = false;
#ifdef DEBUG
bool is_reparsed_class_scope_ = false;
#endif
};
// Iterate over the private name scope chain. The iteration proceeds from the
......
......@@ -196,7 +196,7 @@
V(_, dot_home_object_string, ".home_object") \
V(_, dot_result_string, ".result") \
V(_, dot_repl_result_string, ".repl_result") \
V(_, dot_static_home_object_string, ".static_home_object") \
V(_, dot_static_home_object_string, "._static_home_object") \
V(_, dot_string, ".") \
V(_, dot_switch_tag_string, ".switch_tag") \
V(_, dotAll_string, "dotAll") \
......
......@@ -2813,7 +2813,6 @@ void BytecodeGenerator::BuildClassProperty(ClassLiteral::Property* property) {
// Private methods are not initialized in BuildClassProperty.
DCHECK_IMPLIES(property->is_private(),
property->kind() == ClassLiteral::Property::FIELD);
builder()->SetExpressionPosition(property->key());
bool is_literal_store = property->key()->IsPropertyName() &&
!property->is_computed_name() &&
......
......@@ -1238,10 +1238,6 @@ class ParserBase {
Scanner::Location class_name_location,
bool name_is_strict_reserved,
int class_token_pos);
ExpressionT DoParseClassLiteral(ClassScope* class_scope, IdentifierT name,
Scanner::Location class_name_location,
bool is_anonymous, int class_token_pos);
ExpressionT ParseTemplateLiteral(ExpressionT tag, int start, bool tagged);
ExpressionT ParseSuperExpression();
ExpressionT ParseImportExpressions();
......@@ -2526,6 +2522,8 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseMemberInitializer(
if (initializer_scope == nullptr) {
initializer_scope = NewFunctionScope(function_kind);
// TODO(gsathya): Make scopes be non contiguous.
initializer_scope->set_start_position(beg_pos);
initializer_scope->SetLanguageMode(LanguageMode::kStrict);
}
......@@ -2540,13 +2538,8 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseMemberInitializer(
initializer = factory()->NewUndefinedLiteral(kNoSourcePosition);
}
initializer_scope->set_end_position(end_position());
if (is_static) {
// For the instance initializer, we will save the positions
// later with the positions of the class body so that we can reparse
// it later.
// TODO(joyee): Make scopes be non contiguous.
initializer_scope->set_start_position(beg_pos);
initializer_scope->set_end_position(end_position());
class_info->static_elements_scope = initializer_scope;
class_info->has_static_elements = true;
} else {
......@@ -4688,15 +4681,6 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseClassLiteral(
}
ClassScope* class_scope = NewClassScope(scope(), is_anonymous);
return DoParseClassLiteral(class_scope, name, class_name_location,
is_anonymous, class_token_pos);
}
template <typename Impl>
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::DoParseClassLiteral(
ClassScope* class_scope, IdentifierT name,
Scanner::Location class_name_location, bool is_anonymous,
int class_token_pos) {
BlockState block_state(&scope_, class_scope);
RaiseLanguageMode(LanguageMode::kStrict);
......@@ -4783,12 +4767,6 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::DoParseClassLiteral(
Expect(Token::RBRACE);
int end_pos = end_position();
class_scope->set_end_position(end_pos);
if (class_info.instance_members_scope != nullptr) {
// Use the positions of the class body for the instance initializer
// function so that we can reparse it later.
class_info.instance_members_scope->set_start_position(class_token_pos);
class_info.instance_members_scope->set_end_position(end_pos);
}
VariableProxy* unresolvable = class_scope->ResolvePrivateNamesPartially();
if (unresolvable != nullptr) {
......
......@@ -863,13 +863,11 @@ void Parser::ParseFunction(Isolate* isolate, ParseInfo* info,
// 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 = DoParseDeserializedFunction(
isolate, shared_info, info, start_position, end_position,
function_literal_id, info->function_name());
result = DoParseFunction(isolate, info, start_position, end_position,
function_literal_id, info->function_name());
} else {
result = DoParseDeserializedFunction(
isolate, shared_info, info, start_position, end_position,
function_literal_id, info->function_name());
result = DoParseFunction(isolate, info, start_position, end_position,
function_literal_id, info->function_name());
}
MaybeProcessSourceRanges(info, result, stack_limit_);
if (result != nullptr) {
......@@ -1032,103 +1030,6 @@ FunctionLiteral* Parser::DoParseFunction(Isolate* isolate, ParseInfo* info,
return result;
}
FunctionLiteral* Parser::DoParseDeserializedFunction(
Isolate* isolate, Handle<SharedFunctionInfo> shared_info, ParseInfo* info,
int start_position, int end_position, int function_literal_id,
const AstRawString* raw_name) {
if (flags().function_kind() !=
FunctionKind::kClassMembersInitializerFunction) {
return DoParseFunction(isolate, info, start_position, end_position,
function_literal_id, raw_name);
}
// Reparse the outer class while skipping the non-fields to get a list of
// ClassLiteralProperty and create a InitializeClassMembersStatement for
// the synthetic instance initializer function.
FunctionLiteral* result = ParseClassForInstanceMemberInitialization(
isolate, original_scope_->AsClassScope(), start_position,
function_literal_id);
DCHECK_EQ(result->kind(), FunctionKind::kClassMembersInitializerFunction);
DCHECK_EQ(result->function_literal_id(), function_literal_id);
DCHECK_EQ(result->end_position(), shared_info->EndPosition());
// The private_name_lookup_skips_outer_class bit should be set by
// PostProcessParseResult() during scope analysis later.
return result;
}
FunctionLiteral* Parser::ParseClassForInstanceMemberInitialization(
Isolate* isolate, ClassScope* original_scope, int initializer_pos,
int initializer_id) {
int class_token_pos = initializer_pos;
// Insert a FunctionState with the closest outer Declaration scope
DeclarationScope* nearest_decl_scope = original_scope->GetDeclarationScope();
DCHECK_NOT_NULL(nearest_decl_scope);
FunctionState function_state(&function_state_, &scope_, nearest_decl_scope);
// We will reindex the function literals later.
ResetFunctionLiteralId();
// We preparse the class members that are not fields with initializers
// in order to collect the function literal ids.
ParsingModeScope mode(this, PARSE_LAZILY);
ExpressionParsingScope no_expression_scope(impl());
// We will reparse the entire class because we want to know if
// the class is anonymous.
// When the function is a kClassMembersInitializerFunction, we record the
// source range of the entire class as its positions in its SFI, so at this
// point the scanner should be rewound to the position of the class token.
DCHECK_EQ(peek(), Token::CLASS);
Expect(Token::CLASS);
const AstRawString* class_name = NullIdentifier();
const AstRawString* variable_name = NullIdentifier();
// It's a reparse so we don't need to check for default export or
// whether the names are reserved.
if (peek() == Token::EXTENDS || peek() == Token::LBRACE) {
GetDefaultStrings(&class_name, &variable_name);
} else {
class_name = ParseIdentifier();
variable_name = class_name;
}
bool is_anonymous = class_name == nullptr || class_name->IsEmpty();
// Create a new ClassScope for the parser to create the inner scopes,
// the variable resolution would be done in the original scope, however.
// TODO(joyee): see if we can reset the original scope to a state that
// can be reused directly and avoid creating this temporary scope.
ClassScope* reparsed_scope =
NewClassScope(original_scope->outer_scope(), is_anonymous);
#ifdef DEBUG
original_scope->SetScopeName(class_name);
#endif
Expression* expr =
DoParseClassLiteral(reparsed_scope, class_name, scanner()->location(),
is_anonymous, class_token_pos);
DCHECK(expr->IsClassLiteral());
ClassLiteral* literal = expr->AsClassLiteral();
FunctionLiteral* initializer =
literal->instance_members_initializer_function();
// Reindex so that the function literal ids match.
AstFunctionLiteralIdReindexer reindexer(
stack_limit_, initializer_id - initializer->function_literal_id());
reindexer.Reindex(expr);
no_expression_scope.ValidateExpression();
// Fix up the scope chain and the references used by the instance member
// initializer.
reparsed_scope->ReplaceReparsedClassScope(isolate, ast_value_factory(),
original_scope);
original_scope_ = reparsed_scope;
return initializer;
}
Statement* Parser::ParseModuleItem() {
// ecma262/#prod-ModuleItem
// ModuleItem :
......@@ -3221,9 +3122,7 @@ FunctionLiteral* Parser::CreateInitializerFunction(
FunctionSyntaxKind::kAccessorOrMethod,
FunctionLiteral::kShouldEagerCompile, scope->start_position(), false,
GetNextFunctionLiteralId());
#ifdef DEBUG
scope->SetScopeName(ast_value_factory()->GetOneByteString(name));
#endif
RecordFunctionLiteralSourceRange(result);
return result;
......
......@@ -237,15 +237,6 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
int function_literal_id,
const AstRawString* raw_name);
FunctionLiteral* DoParseDeserializedFunction(
Isolate* isolate, Handle<SharedFunctionInfo> shared_info, ParseInfo* info,
int start_position, int end_position, int function_literal_id,
const AstRawString* raw_name);
FunctionLiteral* ParseClassForInstanceMemberInitialization(
Isolate* isolate, ClassScope* scope, int initializer_pos,
int initializer_id);
// Called by ParseProgram after setting up the scanner.
FunctionLiteral* DoParseProgram(Isolate* isolate, ParseInfo* info);
......
......@@ -4156,583 +4156,6 @@ UNINITIALIZED_TEST(ReinitializeHashSeedRehashable) {
FreeCurrentEmbeddedBlob();
}
UNINITIALIZED_TEST(ClassFields) {
DisableAlwaysOpt();
i::FLAG_rehash_snapshot = true;
i::FLAG_hash_seed = 42;
i::FLAG_allow_natives_syntax = true;
DisableEmbeddedBlobRefcounting();
v8::StartupData blob;
{
v8::SnapshotCreator creator;
v8::Isolate* isolate = creator.GetIsolate();
{
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
CompileRun(
"class ClassWithFieldInitializer {"
" #field = 1;"
" constructor(val) {"
" this.#field = val;"
" }"
" get field() {"
" return this.#field;"
" }"
"}"
"class ClassWithDefaultConstructor {"
" #field = 42;"
" get field() {"
" return this.#field;"
" }"
"}"
"class ClassWithFieldDeclaration {"
" #field;"
" constructor(val) {"
" this.#field = val;"
" }"
" get field() {"
" return this.#field;"
" }"
"}"
"class ClassWithPublicField {"
" field = 1;"
" constructor(val) {"
" this.field = val;"
" }"
"}"
"class ClassWithFunctionField {"
" field = 123;"
" func = () => { return this.field; }"
"}"
"class ClassWithThisInInitializer {"
" #field = 123;"
" field = this.#field;"
"}");
creator.SetDefaultContext(context);
}
blob =
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
}
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
create_params.snapshot_blob = &blob;
v8::Isolate* isolate = v8::Isolate::New(create_params);
{
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
CHECK(!context.IsEmpty());
v8::Context::Scope context_scope(context);
ExpectInt32("(new ClassWithFieldInitializer(123)).field", 123);
ExpectInt32("(new ClassWithDefaultConstructor()).field", 42);
ExpectInt32("(new ClassWithFieldDeclaration(123)).field", 123);
ExpectInt32("(new ClassWithPublicField(123)).field", 123);
ExpectInt32("(new ClassWithFunctionField()).func()", 123);
ExpectInt32("(new ClassWithThisInInitializer()).field", 123);
}
isolate->Dispose();
delete[] blob.data;
FreeCurrentEmbeddedBlob();
}
UNINITIALIZED_TEST(ClassFieldsReferencePrivateInInitializer) {
DisableAlwaysOpt();
i::FLAG_rehash_snapshot = true;
i::FLAG_hash_seed = 42;
i::FLAG_allow_natives_syntax = true;
DisableEmbeddedBlobRefcounting();
v8::StartupData blob;
{
v8::SnapshotCreator creator;
v8::Isolate* isolate = creator.GetIsolate();
{
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
CompileRun(
"class A {"
" #a = 42;"
" a = this.#a;"
"}"
"let str;"
"class ClassWithEval {"
" field = eval(str);"
"}"
"class ClassWithPrivateAndEval {"
" #field = 42;"
" field = eval(str);"
"}");
creator.SetDefaultContext(context);
}
blob =
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
}
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
create_params.snapshot_blob = &blob;
v8::Isolate* isolate = v8::Isolate::New(create_params);
{
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
CHECK(!context.IsEmpty());
v8::Context::Scope context_scope(context);
ExpectInt32("(new A()).a", 42);
v8::TryCatch try_catch(isolate);
CompileRun("str = 'this.#nonexistent'; (new ClassWithEval()).field");
CHECK(try_catch.HasCaught());
ExpectInt32("str = 'this.#field'; (new ClassWithPrivateAndEval()).field",
42);
}
isolate->Dispose();
delete[] blob.data;
FreeCurrentEmbeddedBlob();
}
UNINITIALIZED_TEST(ClassFieldsReferenceClassVariable) {
DisableAlwaysOpt();
i::FLAG_rehash_snapshot = true;
i::FLAG_hash_seed = 42;
i::FLAG_allow_natives_syntax = true;
DisableEmbeddedBlobRefcounting();
v8::StartupData blob;
{
v8::SnapshotCreator creator;
v8::Isolate* isolate = creator.GetIsolate();
{
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
CompileRun(
"class Klass {"
" #consturctor = Klass;"
" func() {"
" return this.#consturctor;"
" }"
"}");
creator.SetDefaultContext(context);
}
blob =
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
}
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
create_params.snapshot_blob = &blob;
v8::Isolate* isolate = v8::Isolate::New(create_params);
{
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
CHECK(!context.IsEmpty());
v8::Context::Scope context_scope(context);
ExpectTrue("new Klass().func() === Klass");
}
isolate->Dispose();
delete[] blob.data;
FreeCurrentEmbeddedBlob();
}
UNINITIALIZED_TEST(ClassFieldsNested) {
DisableAlwaysOpt();
i::FLAG_rehash_snapshot = true;
i::FLAG_hash_seed = 42;
i::FLAG_allow_natives_syntax = true;
DisableEmbeddedBlobRefcounting();
v8::StartupData blob;
{
v8::SnapshotCreator creator;
v8::Isolate* isolate = creator.GetIsolate();
{
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
CompileRun(
"class Outer {"
" #odata = 42;"
" #inner;"
" static getInner() {"
" class Inner {"
" #idata = 42;"
" #outer;"
" constructor(outer) {"
" this.#outer = outer;"
" outer.#inner = this;"
" }"
" check() {"
" return this.#idata === this.#outer.#odata &&"
" this === this.#outer.#inner;"
" }"
" }"
" return Inner;"
" }"
" check() {"
" return this.#inner.check();"
" }"
"}"
"const Inner = Outer.getInner();");
creator.SetDefaultContext(context);
}
blob =
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
}
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
create_params.snapshot_blob = &blob;
v8::Isolate* isolate = v8::Isolate::New(create_params);
{
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
CHECK(!context.IsEmpty());
v8::Context::Scope context_scope(context);
ExpectTrue("(new Inner(new Outer)).check()");
}
isolate->Dispose();
delete[] blob.data;
FreeCurrentEmbeddedBlob();
}
UNINITIALIZED_TEST(ClassPrivateMethods) {
DisableAlwaysOpt();
i::FLAG_rehash_snapshot = true;
i::FLAG_hash_seed = 42;
i::FLAG_allow_natives_syntax = true;
DisableEmbeddedBlobRefcounting();
v8::StartupData blob;
{
v8::SnapshotCreator creator;
v8::Isolate* isolate = creator.GetIsolate();
{
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
CompileRun(
"class JustPrivateMethods {"
" #method() { return this.val; }"
" get #accessor() { return this.val; };"
" set #accessor(val) { this.val = val; }"
" method() { return this.#method(); } "
" getter() { return this.#accessor; } "
" setter(val) { this.#accessor = val } "
"}"
"class PrivateMethodsAndFields {"
" #val = 1;"
" #method() { return this.#val; }"
" get #accessor() { return this.#val; };"
" set #accessor(val) { this.#val = val; }"
" method() { return this.#method(); } "
" getter() { return this.#accessor; } "
" setter(val) { this.#accessor = val } "
"}"
"class Nested {"
" #val = 42;"
" static #method(obj) { return obj.#val; }"
" getInner() {"
" class Inner {"
" runEval(obj, str) {"
" return eval(str);"
" }"
" }"
" return Inner;"
" }"
"}");
creator.SetDefaultContext(context);
}
blob =
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
}
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
create_params.snapshot_blob = &blob;
v8::Isolate* isolate = v8::Isolate::New(create_params);
{
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
CHECK(!context.IsEmpty());
v8::Context::Scope context_scope(context);
CompileRun("const a = new JustPrivateMethods(); a.setter(42);");
ExpectInt32("a.method()", 42);
ExpectInt32("a.getter()", 42);
CompileRun("const b = new PrivateMethodsAndFields(); b.setter(42);");
ExpectInt32("b.method()", 42);
ExpectInt32("b.getter()", 42);
CompileRun("const c = new (new Nested().getInner());");
ExpectInt32("c.runEval(new Nested(), 'Nested.#method(obj)')", 42);
}
isolate->Dispose();
delete[] blob.data;
FreeCurrentEmbeddedBlob();
}
UNINITIALIZED_TEST(ClassFieldsWithInheritance) {
DisableAlwaysOpt();
i::FLAG_rehash_snapshot = true;
i::FLAG_hash_seed = 42;
i::FLAG_allow_natives_syntax = true;
DisableEmbeddedBlobRefcounting();
v8::StartupData blob;
{
v8::SnapshotCreator creator;
v8::Isolate* isolate = creator.GetIsolate();
{
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
CompileRun(
"class Base {"
" #a = 'test';"
" getA() { return this.#a; }"
"}"
"class Derived extends Base {"
" #b = 1;"
" constructor() {"
" super();"
" this.#b = this.getA();"
" }"
" check() {"
" return this.#b === this.getA();"
" }"
"}"
"class DerivedDefaultConstructor extends Base {"
" #b = 1;"
" check() {"
" return this.#b === 1;"
" }"
"}"
"class NestedSuper extends Base {"
" #b = 1;"
" constructor() {"
" const t = () => {"
" super();"
" };"
" t();"
" }"
" check() {"
" return this.#b === 1;"
" }"
"}"
"class EvaledSuper extends Base {"
" #b = 1;"
" constructor() {"
" eval('super()');"
" }"
" check() {"
" return this.#b === 1;"
" }"
"}"
"class NestedEvaledSuper extends Base {"
" #b = 1;"
" constructor() {"
" const t = () => {"
" eval('super()');"
" };"
" t();"
" }"
" check() {"
" return this.#b === 1;"
" }"
"}");
creator.SetDefaultContext(context);
}
blob =
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
}
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
create_params.snapshot_blob = &blob;
v8::Isolate* isolate = v8::Isolate::New(create_params);
{
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
CHECK(!context.IsEmpty());
v8::Context::Scope context_scope(context);
ExpectBoolean("(new Derived()).check()", true);
ExpectBoolean("(new DerivedDefaultConstructor()).check()", true);
ExpectBoolean("(new NestedSuper()).check()", true);
ExpectBoolean("(new EvaledSuper()).check()", true);
ExpectBoolean("(new NestedEvaledSuper()).check()", true);
}
isolate->Dispose();
delete[] blob.data;
FreeCurrentEmbeddedBlob();
}
UNINITIALIZED_TEST(ClassFieldsRecalcPrivateNames) {
DisableAlwaysOpt();
i::FLAG_rehash_snapshot = true;
i::FLAG_hash_seed = 42;
i::FLAG_allow_natives_syntax = true;
DisableEmbeddedBlobRefcounting();
v8::StartupData blob;
{
v8::SnapshotCreator creator;
v8::Isolate* isolate = creator.GetIsolate();
{
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
CompileRun(
"let heritageFn;"
"class Outer {"
" #f = 'Outer.#f';"
" static Inner = class Inner extends (heritageFn = function () {"
" return class Nested {"
" exfil(obj) { return obj.#f; }"
" exfilEval(obj) { return eval('obj.#f'); }"
" };"
" }) {"
" #f = 'Inner.#f';"
" };"
"};");
creator.SetDefaultContext(context);
}
blob =
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
}
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
create_params.snapshot_blob = &blob;
v8::Isolate* isolate = v8::Isolate::New(create_params);
{
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
CHECK(!context.IsEmpty());
v8::Context::Scope context_scope(context);
CompileRun(
"const o = new Outer;"
"const c = new Outer.Inner;"
"const D = heritageFn();"
"const d = new D;"
"let error1;"
"let error2;");
ExpectString("d.exfil(o)", "Outer.#f");
ExpectString("d.exfilEval(o)", "Outer.#f");
CompileRun("try { d.exfil(c) } catch(e) { error1 = e; }");
ExpectBoolean("error1 instanceof TypeError", true);
CompileRun("try { d.exfilEval(c) } catch(e) { error2 = e; }");
ExpectBoolean("error2 instanceof TypeError", true);
}
isolate->Dispose();
delete[] blob.data;
FreeCurrentEmbeddedBlob();
}
UNINITIALIZED_TEST(ClassFieldsWithBindings) {
DisableAlwaysOpt();
i::FLAG_rehash_snapshot = true;
i::FLAG_hash_seed = 42;
i::FLAG_allow_natives_syntax = true;
DisableEmbeddedBlobRefcounting();
v8::StartupData blob;
{
v8::SnapshotCreator creator;
v8::Isolate* isolate = creator.GetIsolate();
{
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
CompileRun(
"function testVarBinding() {"
" function FuncWithVar() {"
" this.getPrivate = () => 'test';"
" }"
" class Derived extends FuncWithVar {"
" ['computed'] = FuncWithVar;"
" #private = FuncWithVar;"
" public = FuncWithVar;"
" constructor() {"
" super();"
" this.#private = this.getPrivate();"
" }"
" check() {"
" return this.#private === this.getPrivate() &&"
" this.computed === FuncWithVar &&"
" this.public === FuncWithVar;"
" }"
" }"
""
" return((new Derived()).check());"
"}"
"class ClassWithLet {"
" #private = 'test';"
" getPrivate() { return this.#private; }"
"}"
"function testLetBinding() {"
" class Derived extends ClassWithLet {"
" ['computed'] = ClassWithLet;"
" #private = ClassWithLet;"
" public = ClassWithLet;"
" constructor() {"
" super();"
" this.#private = this.getPrivate();"
" }"
" check() {"
" return this.#private === this.getPrivate() &&"
" this.computed === ClassWithLet &&"
" this.public === ClassWithLet;"
" }"
" }"
""
" return((new Derived()).check());"
"}"
"const ClassWithConst = class {"
" #private = 'test';"
" getPrivate() { return this.#private; }"
"};"
"function testConstBinding() {"
" class Derived extends ClassWithConst {"
" ['computed'] = ClassWithConst;"
" #private = ClassWithConst;"
" public = ClassWithConst;"
" constructor() {"
" super();"
" this.#private = this.getPrivate();"
" }"
" check() {"
" return this.#private === this.getPrivate() &&"
" this.computed === ClassWithConst &&"
" this.public === ClassWithConst;"
" }"
" }"
""
" return((new Derived()).check());"
"}");
creator.SetDefaultContext(context);
}
blob =
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
}
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
create_params.snapshot_blob = &blob;
v8::Isolate* isolate = v8::Isolate::New(create_params);
{
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
CHECK(!context.IsEmpty());
v8::Context::Scope context_scope(context);
ExpectBoolean("testVarBinding()", true);
ExpectBoolean("testLetBinding()", true);
ExpectBoolean("testConstBinding()", true);
}
isolate->Dispose();
delete[] blob.data;
FreeCurrentEmbeddedBlob();
}
void CheckSFIsAreWeak(WeakFixedArray sfis, Isolate* isolate) {
CHECK_GT(sfis.length(), 0);
int no_of_weak = 0;
......
......@@ -22,41 +22,41 @@ var b1, b2, b3;
// y = [B1]2;
// z = [B2]3;
// }
b1 = Debug.setBreakPoint(initializer, 1, 0);
assertTrue(Debug.showBreakPoints(initializer).indexOf("x = [B0]1;") > 0);
b1 = Debug.setBreakPoint(initializer, 0, 0);
assertTrue(Debug.showBreakPoints(initializer).indexOf("x = [B0]1;") === 0);
Debug.clearBreakPoint(b1);
assertTrue(Debug.showBreakPoints(initializer).indexOf("x = [B0]1;") === -1);
b2 = Debug.setBreakPoint(initializer, 2, 0);
b2 = Debug.setBreakPoint(initializer, 1, 0);
assertTrue(Debug.showBreakPoints(initializer).indexOf("y = [B0]2;") > 0);
Debug.clearBreakPoint(b2);
assertTrue(Debug.showBreakPoints(initializer).indexOf("y = [B0]2;") === -1);
b3 = Debug.setBreakPoint(initializer, 3, 0);
b3 = Debug.setBreakPoint(initializer, 2, 0);
assertTrue(Debug.showBreakPoints(initializer).indexOf("z = [B0]3") > 0);
Debug.clearBreakPoint(b3);
assertTrue(Debug.showBreakPoints(initializer).indexOf("z = [B0]3") === -1);
b1 = Debug.setBreakPoint(initializer, 1, 0);
b2 = Debug.setBreakPoint(initializer, 2, 0);
assertTrue(Debug.showBreakPoints(initializer).indexOf("x = [B0]1;") > 0);
b1 = Debug.setBreakPoint(initializer, 0, 0);
b2 = Debug.setBreakPoint(initializer, 1, 0);
assertTrue(Debug.showBreakPoints(initializer).indexOf("x = [B0]1;") === 0);
assertTrue(Debug.showBreakPoints(initializer).indexOf("y = [B1]2;") > 0);
Debug.clearBreakPoint(b1);
assertTrue(Debug.showBreakPoints(initializer).indexOf("x = [B0]1;") === -1);
Debug.clearBreakPoint(b2);
assertTrue(Debug.showBreakPoints(initializer).indexOf("y = [B1]2;") === -1);
b1 = Debug.setBreakPoint(initializer, 1, 0);
b3 = Debug.setBreakPoint(initializer, 3, 0);
assertTrue(Debug.showBreakPoints(initializer).indexOf("x = [B0]1;") > 0);
b1 = Debug.setBreakPoint(initializer, 0, 0);
b3 = Debug.setBreakPoint(initializer, 2, 0);
assertTrue(Debug.showBreakPoints(initializer).indexOf("x = [B0]1;") === 0);
assertTrue(Debug.showBreakPoints(initializer).indexOf("z = [B1]3") > 0);
Debug.clearBreakPoint(b1);
assertTrue(Debug.showBreakPoints(initializer).indexOf("x = [B0]1;") === -1);
Debug.clearBreakPoint(b3);
assertTrue(Debug.showBreakPoints(initializer).indexOf("z = [B1]3") === -1);
b2 = Debug.setBreakPoint(initializer, 2, 0);
b3 = Debug.setBreakPoint(initializer, 3, 0);
b2 = Debug.setBreakPoint(initializer, 1, 0);
b3 = Debug.setBreakPoint(initializer, 2, 0);
assertTrue(Debug.showBreakPoints(initializer).indexOf("y = [B0]2;") > 0);
assertTrue(Debug.showBreakPoints(initializer).indexOf("z = [B1]3") > 0);
Debug.clearBreakPoint(b2);
......@@ -83,11 +83,11 @@ class X {
// }
initializer = %GetInitializerFunction(X);
b1 = Debug.setBreakPoint(initializer, 1, 0);
assertTrue(Debug.showBreakPoints(initializer).indexOf('[foo()] = 1;') > 0);
b1 = Debug.setBreakPoint(initializer, 0, 0);
assertTrue(Debug.showBreakPoints(initializer).indexOf('[foo()] = 1;') === 0);
Debug.clearBreakPoint(b1);
b1 = Debug.setBreakPoint(initializer, 2, 0);
b1 = Debug.setBreakPoint(initializer, 1, 0);
assertTrue(Debug.showBreakPoints(initializer).indexOf('baz = [B0]foo()') > 0);
Debug.clearBreakPoint(b1);
......
......@@ -90,8 +90,8 @@ Running test: testScopesPaused
scopeChain : [
[0] : {
endLocation : {
columnNumber : 3
lineNumber : 15
columnNumber : 13
lineNumber : 14
scriptId : <scriptId>
}
name : run
......@@ -102,8 +102,8 @@ Running test: testScopesPaused
type : object
}
startLocation : {
columnNumber : 2
lineNumber : 11
columnNumber : 4
lineNumber : 12
scriptId : <scriptId>
}
type : local
......@@ -183,8 +183,8 @@ Running test: testScopesPaused
[0] : {
callFrameId : <callFrameId>
functionLocation : {
columnNumber : 2
lineNumber : 11
columnNumber : 4
lineNumber : 12
scriptId : <scriptId>
}
functionName : <instance_members_initializer>
......@@ -364,8 +364,8 @@ Running test: testScopesPaused
[0] : {
callFrameId : <callFrameId>
functionLocation : {
columnNumber : 2
lineNumber : 11
columnNumber : 4
lineNumber : 12
scriptId : <scriptId>
}
functionName : <instance_members_initializer>
......@@ -545,8 +545,8 @@ Running test: testScopesPaused
[0] : {
callFrameId : <callFrameId>
functionLocation : {
columnNumber : 2
lineNumber : 11
columnNumber : 4
lineNumber : 12
scriptId : <scriptId>
}
functionName : <instance_members_initializer>
......@@ -798,8 +798,8 @@ Running test: testScopesPaused
[1] : {
callFrameId : <callFrameId>
functionLocation : {
columnNumber : 2
lineNumber : 11
columnNumber : 4
lineNumber : 12
scriptId : <scriptId>
}
functionName : <instance_members_initializer>
......
......@@ -7,23 +7,23 @@ let x = |R|class {}
|_|x = |R|class {
x = |_|1;
y = |_|2;
}|R|
y = |_|2|R|;
}
|_|x = |R|class {
x = |C|foo();
y = |_|2;
z = |C|bar();
}|R|
z = |C|bar()|R|;
}
|_|x = class {
x = |C|foo();
y = |_|2;
z = |C|bar();
z = |C|bar()|R|;
constructor() {
this.|_|x;
|R|}
}|R|
}
|_|x = class {
x = |C|foo();
......@@ -31,8 +31,8 @@ let x = |R|class {}
constructor() {
this.|_|x;
|R|}
z = |C|bar();
}|R|
z = |C|bar()|R|;
}
|_|x = class {
x = |C|foo();
......@@ -40,14 +40,14 @@ let x = |R|class {}
constructor() {
this.|_|x;
|R|}
z = |C|bar();
}|R|
z = |C|bar()|R|;
}
|_|x = |R|class {
x = |_|1;
foo() {|R|}
y = |_|2;
}|R|
y = |_|2|R|;
}
|_|x = |R|class {
x = (function() {
......@@ -55,54 +55,54 @@ let x = |R|class {}
|R|})|C|();
y = (() => {
|C|bar();
|R|})|C|();
}|R|
|R|})|C|()|R|;
}
|_|x = |R|class {
x = |_|function() {
|C|foo();
|R|};
}|R|
|R|}|R|;
}
|_|x = |R|class {
x = |_|async function() {
|_|await |C|foo();
|R|};
}|R|
|R|}|R|;
}
|_|x = |R|class {
x = |_|() => {
|C|foo();
|R|};
y = |_|() => |C|bar()|R|;
}|R|
}
|_|x = |R|class {
x = |_|async () => {
|_|await |C|foo();
|R|};
y = |_|async () => |_|await |C|bar()|R|;
}|R|
}
|_|x = |R|class {
[|_|x] = |_|1;
[|C|foo()] = |_|2;
}|R|
[|C|foo()] = |_|2|R|;
}
|_|x = |R|class {
[|_|x] = |_|[...this];
}|R|
[|_|x] = |_|[...this]|R|;
}
|_|x = |R|class {
x;
[|C|foo()];
}|R|
[|C|foo()]|R|;
}
|_|x = |R|class {
x = |_|function*|_|() {
|_|yield 1;
|R|};
}|R|
|R|}|R|;
}
|_|x = |R|class {
static x = |_|1;
......@@ -201,6 +201,6 @@ let x = |R|class {}
static [|_|z] = |_|3;
[|_|p] = |_|4;
static [|C|foo()] = |_|5|R|;
[|C|bar()] = |_|6;
}|R|
[|C|bar()] = |_|6|R|;
}
|R|
......@@ -25,7 +25,7 @@ class X { // 000
}; // 100
`,
[{"start":0,"end":149,"count":1},
{"start":0,"end":101,"count":0}]
{"start":52,"end":70,"count":0}]
);
TestCoverage(
......@@ -37,7 +37,7 @@ class X { // 000
let x = new X(); // 150
`,
[{"start":0,"end":199,"count":1},
{"start":0,"end":101,"count":1},
{"start":52,"end":70,"count":1},
{"start":56,"end":70,"count":0}]
);
......@@ -51,7 +51,7 @@ let x = new X(); // 150
x.x(); // 200
`,
[{"start":0,"end":249,"count":1},
{"start":0,"end":101,"count":1},
{"start":52,"end":70,"count":1},
{"start":56,"end":70,"count":1}]
);
......@@ -68,7 +68,7 @@ x.x(); // 300
x.y(); // 350
`,
[{"start":0,"end":399,"count":1},
{"start":0,"end":201,"count":1},
{"start":52,"end":169,"count":1},
{"start":56,"end":70,"count":1},
{"start":102,"end":111,"count":0},
{"start":156,"end":169,"count":1}]
......@@ -88,7 +88,7 @@ x.y(); // 350
x.foo(); // 400
`,
[{"start":0,"end":449,"count":1},
{"start":0,"end":201,"count":1},
{"start":52,"end":169,"count":1},
{"start":56,"end":70,"count":1},
{"start":102,"end":111,"count":1},
{"start":156,"end":169,"count":1}]
......@@ -103,7 +103,7 @@ class X { // 000
let x = new X(); // 150
`,
[{"start":0,"end":199,"count":1},
{"start":0,"end":101,"count":1},
{"start":52,"end":74,"count":1},
{"start":57,"end":71,"count":1}]
);
......@@ -118,7 +118,7 @@ let x = new X(); // 200
`,
[{"start":0,"end":249,"count":1},
{"start":0,"end":15,"count":1},
{"start":50,"end":151,"count":1},
{"start":102,"end":128,"count":1},
{"start":111,"end":125,"count":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