Reland "[class] implement reparsing of class instance member initializers"
This is a reland of 91f08378bc
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/+/3325912
Reviewed-by: Toon Verwaest <verwaest@chromium.org>
Commit-Queue: Joyee Cheung <joyee@igalia.com>
Cr-Commit-Position: refs/heads/main@{#78745}
This commit is contained in:
parent
776126acfd
commit
0e07eb5341
@ -223,6 +223,10 @@ ClassScope::ClassScope(IsolateT* isolate, Zone* zone,
|
|||||||
var->AllocateTo(VariableLocation::CONTEXT,
|
var->AllocateTo(VariableLocation::CONTEXT,
|
||||||
Context::MIN_CONTEXT_SLOTS + index);
|
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,
|
template ClassScope::ClassScope(Isolate* isolate, Zone* zone,
|
||||||
AstValueFactory* ast_value_factory,
|
AstValueFactory* ast_value_factory,
|
||||||
@ -523,6 +527,15 @@ template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
|
|||||||
DeclarationScope* script_scope, AstValueFactory* ast_value_factory,
|
DeclarationScope* script_scope, AstValueFactory* ast_value_factory,
|
||||||
DeserializationMode deserialization_mode);
|
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() {
|
DeclarationScope* Scope::AsDeclarationScope() {
|
||||||
DCHECK(is_declaration_scope());
|
DCHECK(is_declaration_scope());
|
||||||
return static_cast<DeclarationScope*>(this);
|
return static_cast<DeclarationScope*>(this);
|
||||||
@ -666,8 +679,10 @@ bool DeclarationScope::Analyze(ParseInfo* info) {
|
|||||||
// We are compiling one of four cases:
|
// We are compiling one of four cases:
|
||||||
// 1) top-level code,
|
// 1) top-level code,
|
||||||
// 2) a function/eval/module on the top-level
|
// 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() ||
|
DCHECK(scope->is_script_scope() || scope->outer_scope()->is_script_scope() ||
|
||||||
|
scope->IsReparsedMemberInitializerScope() ||
|
||||||
scope->outer_scope()->already_resolved_);
|
scope->outer_scope()->already_resolved_);
|
||||||
|
|
||||||
// The outer scope is never lazy.
|
// The outer scope is never lazy.
|
||||||
@ -1895,6 +1910,8 @@ void Scope::Print(int n) {
|
|||||||
if (scope->needs_private_name_context_chain_recalc()) {
|
if (scope->needs_private_name_context_chain_recalc()) {
|
||||||
Indent(n1, "// needs #-name context chain recalc\n");
|
Indent(n1, "// needs #-name context chain recalc\n");
|
||||||
}
|
}
|
||||||
|
Indent(n1, "// ");
|
||||||
|
PrintF("%s\n", FunctionKind2String(scope->function_kind()));
|
||||||
}
|
}
|
||||||
if (num_stack_slots_ > 0) {
|
if (num_stack_slots_ > 0) {
|
||||||
Indent(n1, "// ");
|
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,
|
Variable* ClassScope::DeclarePrivateName(const AstRawString* name,
|
||||||
VariableMode mode,
|
VariableMode mode,
|
||||||
IsStaticFlag is_static_flag,
|
IsStaticFlag is_static_flag,
|
||||||
|
@ -424,6 +424,9 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
|
|||||||
return num_heap_slots() > 0;
|
return num_heap_slots() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
bool IsReparsedMemberInitializerScope() const;
|
||||||
|
#endif
|
||||||
// Use Scope::ForEach for depth first traversal of scopes.
|
// Use Scope::ForEach for depth first traversal of scopes.
|
||||||
// Before:
|
// Before:
|
||||||
// void Scope::VisitRecursively() {
|
// void Scope::VisitRecursively() {
|
||||||
@ -1479,6 +1482,18 @@ class V8_EXPORT_PRIVATE ClassScope : public Scope {
|
|||||||
should_save_class_variable_index_ = true;
|
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:
|
private:
|
||||||
friend class Scope;
|
friend class Scope;
|
||||||
friend class PrivateNameScopeIterator;
|
friend class PrivateNameScopeIterator;
|
||||||
@ -1524,6 +1539,9 @@ class V8_EXPORT_PRIVATE ClassScope : public Scope {
|
|||||||
// This is only maintained during reparsing, restored from the
|
// This is only maintained during reparsing, restored from the
|
||||||
// preparsed data.
|
// preparsed data.
|
||||||
bool should_save_class_variable_index_ = false;
|
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
|
// 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_home_object_string, ".home_object") \
|
||||||
V(_, dot_result_string, ".result") \
|
V(_, dot_result_string, ".result") \
|
||||||
V(_, dot_repl_result_string, ".repl_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_string, ".") \
|
||||||
V(_, dot_switch_tag_string, ".switch_tag") \
|
V(_, dot_switch_tag_string, ".switch_tag") \
|
||||||
V(_, dotAll_string, "dotAll") \
|
V(_, dotAll_string, "dotAll") \
|
||||||
|
@ -2813,6 +2813,7 @@ void BytecodeGenerator::BuildClassProperty(ClassLiteral::Property* property) {
|
|||||||
// Private methods are not initialized in BuildClassProperty.
|
// Private methods are not initialized in BuildClassProperty.
|
||||||
DCHECK_IMPLIES(property->is_private(),
|
DCHECK_IMPLIES(property->is_private(),
|
||||||
property->kind() == ClassLiteral::Property::FIELD);
|
property->kind() == ClassLiteral::Property::FIELD);
|
||||||
|
builder()->SetExpressionPosition(property->key());
|
||||||
|
|
||||||
bool is_literal_store = property->key()->IsPropertyName() &&
|
bool is_literal_store = property->key()->IsPropertyName() &&
|
||||||
!property->is_computed_name() &&
|
!property->is_computed_name() &&
|
||||||
|
@ -711,7 +711,7 @@ bool ScopeInfo::HasPositionInfo() const {
|
|||||||
// static
|
// static
|
||||||
bool ScopeInfo::NeedsPositionInfo(ScopeType type) {
|
bool ScopeInfo::NeedsPositionInfo(ScopeType type) {
|
||||||
return type == FUNCTION_SCOPE || type == SCRIPT_SCOPE || type == EVAL_SCOPE ||
|
return type == FUNCTION_SCOPE || type == SCRIPT_SCOPE || type == EVAL_SCOPE ||
|
||||||
type == MODULE_SCOPE;
|
type == MODULE_SCOPE || type == CLASS_SCOPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ScopeInfo::HasSharedFunctionName() const {
|
bool ScopeInfo::HasSharedFunctionName() const {
|
||||||
|
@ -6,7 +6,11 @@ extern macro EmptyScopeInfoConstant(): ScopeInfo;
|
|||||||
const kEmptyScopeInfo: ScopeInfo = EmptyScopeInfoConstant();
|
const kEmptyScopeInfo: ScopeInfo = EmptyScopeInfoConstant();
|
||||||
|
|
||||||
extern enum ScopeType extends uint32 {
|
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,
|
EVAL_SCOPE,
|
||||||
FUNCTION_SCOPE,
|
FUNCTION_SCOPE,
|
||||||
MODULE_SCOPE,
|
MODULE_SCOPE,
|
||||||
@ -144,7 +148,9 @@ extern class ScopeInfo extends HeapObject {
|
|||||||
[flags.scope_type == ScopeType::FUNCTION_SCOPE ||
|
[flags.scope_type == ScopeType::FUNCTION_SCOPE ||
|
||||||
flags.scope_type == ScopeType::SCRIPT_SCOPE ||
|
flags.scope_type == ScopeType::SCRIPT_SCOPE ||
|
||||||
flags.scope_type == ScopeType::EVAL_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;
|
outer_scope_info?[flags.has_outer_scope_info]: ScopeInfo|TheHole;
|
||||||
|
|
||||||
|
@ -1234,10 +1234,12 @@ class ParserBase {
|
|||||||
ExpressionT ParseArrowFunctionLiteral(const FormalParametersT& parameters);
|
ExpressionT ParseArrowFunctionLiteral(const FormalParametersT& parameters);
|
||||||
void ParseAsyncFunctionBody(Scope* scope, StatementListT* body);
|
void ParseAsyncFunctionBody(Scope* scope, StatementListT* body);
|
||||||
ExpressionT ParseAsyncFunctionLiteral();
|
ExpressionT ParseAsyncFunctionLiteral();
|
||||||
ExpressionT ParseClassLiteral(IdentifierT name,
|
ExpressionT ParseClassExpression(Scope* outer_scope);
|
||||||
|
ExpressionT ParseClassLiteral(Scope* outer_scope, IdentifierT name,
|
||||||
Scanner::Location class_name_location,
|
Scanner::Location class_name_location,
|
||||||
bool name_is_strict_reserved,
|
bool name_is_strict_reserved,
|
||||||
int class_token_pos);
|
int class_token_pos);
|
||||||
|
|
||||||
ExpressionT ParseTemplateLiteral(ExpressionT tag, int start, bool tagged);
|
ExpressionT ParseTemplateLiteral(ExpressionT tag, int start, bool tagged);
|
||||||
ExpressionT ParseSuperExpression();
|
ExpressionT ParseSuperExpression();
|
||||||
ExpressionT ParseImportExpressions();
|
ExpressionT ParseImportExpressions();
|
||||||
@ -2002,19 +2004,7 @@ ParserBase<Impl>::ParsePrimaryExpression() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case Token::CLASS: {
|
case Token::CLASS: {
|
||||||
Consume(Token::CLASS);
|
return ParseClassExpression(scope());
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case Token::TEMPLATE_SPAN:
|
case Token::TEMPLATE_SPAN:
|
||||||
@ -2522,8 +2512,6 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseMemberInitializer(
|
|||||||
|
|
||||||
if (initializer_scope == nullptr) {
|
if (initializer_scope == nullptr) {
|
||||||
initializer_scope = NewFunctionScope(function_kind);
|
initializer_scope = NewFunctionScope(function_kind);
|
||||||
// TODO(gsathya): Make scopes be non contiguous.
|
|
||||||
initializer_scope->set_start_position(beg_pos);
|
|
||||||
initializer_scope->SetLanguageMode(LanguageMode::kStrict);
|
initializer_scope->SetLanguageMode(LanguageMode::kStrict);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2538,8 +2526,13 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseMemberInitializer(
|
|||||||
initializer = factory()->NewUndefinedLiteral(kNoSourcePosition);
|
initializer = factory()->NewUndefinedLiteral(kNoSourcePosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
initializer_scope->set_end_position(end_position());
|
|
||||||
if (is_static) {
|
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->static_elements_scope = initializer_scope;
|
||||||
class_info->has_static_elements = true;
|
class_info->has_static_elements = true;
|
||||||
} else {
|
} else {
|
||||||
@ -4255,7 +4248,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseClassDeclaration(
|
|||||||
}
|
}
|
||||||
|
|
||||||
ExpressionParsingScope no_expression_scope(impl());
|
ExpressionParsingScope no_expression_scope(impl());
|
||||||
ExpressionT value = ParseClassLiteral(name, scanner()->location(),
|
ExpressionT value = ParseClassLiteral(scope(), name, scanner()->location(),
|
||||||
is_strict_reserved, class_token_pos);
|
is_strict_reserved, class_token_pos);
|
||||||
no_expression_scope.ValidateExpression();
|
no_expression_scope.ValidateExpression();
|
||||||
int end_pos = position();
|
int end_pos = position();
|
||||||
@ -4660,9 +4653,27 @@ ParserBase<Impl>::ParseArrowFunctionLiteral(
|
|||||||
return function_literal;
|
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>
|
template <typename Impl>
|
||||||
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseClassLiteral(
|
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 name_is_strict_reserved, int class_token_pos) {
|
||||||
bool is_anonymous = impl()->IsNull(name);
|
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);
|
BlockState block_state(&scope_, class_scope);
|
||||||
RaiseLanguageMode(LanguageMode::kStrict);
|
RaiseLanguageMode(LanguageMode::kStrict);
|
||||||
|
|
||||||
@ -4767,6 +4778,12 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseClassLiteral(
|
|||||||
Expect(Token::RBRACE);
|
Expect(Token::RBRACE);
|
||||||
int end_pos = end_position();
|
int end_pos = end_position();
|
||||||
class_scope->set_end_position(end_pos);
|
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();
|
VariableProxy* unresolvable = class_scope->ResolvePrivateNamesPartially();
|
||||||
if (unresolvable != nullptr) {
|
if (unresolvable != nullptr) {
|
||||||
|
@ -832,7 +832,30 @@ void Parser::ParseFunction(Isolate* isolate, ParseInfo* info,
|
|||||||
if (shared_info->HasOuterScopeInfo()) {
|
if (shared_info->HasOuterScopeInfo()) {
|
||||||
maybe_outer_scope_info = handle(shared_info->GetOuterScopeInfo(), isolate);
|
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);
|
Scope::DeserializationMode::kIncludingVariables);
|
||||||
DCHECK_EQ(factory()->zone(), info->zone());
|
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);
|
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();
|
int function_literal_id = shared_info->function_literal_id();
|
||||||
if V8_UNLIKELY (script->type() == Script::TYPE_WEB_SNAPSHOT) {
|
if V8_UNLIKELY (script->type() == Script::TYPE_WEB_SNAPSHOT) {
|
||||||
// Function literal IDs for inner functions haven't been allocated when
|
// 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
|
// function is in heritage position. Otherwise the function scope's skip bit
|
||||||
// will be correctly inherited from the outer scope.
|
// will be correctly inherited from the outer scope.
|
||||||
ClassScope::HeritageParsingScope heritage(original_scope_->AsClassScope());
|
ClassScope::HeritageParsingScope heritage(original_scope_->AsClassScope());
|
||||||
result = DoParseFunction(isolate, info, start_position, end_position,
|
result = DoParseDeserializedFunction(
|
||||||
function_literal_id, info->function_name());
|
isolate, maybe_outer_scope_info, info, start_position, end_position,
|
||||||
|
function_literal_id, info->function_name());
|
||||||
} else {
|
} else {
|
||||||
result = DoParseFunction(isolate, info, start_position, end_position,
|
result = DoParseDeserializedFunction(
|
||||||
function_literal_id, info->function_name());
|
isolate, maybe_outer_scope_info, info, start_position, end_position,
|
||||||
|
function_literal_id, info->function_name());
|
||||||
}
|
}
|
||||||
MaybeProcessSourceRanges(info, result, stack_limit_);
|
MaybeProcessSourceRanges(info, result, stack_limit_);
|
||||||
if (result != nullptr) {
|
if (result != nullptr) {
|
||||||
@ -1033,6 +1056,81 @@ FunctionLiteral* Parser::DoParseFunction(Isolate* isolate, ParseInfo* info,
|
|||||||
return result;
|
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() {
|
Statement* Parser::ParseModuleItem() {
|
||||||
// ecma262/#prod-ModuleItem
|
// ecma262/#prod-ModuleItem
|
||||||
// ModuleItem :
|
// ModuleItem :
|
||||||
@ -3123,7 +3221,9 @@ FunctionLiteral* Parser::CreateInitializerFunction(
|
|||||||
FunctionSyntaxKind::kAccessorOrMethod,
|
FunctionSyntaxKind::kAccessorOrMethod,
|
||||||
FunctionLiteral::kShouldEagerCompile, scope->start_position(), false,
|
FunctionLiteral::kShouldEagerCompile, scope->start_position(), false,
|
||||||
GetNextFunctionLiteralId());
|
GetNextFunctionLiteralId());
|
||||||
|
#ifdef DEBUG
|
||||||
|
scope->SetScopeName(ast_value_factory()->GetOneByteString(name));
|
||||||
|
#endif
|
||||||
RecordFunctionLiteralSourceRange(result);
|
RecordFunctionLiteralSourceRange(result);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -237,6 +237,15 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
|
|||||||
int function_literal_id,
|
int function_literal_id,
|
||||||
const AstRawString* raw_name);
|
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.
|
// Called by ParseProgram after setting up the scanner.
|
||||||
FunctionLiteral* DoParseProgram(Isolate* isolate, ParseInfo* info);
|
FunctionLiteral* DoParseProgram(Isolate* isolate, ParseInfo* info);
|
||||||
|
|
||||||
|
@ -4156,6 +4156,590 @@ UNINITIALIZED_TEST(ReinitializeHashSeedRehashable) {
|
|||||||
FreeCurrentEmbeddedBlob();
|
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 PrivateFieldClass {"
|
||||||
|
" #consturctor = PrivateFieldClass;"
|
||||||
|
" func() {"
|
||||||
|
" return this.#consturctor;"
|
||||||
|
" }"
|
||||||
|
"}"
|
||||||
|
"class PublicFieldClass {"
|
||||||
|
" ctor = PublicFieldClass;"
|
||||||
|
" func() {"
|
||||||
|
" return this.ctor;"
|
||||||
|
" }"
|
||||||
|
"}");
|
||||||
|
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 PrivateFieldClass().func() === PrivateFieldClass");
|
||||||
|
ExpectTrue("new PublicFieldClass().func() === PublicFieldClass");
|
||||||
|
}
|
||||||
|
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) {
|
void CheckSFIsAreWeak(WeakFixedArray sfis, Isolate* isolate) {
|
||||||
CHECK_GT(sfis.length(), 0);
|
CHECK_GT(sfis.length(), 0);
|
||||||
int no_of_weak = 0;
|
int no_of_weak = 0;
|
||||||
|
@ -22,41 +22,41 @@ var b1, b2, b3;
|
|||||||
// y = [B1]2;
|
// y = [B1]2;
|
||||||
// z = [B2]3;
|
// z = [B2]3;
|
||||||
// }
|
// }
|
||||||
b1 = Debug.setBreakPoint(initializer, 0, 0);
|
b1 = Debug.setBreakPoint(initializer, 1, 0);
|
||||||
assertTrue(Debug.showBreakPoints(initializer).indexOf("x = [B0]1;") === 0);
|
assertTrue(Debug.showBreakPoints(initializer).indexOf("x = [B0]1;") > 0);
|
||||||
Debug.clearBreakPoint(b1);
|
Debug.clearBreakPoint(b1);
|
||||||
assertTrue(Debug.showBreakPoints(initializer).indexOf("x = [B0]1;") === -1);
|
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);
|
assertTrue(Debug.showBreakPoints(initializer).indexOf("y = [B0]2;") > 0);
|
||||||
Debug.clearBreakPoint(b2);
|
Debug.clearBreakPoint(b2);
|
||||||
assertTrue(Debug.showBreakPoints(initializer).indexOf("y = [B0]2;") === -1);
|
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);
|
assertTrue(Debug.showBreakPoints(initializer).indexOf("z = [B0]3") > 0);
|
||||||
Debug.clearBreakPoint(b3);
|
Debug.clearBreakPoint(b3);
|
||||||
assertTrue(Debug.showBreakPoints(initializer).indexOf("z = [B0]3") === -1);
|
assertTrue(Debug.showBreakPoints(initializer).indexOf("z = [B0]3") === -1);
|
||||||
|
|
||||||
b1 = Debug.setBreakPoint(initializer, 0, 0);
|
b1 = Debug.setBreakPoint(initializer, 1, 0);
|
||||||
b2 = 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("x = [B0]1;") > 0);
|
||||||
assertTrue(Debug.showBreakPoints(initializer).indexOf("y = [B1]2;") > 0);
|
assertTrue(Debug.showBreakPoints(initializer).indexOf("y = [B1]2;") > 0);
|
||||||
Debug.clearBreakPoint(b1);
|
Debug.clearBreakPoint(b1);
|
||||||
assertTrue(Debug.showBreakPoints(initializer).indexOf("x = [B0]1;") === -1);
|
assertTrue(Debug.showBreakPoints(initializer).indexOf("x = [B0]1;") === -1);
|
||||||
Debug.clearBreakPoint(b2);
|
Debug.clearBreakPoint(b2);
|
||||||
assertTrue(Debug.showBreakPoints(initializer).indexOf("y = [B1]2;") === -1);
|
assertTrue(Debug.showBreakPoints(initializer).indexOf("y = [B1]2;") === -1);
|
||||||
|
|
||||||
b1 = Debug.setBreakPoint(initializer, 0, 0);
|
b1 = Debug.setBreakPoint(initializer, 1, 0);
|
||||||
b3 = Debug.setBreakPoint(initializer, 2, 0);
|
b3 = Debug.setBreakPoint(initializer, 3, 0);
|
||||||
assertTrue(Debug.showBreakPoints(initializer).indexOf("x = [B0]1;") === 0);
|
assertTrue(Debug.showBreakPoints(initializer).indexOf("x = [B0]1;") > 0);
|
||||||
assertTrue(Debug.showBreakPoints(initializer).indexOf("z = [B1]3") > 0);
|
assertTrue(Debug.showBreakPoints(initializer).indexOf("z = [B1]3") > 0);
|
||||||
Debug.clearBreakPoint(b1);
|
Debug.clearBreakPoint(b1);
|
||||||
assertTrue(Debug.showBreakPoints(initializer).indexOf("x = [B0]1;") === -1);
|
assertTrue(Debug.showBreakPoints(initializer).indexOf("x = [B0]1;") === -1);
|
||||||
Debug.clearBreakPoint(b3);
|
Debug.clearBreakPoint(b3);
|
||||||
assertTrue(Debug.showBreakPoints(initializer).indexOf("z = [B1]3") === -1);
|
assertTrue(Debug.showBreakPoints(initializer).indexOf("z = [B1]3") === -1);
|
||||||
|
|
||||||
b2 = Debug.setBreakPoint(initializer, 1, 0);
|
b2 = Debug.setBreakPoint(initializer, 2, 0);
|
||||||
b3 = 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("y = [B0]2;") > 0);
|
||||||
assertTrue(Debug.showBreakPoints(initializer).indexOf("z = [B1]3") > 0);
|
assertTrue(Debug.showBreakPoints(initializer).indexOf("z = [B1]3") > 0);
|
||||||
Debug.clearBreakPoint(b2);
|
Debug.clearBreakPoint(b2);
|
||||||
@ -83,11 +83,11 @@ class X {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
initializer = %GetInitializerFunction(X);
|
initializer = %GetInitializerFunction(X);
|
||||||
b1 = Debug.setBreakPoint(initializer, 0, 0);
|
b1 = Debug.setBreakPoint(initializer, 1, 0);
|
||||||
assertTrue(Debug.showBreakPoints(initializer).indexOf('[foo()] = 1;') === 0);
|
assertTrue(Debug.showBreakPoints(initializer).indexOf('[foo()] = 1;') > 0);
|
||||||
Debug.clearBreakPoint(b1);
|
Debug.clearBreakPoint(b1);
|
||||||
|
|
||||||
b1 = Debug.setBreakPoint(initializer, 1, 0);
|
b1 = Debug.setBreakPoint(initializer, 2, 0);
|
||||||
assertTrue(Debug.showBreakPoints(initializer).indexOf('baz = [B0]foo()') > 0);
|
assertTrue(Debug.showBreakPoints(initializer).indexOf('baz = [B0]foo()') > 0);
|
||||||
Debug.clearBreakPoint(b1);
|
Debug.clearBreakPoint(b1);
|
||||||
|
|
||||||
|
@ -90,8 +90,8 @@ Running test: testScopesPaused
|
|||||||
scopeChain : [
|
scopeChain : [
|
||||||
[0] : {
|
[0] : {
|
||||||
endLocation : {
|
endLocation : {
|
||||||
columnNumber : 13
|
columnNumber : 3
|
||||||
lineNumber : 14
|
lineNumber : 15
|
||||||
scriptId : <scriptId>
|
scriptId : <scriptId>
|
||||||
}
|
}
|
||||||
name : run
|
name : run
|
||||||
@ -102,8 +102,8 @@ Running test: testScopesPaused
|
|||||||
type : object
|
type : object
|
||||||
}
|
}
|
||||||
startLocation : {
|
startLocation : {
|
||||||
columnNumber : 4
|
columnNumber : 2
|
||||||
lineNumber : 12
|
lineNumber : 11
|
||||||
scriptId : <scriptId>
|
scriptId : <scriptId>
|
||||||
}
|
}
|
||||||
type : local
|
type : local
|
||||||
@ -183,8 +183,8 @@ Running test: testScopesPaused
|
|||||||
[0] : {
|
[0] : {
|
||||||
callFrameId : <callFrameId>
|
callFrameId : <callFrameId>
|
||||||
functionLocation : {
|
functionLocation : {
|
||||||
columnNumber : 4
|
columnNumber : 2
|
||||||
lineNumber : 12
|
lineNumber : 11
|
||||||
scriptId : <scriptId>
|
scriptId : <scriptId>
|
||||||
}
|
}
|
||||||
functionName : <instance_members_initializer>
|
functionName : <instance_members_initializer>
|
||||||
@ -364,8 +364,8 @@ Running test: testScopesPaused
|
|||||||
[0] : {
|
[0] : {
|
||||||
callFrameId : <callFrameId>
|
callFrameId : <callFrameId>
|
||||||
functionLocation : {
|
functionLocation : {
|
||||||
columnNumber : 4
|
columnNumber : 2
|
||||||
lineNumber : 12
|
lineNumber : 11
|
||||||
scriptId : <scriptId>
|
scriptId : <scriptId>
|
||||||
}
|
}
|
||||||
functionName : <instance_members_initializer>
|
functionName : <instance_members_initializer>
|
||||||
@ -545,8 +545,8 @@ Running test: testScopesPaused
|
|||||||
[0] : {
|
[0] : {
|
||||||
callFrameId : <callFrameId>
|
callFrameId : <callFrameId>
|
||||||
functionLocation : {
|
functionLocation : {
|
||||||
columnNumber : 4
|
columnNumber : 2
|
||||||
lineNumber : 12
|
lineNumber : 11
|
||||||
scriptId : <scriptId>
|
scriptId : <scriptId>
|
||||||
}
|
}
|
||||||
functionName : <instance_members_initializer>
|
functionName : <instance_members_initializer>
|
||||||
@ -798,8 +798,8 @@ Running test: testScopesPaused
|
|||||||
[1] : {
|
[1] : {
|
||||||
callFrameId : <callFrameId>
|
callFrameId : <callFrameId>
|
||||||
functionLocation : {
|
functionLocation : {
|
||||||
columnNumber : 4
|
columnNumber : 2
|
||||||
lineNumber : 12
|
lineNumber : 11
|
||||||
scriptId : <scriptId>
|
scriptId : <scriptId>
|
||||||
}
|
}
|
||||||
functionName : <instance_members_initializer>
|
functionName : <instance_members_initializer>
|
||||||
|
@ -7,23 +7,23 @@ let x = |R|class {}
|
|||||||
|
|
||||||
|_|x = |R|class {
|
|_|x = |R|class {
|
||||||
x = |_|1;
|
x = |_|1;
|
||||||
y = |_|2|R|;
|
y = |_|2;
|
||||||
}
|
}|R|
|
||||||
|
|
||||||
|_|x = |R|class {
|
|_|x = |R|class {
|
||||||
x = |C|foo();
|
x = |C|foo();
|
||||||
y = |_|2;
|
y = |_|2;
|
||||||
z = |C|bar()|R|;
|
z = |C|bar();
|
||||||
}
|
}|R|
|
||||||
|
|
||||||
|_|x = class {
|
|_|x = class {
|
||||||
x = |C|foo();
|
x = |C|foo();
|
||||||
y = |_|2;
|
y = |_|2;
|
||||||
z = |C|bar()|R|;
|
z = |C|bar();
|
||||||
constructor() {
|
constructor() {
|
||||||
this.|_|x;
|
this.|_|x;
|
||||||
|R|}
|
|R|}
|
||||||
}
|
}|R|
|
||||||
|
|
||||||
|_|x = class {
|
|_|x = class {
|
||||||
x = |C|foo();
|
x = |C|foo();
|
||||||
@ -31,8 +31,8 @@ let x = |R|class {}
|
|||||||
constructor() {
|
constructor() {
|
||||||
this.|_|x;
|
this.|_|x;
|
||||||
|R|}
|
|R|}
|
||||||
z = |C|bar()|R|;
|
z = |C|bar();
|
||||||
}
|
}|R|
|
||||||
|
|
||||||
|_|x = class {
|
|_|x = class {
|
||||||
x = |C|foo();
|
x = |C|foo();
|
||||||
@ -40,14 +40,14 @@ let x = |R|class {}
|
|||||||
constructor() {
|
constructor() {
|
||||||
this.|_|x;
|
this.|_|x;
|
||||||
|R|}
|
|R|}
|
||||||
z = |C|bar()|R|;
|
z = |C|bar();
|
||||||
}
|
}|R|
|
||||||
|
|
||||||
|_|x = |R|class {
|
|_|x = |R|class {
|
||||||
x = |_|1;
|
x = |_|1;
|
||||||
foo() {|R|}
|
foo() {|R|}
|
||||||
y = |_|2|R|;
|
y = |_|2;
|
||||||
}
|
}|R|
|
||||||
|
|
||||||
|_|x = |R|class {
|
|_|x = |R|class {
|
||||||
x = (function() {
|
x = (function() {
|
||||||
@ -55,54 +55,54 @@ let x = |R|class {}
|
|||||||
|R|})|C|();
|
|R|})|C|();
|
||||||
y = (() => {
|
y = (() => {
|
||||||
|C|bar();
|
|C|bar();
|
||||||
|R|})|C|()|R|;
|
|R|})|C|();
|
||||||
}
|
}|R|
|
||||||
|
|
||||||
|_|x = |R|class {
|
|_|x = |R|class {
|
||||||
x = |_|function() {
|
x = |_|function() {
|
||||||
|C|foo();
|
|C|foo();
|
||||||
|R|}|R|;
|
|R|};
|
||||||
}
|
}|R|
|
||||||
|
|
||||||
|_|x = |R|class {
|
|_|x = |R|class {
|
||||||
x = |_|async function() {
|
x = |_|async function() {
|
||||||
|_|await |C|foo();
|
|_|await |C|foo();
|
||||||
|R|}|R|;
|
|R|};
|
||||||
}
|
}|R|
|
||||||
|
|
||||||
|_|x = |R|class {
|
|_|x = |R|class {
|
||||||
x = |_|() => {
|
x = |_|() => {
|
||||||
|C|foo();
|
|C|foo();
|
||||||
|R|};
|
|R|};
|
||||||
y = |_|() => |C|bar()|R|;
|
y = |_|() => |C|bar()|R|;
|
||||||
}
|
}|R|
|
||||||
|
|
||||||
|_|x = |R|class {
|
|_|x = |R|class {
|
||||||
x = |_|async () => {
|
x = |_|async () => {
|
||||||
|_|await |C|foo();
|
|_|await |C|foo();
|
||||||
|R|};
|
|R|};
|
||||||
y = |_|async () => |_|await |C|bar()|R|;
|
y = |_|async () => |_|await |C|bar()|R|;
|
||||||
}
|
}|R|
|
||||||
|
|
||||||
|_|x = |R|class {
|
|_|x = |R|class {
|
||||||
[|_|x] = |_|1;
|
[|_|x] = |_|1;
|
||||||
[|C|foo()] = |_|2|R|;
|
[|C|foo()] = |_|2;
|
||||||
}
|
}|R|
|
||||||
|
|
||||||
|_|x = |R|class {
|
|_|x = |R|class {
|
||||||
[|_|x] = |_|[...this]|R|;
|
[|_|x] = |_|[...this];
|
||||||
}
|
}|R|
|
||||||
|
|
||||||
|_|x = |R|class {
|
|_|x = |R|class {
|
||||||
x;
|
x;
|
||||||
[|C|foo()]|R|;
|
[|C|foo()];
|
||||||
}
|
}|R|
|
||||||
|
|
||||||
|_|x = |R|class {
|
|_|x = |R|class {
|
||||||
x = |_|function*|_|() {
|
x = |_|function*|_|() {
|
||||||
|_|yield 1;
|
|_|yield 1;
|
||||||
|R|}|R|;
|
|R|};
|
||||||
}
|
}|R|
|
||||||
|
|
||||||
|_|x = |R|class {
|
|_|x = |R|class {
|
||||||
static x = |_|1;
|
static x = |_|1;
|
||||||
@ -201,6 +201,6 @@ let x = |R|class {}
|
|||||||
static [|_|z] = |_|3;
|
static [|_|z] = |_|3;
|
||||||
[|_|p] = |_|4;
|
[|_|p] = |_|4;
|
||||||
static [|C|foo()] = |_|5|R|;
|
static [|C|foo()] = |_|5|R|;
|
||||||
[|C|bar()] = |_|6|R|;
|
[|C|bar()] = |_|6;
|
||||||
}
|
}|R|
|
||||||
|R|
|
|R|
|
||||||
|
@ -25,7 +25,7 @@ class X { // 000
|
|||||||
}; // 100
|
}; // 100
|
||||||
`,
|
`,
|
||||||
[{"start":0,"end":149,"count":1},
|
[{"start":0,"end":149,"count":1},
|
||||||
{"start":52,"end":70,"count":0}]
|
{"start":0,"end":101,"count":0}]
|
||||||
);
|
);
|
||||||
|
|
||||||
TestCoverage(
|
TestCoverage(
|
||||||
@ -37,7 +37,7 @@ class X { // 000
|
|||||||
let x = new X(); // 150
|
let x = new X(); // 150
|
||||||
`,
|
`,
|
||||||
[{"start":0,"end":199,"count":1},
|
[{"start":0,"end":199,"count":1},
|
||||||
{"start":52,"end":70,"count":1},
|
{"start":0,"end":101,"count":1},
|
||||||
{"start":56,"end":70,"count":0}]
|
{"start":56,"end":70,"count":0}]
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ let x = new X(); // 150
|
|||||||
x.x(); // 200
|
x.x(); // 200
|
||||||
`,
|
`,
|
||||||
[{"start":0,"end":249,"count":1},
|
[{"start":0,"end":249,"count":1},
|
||||||
{"start":52,"end":70,"count":1},
|
{"start":0,"end":101,"count":1},
|
||||||
{"start":56,"end":70,"count":1}]
|
{"start":56,"end":70,"count":1}]
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ x.x(); // 300
|
|||||||
x.y(); // 350
|
x.y(); // 350
|
||||||
`,
|
`,
|
||||||
[{"start":0,"end":399,"count":1},
|
[{"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":56,"end":70,"count":1},
|
||||||
{"start":102,"end":111,"count":0},
|
{"start":102,"end":111,"count":0},
|
||||||
{"start":156,"end":169,"count":1}]
|
{"start":156,"end":169,"count":1}]
|
||||||
@ -88,7 +88,7 @@ x.y(); // 350
|
|||||||
x.foo(); // 400
|
x.foo(); // 400
|
||||||
`,
|
`,
|
||||||
[{"start":0,"end":449,"count":1},
|
[{"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":56,"end":70,"count":1},
|
||||||
{"start":102,"end":111,"count":1},
|
{"start":102,"end":111,"count":1},
|
||||||
{"start":156,"end":169,"count":1}]
|
{"start":156,"end":169,"count":1}]
|
||||||
@ -103,7 +103,7 @@ class X { // 000
|
|||||||
let x = new X(); // 150
|
let x = new X(); // 150
|
||||||
`,
|
`,
|
||||||
[{"start":0,"end":199,"count":1},
|
[{"start":0,"end":199,"count":1},
|
||||||
{"start":52,"end":74,"count":1},
|
{"start":0,"end":101,"count":1},
|
||||||
{"start":57,"end":71,"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":249,"count":1},
|
||||||
{"start":0,"end":15,"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}]
|
{"start":111,"end":125,"count":1}]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
79
test/mjsunit/regress/regress-crbug-1278086.js
Normal file
79
test/mjsunit/regress/regress-crbug-1278086.js
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
// 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.
|
// Generate BytecodeArray.
|
||||||
Handle<ScopeInfo> scope_info =
|
Handle<ScopeInfo> scope_info =
|
||||||
factory->NewScopeInfo(ScopeInfo::kVariablePartIndex);
|
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_context_local_count(0);
|
||||||
scope_info->set_parameter_count(0);
|
scope_info->set_parameter_count(0);
|
||||||
scope.SetScriptScopeInfo(scope_info);
|
scope.SetScriptScopeInfo(scope_info);
|
||||||
|
Loading…
Reference in New Issue
Block a user