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

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

This is a reland of 91f08378

When the class scope does not need a context, the deserialized
outer scope of the initializer scope would not be the class scope,
and we should not and do not need to use it to fix up the allocation
information of the context-allocated variables. The original patch
did not consider this case and resulted in a regression when we
tried to reparse the initializer function to look for destructuring
assignment errors. This fixes the regression by not deserializing
the class scope that's going to be reparsed, and using the positions
of the scopes to tell whether the scope info matches the reparsed
scope and can be used to fix up the allocation info.

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: chromium:1278086, chromium:1278085, v8:10704
Change-Id: Iea4f1f6dc398846cbe322adc16f6fffd6d2dfdf3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3325912Reviewed-by: 's avatarToon Verwaest <verwaest@chromium.org>
Commit-Queue: Joyee Cheung <joyee@igalia.com>
Cr-Commit-Position: refs/heads/main@{#78745}
parent 776126ac
......@@ -223,6 +223,10 @@ ClassScope::ClassScope(IsolateT* isolate, Zone* zone,
var->AllocateTo(VariableLocation::CONTEXT,
Context::MIN_CONTEXT_SLOTS + index);
}
DCHECK(scope_info->HasPositionInfo());
set_start_position(scope_info->StartPosition());
set_end_position(scope_info->EndPosition());
}
template ClassScope::ClassScope(Isolate* isolate, Zone* zone,
AstValueFactory* ast_value_factory,
......@@ -523,6 +527,15 @@ 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);
......@@ -666,8 +679,10 @@ 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
// 3) a function/eval in a scope that was already resolved.
// 4) a class member initializer function scope
// 3) 4 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.
......@@ -1895,6 +1910,8 @@ 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, "// ");
......@@ -2727,6 +2744,46 @@ bool IsComplementaryAccessorPair(VariableMode a, VariableMode b) {
}
}
void ClassScope::FinalizeReparsedClassScope(
Isolate* isolate, MaybeHandle<ScopeInfo> maybe_scope_info,
AstValueFactory* ast_value_factory, bool needs_allocation_fixup) {
// Set this bit so that DelcarationScope::Analyze recognizes
// the reparsed instance member initializer scope.
#ifdef DEBUG
is_reparsed_class_scope_ = true;
#endif
if (!needs_allocation_fixup) {
return;
}
// 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.
DCHECK(!maybe_scope_info.is_null());
Handle<ScopeInfo> scope_info = maybe_scope_info.ToHandleChecked();
DCHECK_EQ(scope_info->scope_type(), CLASS_SCOPE);
DCHECK_EQ(scope_info->StartPosition(), start_position_);
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->ContextInlinedLocalName(i);
const AstRawString* string = ast_value_factory->GetString(
name, SharedStringAccessGuardIfNeeded(isolate));
Variable* var = string->IsPrivateName() ? LookupLocalPrivateName(string)
: LookupLocal(string);
DCHECK_NOT_NULL(var);
var->AllocateTo(VariableLocation::CONTEXT, slot_index);
}
scope_info_ = scope_info;
}
Variable* ClassScope::DeclarePrivateName(const AstRawString* name,
VariableMode mode,
IsStaticFlag is_static_flag,
......
......@@ -424,6 +424,9 @@ 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() {
......@@ -1479,6 +1482,18 @@ class V8_EXPORT_PRIVATE ClassScope : public Scope {
should_save_class_variable_index_ = true;
}
// Finalize the reparsed class scope, called when reparsing the
// class scope for the initializer member function.
// If the reparsed scope declares any variable that needs allocation
// fixup using the scope info, needs_allocation_fixup is true.
void FinalizeReparsedClassScope(Isolate* isolate,
MaybeHandle<ScopeInfo> outer_scope_info,
AstValueFactory* ast_value_factory,
bool needs_allocation_fixup);
#ifdef DEBUG
bool is_reparsed_class_scope() const { return is_reparsed_class_scope_; }
#endif
private:
friend class Scope;
friend class PrivateNameScopeIterator;
......@@ -1524,6 +1539,9 @@ 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
......
......@@ -197,7 +197,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,6 +2813,7 @@ 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() &&
......
......@@ -711,7 +711,7 @@ bool ScopeInfo::HasPositionInfo() const {
// static
bool ScopeInfo::NeedsPositionInfo(ScopeType type) {
return type == FUNCTION_SCOPE || type == SCRIPT_SCOPE || type == EVAL_SCOPE ||
type == MODULE_SCOPE;
type == MODULE_SCOPE || type == CLASS_SCOPE;
}
bool ScopeInfo::HasSharedFunctionName() const {
......
......@@ -6,7 +6,11 @@ extern macro EmptyScopeInfoConstant(): ScopeInfo;
const kEmptyScopeInfo: ScopeInfo = EmptyScopeInfoConstant();
extern enum ScopeType extends uint32 {
CLASS_SCOPE, // Also used for the empty scope (for NativeContext & builtins).
// The empty scope info for builtins and NativeContexts is allocated
// in a way that it gets the first scope type in line, see
// Heap::CreateInitialMaps(). It's always guarded with the IsEmpty
// bit, so it doesn't matter what scope type it gets.
CLASS_SCOPE,
EVAL_SCOPE,
FUNCTION_SCOPE,
MODULE_SCOPE,
......@@ -144,7 +148,9 @@ extern class ScopeInfo extends HeapObject {
[flags.scope_type == ScopeType::FUNCTION_SCOPE ||
flags.scope_type == ScopeType::SCRIPT_SCOPE ||
flags.scope_type == ScopeType::EVAL_SCOPE ||
flags.scope_type == ScopeType::MODULE_SCOPE]: PositionInfo;
flags.scope_type == ScopeType::MODULE_SCOPE ||
(flags.is_empty ? false : flags.scope_type == ScopeType::CLASS_SCOPE)]:
PositionInfo;
outer_scope_info?[flags.has_outer_scope_info]: ScopeInfo|TheHole;
......
......@@ -1234,10 +1234,12 @@ class ParserBase {
ExpressionT ParseArrowFunctionLiteral(const FormalParametersT& parameters);
void ParseAsyncFunctionBody(Scope* scope, StatementListT* body);
ExpressionT ParseAsyncFunctionLiteral();
ExpressionT ParseClassLiteral(IdentifierT name,
ExpressionT ParseClassExpression(Scope* outer_scope);
ExpressionT ParseClassLiteral(Scope* outer_scope, IdentifierT name,
Scanner::Location class_name_location,
bool name_is_strict_reserved,
int class_token_pos);
ExpressionT ParseTemplateLiteral(ExpressionT tag, int start, bool tagged);
ExpressionT ParseSuperExpression();
ExpressionT ParseImportExpressions();
......@@ -2002,19 +2004,7 @@ ParserBase<Impl>::ParsePrimaryExpression() {
}
case Token::CLASS: {
Consume(Token::CLASS);
int class_token_pos = position();
IdentifierT name = impl()->NullIdentifier();
bool is_strict_reserved_name = false;
Scanner::Location class_name_location = Scanner::Location::invalid();
if (peek_any_identifier()) {
name = ParseAndClassifyIdentifier(Next());
class_name_location = scanner()->location();
is_strict_reserved_name =
Token::IsStrictReservedWord(scanner()->current_token());
}
return ParseClassLiteral(name, class_name_location,
is_strict_reserved_name, class_token_pos);
return ParseClassExpression(scope());
}
case Token::TEMPLATE_SPAN:
......@@ -2522,8 +2512,6 @@ 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);
}
......@@ -2538,8 +2526,13 @@ 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 {
......@@ -4255,7 +4248,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseClassDeclaration(
}
ExpressionParsingScope no_expression_scope(impl());
ExpressionT value = ParseClassLiteral(name, scanner()->location(),
ExpressionT value = ParseClassLiteral(scope(), name, scanner()->location(),
is_strict_reserved, class_token_pos);
no_expression_scope.ValidateExpression();
int end_pos = position();
......@@ -4660,9 +4653,27 @@ ParserBase<Impl>::ParseArrowFunctionLiteral(
return function_literal;
}
template <typename Impl>
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseClassExpression(
Scope* outer_scope) {
Consume(Token::CLASS);
int class_token_pos = position();
IdentifierT name = impl()->NullIdentifier();
bool is_strict_reserved_name = false;
Scanner::Location class_name_location = Scanner::Location::invalid();
if (peek_any_identifier()) {
name = ParseAndClassifyIdentifier(Next());
class_name_location = scanner()->location();
is_strict_reserved_name =
Token::IsStrictReservedWord(scanner()->current_token());
}
return ParseClassLiteral(outer_scope, name, class_name_location,
is_strict_reserved_name, class_token_pos);
}
template <typename Impl>
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseClassLiteral(
IdentifierT name, Scanner::Location class_name_location,
Scope* outer_scope, IdentifierT name, Scanner::Location class_name_location,
bool name_is_strict_reserved, int class_token_pos) {
bool is_anonymous = impl()->IsNull(name);
......@@ -4680,7 +4691,7 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseClassLiteral(
}
}
ClassScope* class_scope = NewClassScope(scope(), is_anonymous);
ClassScope* class_scope = NewClassScope(outer_scope, is_anonymous);
BlockState block_state(&scope_, class_scope);
RaiseLanguageMode(LanguageMode::kStrict);
......@@ -4767,6 +4778,12 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseClassLiteral(
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) {
......
......@@ -832,7 +832,30 @@ void Parser::ParseFunction(Isolate* isolate, ParseInfo* info,
if (shared_info->HasOuterScopeInfo()) {
maybe_outer_scope_info = handle(shared_info->GetOuterScopeInfo(), isolate);
}
DeserializeScopeChain(isolate, info, maybe_outer_scope_info,
int start_position = shared_info->StartPosition();
int end_position = shared_info->EndPosition();
MaybeHandle<ScopeInfo> deserialize_start_scope = maybe_outer_scope_info;
// If the function is a class member initializer and there isn't a
// scope mismatch, we will only deserialize up to the outer scope of
// the class scope, and regenerate the class scope during reparsing.
if (flags().function_kind() ==
FunctionKind::kClassMembersInitializerFunction &&
shared_info->HasOuterScopeInfo() &&
maybe_outer_scope_info.ToHandleChecked()->scope_type() == CLASS_SCOPE &&
maybe_outer_scope_info.ToHandleChecked()->StartPosition() ==
start_position) {
Handle<ScopeInfo> outer_scope_info =
maybe_outer_scope_info.ToHandleChecked();
if (outer_scope_info->HasOuterScopeInfo()) {
deserialize_start_scope =
handle(outer_scope_info->OuterScopeInfo(), isolate);
} else {
deserialize_start_scope = MaybeHandle<ScopeInfo>();
}
}
DeserializeScopeChain(isolate, info, deserialize_start_scope,
Scope::DeserializationMode::kIncludingVariables);
DCHECK_EQ(factory()->zone(), info->zone());
......@@ -841,8 +864,6 @@ void Parser::ParseFunction(Isolate* isolate, ParseInfo* info,
maybe_wrapped_arguments_ = handle(script->wrapped_arguments(), isolate);
}
int start_position = shared_info->StartPosition();
int end_position = shared_info->EndPosition();
int function_literal_id = shared_info->function_literal_id();
if V8_UNLIKELY (script->type() == Script::TYPE_WEB_SNAPSHOT) {
// Function literal IDs for inner functions haven't been allocated when
......@@ -866,11 +887,13 @@ 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 = DoParseFunction(isolate, info, start_position, end_position,
function_literal_id, info->function_name());
result = DoParseDeserializedFunction(
isolate, maybe_outer_scope_info, info, start_position, end_position,
function_literal_id, info->function_name());
} else {
result = DoParseFunction(isolate, info, start_position, end_position,
function_literal_id, info->function_name());
result = DoParseDeserializedFunction(
isolate, maybe_outer_scope_info, info, start_position, end_position,
function_literal_id, info->function_name());
}
MaybeProcessSourceRanges(info, result, stack_limit_);
if (result != nullptr) {
......@@ -1033,6 +1056,81 @@ FunctionLiteral* Parser::DoParseFunction(Isolate* isolate, ParseInfo* info,
return result;
}
FunctionLiteral* Parser::DoParseDeserializedFunction(
Isolate* isolate, MaybeHandle<ScopeInfo> maybe_outer_scope_info,
ParseInfo* info, int start_position, int end_position,
int function_literal_id, const AstRawString* raw_name) {
if (flags().function_kind() ==
FunctionKind::kClassMembersInitializerFunction) {
return ParseClassForInstanceMemberInitialization(
isolate, maybe_outer_scope_info, start_position, function_literal_id,
end_position);
}
return DoParseFunction(isolate, info, start_position, end_position,
function_literal_id, raw_name);
}
FunctionLiteral* Parser::ParseClassForInstanceMemberInitialization(
Isolate* isolate, MaybeHandle<ScopeInfo> maybe_class_scope_info,
int initializer_pos, int initializer_id, int initializer_end_pos) {
// 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.
int class_token_pos = initializer_pos;
DCHECK_EQ(peek_position(), class_token_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());
// Reparse the class as an expression to build the instance member
// initializer function.
Expression* expr = ParseClassExpression(original_scope_);
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();
// If the class scope was not optimized away, we know that it allocated
// some variables and we need to fix up the allocation info for them.
bool needs_allocation_fixup =
!maybe_class_scope_info.is_null() &&
maybe_class_scope_info.ToHandleChecked()->scope_type() == CLASS_SCOPE &&
maybe_class_scope_info.ToHandleChecked()->StartPosition() ==
class_token_pos;
ClassScope* reparsed_scope = literal->scope();
reparsed_scope->FinalizeReparsedClassScope(isolate, maybe_class_scope_info,
ast_value_factory(),
needs_allocation_fixup);
original_scope_ = reparsed_scope;
DCHECK_EQ(initializer->kind(),
FunctionKind::kClassMembersInitializerFunction);
DCHECK_EQ(initializer->function_literal_id(), initializer_id);
DCHECK_EQ(initializer->end_position(), initializer_end_pos);
return initializer;
}
Statement* Parser::ParseModuleItem() {
// ecma262/#prod-ModuleItem
// ModuleItem :
......@@ -3123,7 +3221,9 @@ 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,6 +237,15 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
int function_literal_id,
const AstRawString* raw_name);
FunctionLiteral* DoParseDeserializedFunction(
Isolate* isolate, MaybeHandle<ScopeInfo> maybe_outer_scope_info,
ParseInfo* info, int start_position, int end_position,
int function_literal_id, const AstRawString* raw_name);
FunctionLiteral* ParseClassForInstanceMemberInitialization(
Isolate* isolate, MaybeHandle<ScopeInfo> maybe_class_scope_info,
int initializer_pos, int initializer_id, int initializer_end_pos);
// Called by ParseProgram after setting up the scanner.
FunctionLiteral* DoParseProgram(Isolate* isolate, ParseInfo* info);
......
This diff is collapsed.
......@@ -22,41 +22,41 @@ var b1, b2, b3;
// y = [B1]2;
// z = [B2]3;
// }
b1 = Debug.setBreakPoint(initializer, 0, 0);
assertTrue(Debug.showBreakPoints(initializer).indexOf("x = [B0]1;") === 0);
b1 = Debug.setBreakPoint(initializer, 1, 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, 1, 0);
b2 = Debug.setBreakPoint(initializer, 2, 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, 2, 0);
b3 = Debug.setBreakPoint(initializer, 3, 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, 0, 0);
b2 = Debug.setBreakPoint(initializer, 1, 0);
assertTrue(Debug.showBreakPoints(initializer).indexOf("x = [B0]1;") === 0);
b1 = Debug.setBreakPoint(initializer, 1, 0);
b2 = Debug.setBreakPoint(initializer, 2, 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, 0, 0);
b3 = Debug.setBreakPoint(initializer, 2, 0);
assertTrue(Debug.showBreakPoints(initializer).indexOf("x = [B0]1;") === 0);
b1 = Debug.setBreakPoint(initializer, 1, 0);
b3 = Debug.setBreakPoint(initializer, 3, 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, 1, 0);
b3 = Debug.setBreakPoint(initializer, 2, 0);
b2 = Debug.setBreakPoint(initializer, 2, 0);
b3 = Debug.setBreakPoint(initializer, 3, 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, 0, 0);
assertTrue(Debug.showBreakPoints(initializer).indexOf('[foo()] = 1;') === 0);
b1 = Debug.setBreakPoint(initializer, 1, 0);
assertTrue(Debug.showBreakPoints(initializer).indexOf('[foo()] = 1;') > 0);
Debug.clearBreakPoint(b1);
b1 = Debug.setBreakPoint(initializer, 1, 0);
b1 = Debug.setBreakPoint(initializer, 2, 0);
assertTrue(Debug.showBreakPoints(initializer).indexOf('baz = [B0]foo()') > 0);
Debug.clearBreakPoint(b1);
......
......@@ -90,8 +90,8 @@ Running test: testScopesPaused
scopeChain : [
[0] : {
endLocation : {
columnNumber : 13
lineNumber : 14
columnNumber : 3
lineNumber : 15
scriptId : <scriptId>
}
name : run
......@@ -102,8 +102,8 @@ Running test: testScopesPaused
type : object
}
startLocation : {
columnNumber : 4
lineNumber : 12
columnNumber : 2
lineNumber : 11
scriptId : <scriptId>
}
type : local
......@@ -183,8 +183,8 @@ Running test: testScopesPaused
[0] : {
callFrameId : <callFrameId>
functionLocation : {
columnNumber : 4
lineNumber : 12
columnNumber : 2
lineNumber : 11
scriptId : <scriptId>
}
functionName : <instance_members_initializer>
......@@ -364,8 +364,8 @@ Running test: testScopesPaused
[0] : {
callFrameId : <callFrameId>
functionLocation : {
columnNumber : 4
lineNumber : 12
columnNumber : 2
lineNumber : 11
scriptId : <scriptId>
}
functionName : <instance_members_initializer>
......@@ -545,8 +545,8 @@ Running test: testScopesPaused
[0] : {
callFrameId : <callFrameId>
functionLocation : {
columnNumber : 4
lineNumber : 12
columnNumber : 2
lineNumber : 11
scriptId : <scriptId>
}
functionName : <instance_members_initializer>
......@@ -798,8 +798,8 @@ Running test: testScopesPaused
[1] : {
callFrameId : <callFrameId>
functionLocation : {
columnNumber : 4
lineNumber : 12
columnNumber : 2
lineNumber : 11
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()|R|;
z = |C|bar();
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":52,"end":70,"count":0}]
{"start":0,"end":101,"count":0}]
);
TestCoverage(
......@@ -37,7 +37,7 @@ class X { // 000
let x = new X(); // 150
`,
[{"start":0,"end":199,"count":1},
{"start":52,"end":70,"count":1},
{"start":0,"end":101,"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":52,"end":70,"count":1},
{"start":0,"end":101,"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":52,"end":169,"count":1},
{"start":0,"end":201,"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":52,"end":169,"count":1},
{"start":0,"end":201,"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":52,"end":74,"count":1},
{"start":0,"end":101,"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":102,"end":128,"count":1},
{"start":50,"end":151,"count":1},
{"start":111,"end":125,"count":1}]
);
......
// 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.
'use strict';
{
class C {
field = c.concat();
}
var c;
assertThrows(() => {
c = new C();
}, TypeError);
}
// Anonymous class
{
const C = class {
field = c.concat();
}
var c;
assertThrows(() => {
c = new C();
}, TypeError);
}
class D {
field = ({ d } = undefined);
}
var d;
assertThrows(
() => {
d = new D();
},
TypeError,
/Cannot destructure property 'd' of 'undefined' as it is undefined/);
class B {
static B = class B {
field = b.concat();
}
static func() {
return B; // keep the context for class B
}
}
var b;
assertThrows(() => {
b = new B.B();
}, TypeError);
class A {
static B = class B {
field = a.concat();
}
static func() {
return A; // keep the context for class A
}
}
var a;
assertThrows(() => {
a = new A.B();
}, TypeError);
class E {
#x = 1;
static B = class B {
field = this.#x;
}
}
var e;
assertThrows(
() => { e = new E.B(); },
TypeError,
/Cannot read private member #x from an object whose class did not declare it/);
......@@ -449,7 +449,8 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
// Generate BytecodeArray.
Handle<ScopeInfo> scope_info =
factory->NewScopeInfo(ScopeInfo::kVariablePartIndex);
scope_info->set_flags(0);
int flags = ScopeInfo::IsEmptyBit::encode(true);
scope_info->set_flags(flags);
scope_info->set_context_local_count(0);
scope_info->set_parameter_count(0);
scope.SetScriptScopeInfo(scope_info);
......
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