[parser] Use std::vector as pointer-buffer for ScopedPtrList
This allows us to use one underlying buffer rather than 3, and allows memory to be freed as we're growing the vector. Change-Id: I45c178a31e8f6d3ee44d3319ce8bca2db2460d33 Reviewed-on: https://chromium-review.googlesource.com/c/1297328 Reviewed-by: Igor Sheludko <ishell@chromium.org> Commit-Queue: Toon Verwaest <verwaest@chromium.org> Cr-Commit-Position: refs/heads/master@{#56966}
This commit is contained in:
parent
a6423cca4a
commit
840242f4c4
@ -862,10 +862,10 @@ Call::CallType Call::GetCallType() const {
|
||||
return OTHER_CALL;
|
||||
}
|
||||
|
||||
CaseClause::CaseClause(Expression* label,
|
||||
CaseClause::CaseClause(Zone* zone, Expression* label,
|
||||
const ScopedPtrList<Statement>& statements)
|
||||
: label_(label), statements_(0, nullptr) {
|
||||
statements.CopyTo(&statements_);
|
||||
statements.CopyTo(&statements_, zone);
|
||||
}
|
||||
|
||||
bool Literal::IsPropertyName() const {
|
||||
|
@ -809,7 +809,8 @@ class CaseClause final : public ZoneObject {
|
||||
private:
|
||||
friend class AstNodeFactory;
|
||||
|
||||
CaseClause(Expression* label, const ScopedPtrList<Statement>& statements);
|
||||
CaseClause(Zone* zone, Expression* label,
|
||||
const ScopedPtrList<Statement>& statements);
|
||||
|
||||
Expression* label_;
|
||||
ZonePtrList<Statement> statements_;
|
||||
@ -1403,7 +1404,7 @@ class ObjectLiteral final : public AggregateLiteral {
|
||||
private:
|
||||
friend class AstNodeFactory;
|
||||
|
||||
ObjectLiteral(const ScopedPtrList<Property>& properties,
|
||||
ObjectLiteral(Zone* zone, const ScopedPtrList<Property>& properties,
|
||||
uint32_t boilerplate_properties, int pos,
|
||||
bool has_rest_property)
|
||||
: AggregateLiteral(pos, kObjectLiteral),
|
||||
@ -1413,7 +1414,7 @@ class ObjectLiteral final : public AggregateLiteral {
|
||||
HasRestPropertyField::encode(has_rest_property) |
|
||||
FastElementsField::encode(false) |
|
||||
HasNullPrototypeField::encode(false);
|
||||
properties.CopyTo(&properties_);
|
||||
properties.CopyTo(&properties_, zone);
|
||||
}
|
||||
|
||||
void InitFlagsForPendingNullPrototype(int i);
|
||||
@ -1506,12 +1507,12 @@ class ArrayLiteral final : public AggregateLiteral {
|
||||
private:
|
||||
friend class AstNodeFactory;
|
||||
|
||||
ArrayLiteral(const ScopedPtrList<Expression>& values, int first_spread_index,
|
||||
int pos)
|
||||
ArrayLiteral(Zone* zone, const ScopedPtrList<Expression>& values,
|
||||
int first_spread_index, int pos)
|
||||
: AggregateLiteral(pos, kArrayLiteral),
|
||||
first_spread_index_(first_spread_index),
|
||||
values_(0, nullptr) {
|
||||
values.CopyTo(&values_);
|
||||
values.CopyTo(&values_, zone);
|
||||
}
|
||||
|
||||
int first_spread_index_;
|
||||
@ -1736,25 +1737,27 @@ class Call final : public Expression {
|
||||
private:
|
||||
friend class AstNodeFactory;
|
||||
|
||||
Call(Expression* expression, const ScopedPtrList<Expression>& arguments,
|
||||
int pos, PossiblyEval possibly_eval)
|
||||
Call(Zone* zone, Expression* expression,
|
||||
const ScopedPtrList<Expression>& arguments, int pos,
|
||||
PossiblyEval possibly_eval)
|
||||
: Expression(pos, kCall),
|
||||
expression_(expression),
|
||||
arguments_(0, nullptr) {
|
||||
bit_field_ |=
|
||||
IsPossiblyEvalField::encode(possibly_eval == IS_POSSIBLY_EVAL) |
|
||||
IsTaggedTemplateField::encode(false);
|
||||
arguments.CopyTo(&arguments_);
|
||||
arguments.CopyTo(&arguments_, zone);
|
||||
}
|
||||
|
||||
Call(Expression* expression, const ScopedPtrList<Expression>& arguments,
|
||||
int pos, TaggedTemplateTag tag)
|
||||
Call(Zone* zone, Expression* expression,
|
||||
const ScopedPtrList<Expression>& arguments, int pos,
|
||||
TaggedTemplateTag tag)
|
||||
: Expression(pos, kCall),
|
||||
expression_(expression),
|
||||
arguments_(0, nullptr) {
|
||||
bit_field_ |= IsPossiblyEvalField::encode(false) |
|
||||
IsTaggedTemplateField::encode(true);
|
||||
arguments.CopyTo(&arguments_);
|
||||
arguments.CopyTo(&arguments_, zone);
|
||||
}
|
||||
|
||||
class IsPossiblyEvalField
|
||||
@ -1779,12 +1782,12 @@ class CallNew final : public Expression {
|
||||
private:
|
||||
friend class AstNodeFactory;
|
||||
|
||||
CallNew(Expression* expression, const ScopedPtrList<Expression>& arguments,
|
||||
int pos)
|
||||
CallNew(Zone* zone, Expression* expression,
|
||||
const ScopedPtrList<Expression>& arguments, int pos)
|
||||
: Expression(pos, kCallNew),
|
||||
expression_(expression),
|
||||
arguments_(0, nullptr) {
|
||||
arguments.CopyTo(&arguments_);
|
||||
arguments.CopyTo(&arguments_, zone);
|
||||
}
|
||||
|
||||
Expression* expression_;
|
||||
@ -1814,20 +1817,20 @@ class CallRuntime final : public Expression {
|
||||
private:
|
||||
friend class AstNodeFactory;
|
||||
|
||||
CallRuntime(const Runtime::Function* function,
|
||||
CallRuntime(Zone* zone, const Runtime::Function* function,
|
||||
const ScopedPtrList<Expression>& arguments, int pos)
|
||||
: Expression(pos, kCallRuntime),
|
||||
function_(function),
|
||||
arguments_(0, nullptr) {
|
||||
arguments.CopyTo(&arguments_);
|
||||
arguments.CopyTo(&arguments_, zone);
|
||||
}
|
||||
CallRuntime(int context_index, const ScopedPtrList<Expression>& arguments,
|
||||
int pos)
|
||||
CallRuntime(Zone* zone, int context_index,
|
||||
const ScopedPtrList<Expression>& arguments, int pos)
|
||||
: Expression(pos, kCallRuntime),
|
||||
context_index_(context_index),
|
||||
function_(nullptr),
|
||||
arguments_(0, nullptr) {
|
||||
arguments.CopyTo(&arguments_);
|
||||
arguments.CopyTo(&arguments_, zone);
|
||||
}
|
||||
|
||||
int context_index_;
|
||||
@ -3018,7 +3021,7 @@ class AstNodeFactory final {
|
||||
|
||||
CaseClause* NewCaseClause(Expression* label,
|
||||
const ScopedPtrList<Statement>& statements) {
|
||||
return new (zone_) CaseClause(label, statements);
|
||||
return new (zone_) CaseClause(zone_, label, statements);
|
||||
}
|
||||
|
||||
Literal* NewStringLiteral(const AstRawString* string, int pos) {
|
||||
@ -3059,8 +3062,8 @@ class AstNodeFactory final {
|
||||
ObjectLiteral* NewObjectLiteral(
|
||||
const ScopedPtrList<ObjectLiteral::Property>& properties,
|
||||
uint32_t boilerplate_properties, int pos, bool has_rest_property) {
|
||||
return new (zone_) ObjectLiteral(properties, boilerplate_properties, pos,
|
||||
has_rest_property);
|
||||
return new (zone_) ObjectLiteral(zone_, properties, boilerplate_properties,
|
||||
pos, has_rest_property);
|
||||
}
|
||||
|
||||
ObjectLiteral::Property* NewObjectLiteralProperty(
|
||||
@ -3084,12 +3087,12 @@ class AstNodeFactory final {
|
||||
|
||||
ArrayLiteral* NewArrayLiteral(const ScopedPtrList<Expression>& values,
|
||||
int pos) {
|
||||
return new (zone_) ArrayLiteral(values, -1, pos);
|
||||
return new (zone_) ArrayLiteral(zone_, values, -1, pos);
|
||||
}
|
||||
|
||||
ArrayLiteral* NewArrayLiteral(const ScopedPtrList<Expression>& values,
|
||||
int first_spread_index, int pos) {
|
||||
return new (zone_) ArrayLiteral(values, first_spread_index, pos);
|
||||
return new (zone_) ArrayLiteral(zone_, values, first_spread_index, pos);
|
||||
}
|
||||
|
||||
VariableProxy* NewVariableProxy(Variable* var,
|
||||
@ -3126,36 +3129,37 @@ class AstNodeFactory final {
|
||||
Call* NewCall(Expression* expression,
|
||||
const ScopedPtrList<Expression>& arguments, int pos,
|
||||
Call::PossiblyEval possibly_eval = Call::NOT_EVAL) {
|
||||
return new (zone_) Call(expression, arguments, pos, possibly_eval);
|
||||
return new (zone_) Call(zone_, expression, arguments, pos, possibly_eval);
|
||||
}
|
||||
|
||||
Call* NewTaggedTemplate(Expression* expression,
|
||||
const ScopedPtrList<Expression>& arguments, int pos) {
|
||||
return new (zone_)
|
||||
Call(expression, arguments, pos, Call::TaggedTemplateTag::kTrue);
|
||||
Call(zone_, expression, arguments, pos, Call::TaggedTemplateTag::kTrue);
|
||||
}
|
||||
|
||||
CallNew* NewCallNew(Expression* expression,
|
||||
const ScopedPtrList<Expression>& arguments, int pos) {
|
||||
return new (zone_) CallNew(expression, arguments, pos);
|
||||
return new (zone_) CallNew(zone_, expression, arguments, pos);
|
||||
}
|
||||
|
||||
CallRuntime* NewCallRuntime(Runtime::FunctionId id,
|
||||
const ScopedPtrList<Expression>& arguments,
|
||||
int pos) {
|
||||
return new (zone_) CallRuntime(Runtime::FunctionForId(id), arguments, pos);
|
||||
return new (zone_)
|
||||
CallRuntime(zone_, Runtime::FunctionForId(id), arguments, pos);
|
||||
}
|
||||
|
||||
CallRuntime* NewCallRuntime(const Runtime::Function* function,
|
||||
const ScopedPtrList<Expression>& arguments,
|
||||
int pos) {
|
||||
return new (zone_) CallRuntime(function, arguments, pos);
|
||||
return new (zone_) CallRuntime(zone_, function, arguments, pos);
|
||||
}
|
||||
|
||||
CallRuntime* NewCallRuntime(int context_index,
|
||||
const ScopedPtrList<Expression>& arguments,
|
||||
int pos) {
|
||||
return new (zone_) CallRuntime(context_index, arguments, pos);
|
||||
return new (zone_) CallRuntime(zone_, context_index, arguments, pos);
|
||||
}
|
||||
|
||||
UnaryOperation* NewUnaryOperation(Token::Value op,
|
||||
|
@ -207,14 +207,12 @@ class ParserBase {
|
||||
typedef typename Types::ClassLiteralProperty ClassLiteralPropertyT;
|
||||
typedef typename Types::Suspend SuspendExpressionT;
|
||||
typedef typename Types::RewritableExpression RewritableExpressionT;
|
||||
typedef typename Types::ExpressionList ExpressionListT;
|
||||
typedef typename Types::ObjectPropertyList ObjectPropertyListT;
|
||||
typedef typename Types::ScopedObjectPropertyList ScopedObjectPropertyListT;
|
||||
typedef typename Types::FormalParameters FormalParametersT;
|
||||
typedef typename Types::Statement StatementT;
|
||||
typedef typename Types::StatementList StatementListT;
|
||||
typedef typename Types::ScopedStatementList ScopedStatementListT;
|
||||
typedef typename Types::ScopedExpressionList ScopedExpressionListT;
|
||||
typedef typename Types::ExpressionList ExpressionListT;
|
||||
typedef typename Types::Block BlockT;
|
||||
typedef typename Types::ForStatement ForStatementT;
|
||||
typedef typename v8::internal::ExpressionClassifier<Types>
|
||||
@ -248,9 +246,6 @@ class ParserBase {
|
||||
pending_error_handler_(pending_error_handler),
|
||||
zone_(zone),
|
||||
classifier_(nullptr),
|
||||
statement_buffer_(impl()->NewStatementList(32)),
|
||||
expression_buffer_(impl()->NewExpressionList(32)),
|
||||
property_buffer_(impl()->NewObjectPropertyList(32)),
|
||||
scanner_(scanner),
|
||||
default_eager_compile_hint_(FunctionLiteral::kShouldLazyCompile),
|
||||
function_literal_id_(0),
|
||||
@ -262,7 +257,9 @@ class ParserBase {
|
||||
allow_harmony_dynamic_import_(false),
|
||||
allow_harmony_import_meta_(false),
|
||||
allow_harmony_private_fields_(false),
|
||||
allow_eval_cache_(true) {}
|
||||
allow_eval_cache_(true) {
|
||||
pointer_buffer_.reserve(128);
|
||||
}
|
||||
|
||||
#define ALLOW_ACCESSORS(name) \
|
||||
bool allow_##name() const { return allow_##name##_; } \
|
||||
@ -1063,9 +1060,9 @@ class ParserBase {
|
||||
ObjectLiteralPropertyT ParseObjectPropertyDefinition(
|
||||
ObjectLiteralChecker* checker, bool* is_computed_name,
|
||||
bool* is_rest_property, bool* ok);
|
||||
void ParseArguments(ScopedExpressionListT* args, bool* has_spread,
|
||||
bool maybe_arrow, bool* ok);
|
||||
void ParseArguments(ScopedExpressionListT* args, bool* has_spread, bool* ok) {
|
||||
void ParseArguments(ExpressionListT* args, bool* has_spread, bool maybe_arrow,
|
||||
bool* ok);
|
||||
void ParseArguments(ExpressionListT* args, bool* has_spread, bool* ok) {
|
||||
ParseArguments(args, has_spread, false, ok);
|
||||
}
|
||||
|
||||
@ -1469,7 +1466,7 @@ class ParserBase {
|
||||
ExpressionClassifier::AsyncArrowFormalParametersProduction);
|
||||
}
|
||||
|
||||
ExpressionListT expression_buffer() { return expression_buffer_; }
|
||||
std::vector<void*>* pointer_buffer() { return &pointer_buffer_; }
|
||||
|
||||
// Parser base's protected field members.
|
||||
|
||||
@ -1493,10 +1490,7 @@ class ParserBase {
|
||||
Zone* zone_;
|
||||
ExpressionClassifier* classifier_;
|
||||
|
||||
// TODO(verwaest): Merge and/or buffer page-wise.
|
||||
StatementListT statement_buffer_;
|
||||
ExpressionListT expression_buffer_;
|
||||
ObjectPropertyListT property_buffer_;
|
||||
std::vector<void*> pointer_buffer_;
|
||||
|
||||
Scanner* scanner_;
|
||||
|
||||
@ -1912,7 +1906,7 @@ ParserBase<Impl>::ParseExpressionCoverGrammar(bool accept_IN, bool* ok) {
|
||||
// AssignmentExpression
|
||||
// Expression ',' AssignmentExpression
|
||||
|
||||
ScopedExpressionListT list(zone_, expression_buffer_);
|
||||
ExpressionListT list(pointer_buffer());
|
||||
ExpressionT right;
|
||||
while (true) {
|
||||
ExpressionClassifier binding_classifier(this);
|
||||
@ -1976,7 +1970,7 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseArrayLiteral(
|
||||
// '[' Expression? (',' Expression?)* ']'
|
||||
|
||||
int pos = peek_position();
|
||||
ScopedExpressionListT values(zone_, expression_buffer_);
|
||||
ExpressionListT values(pointer_buffer());
|
||||
int first_spread_index = -1;
|
||||
Consume(Token::LBRACK);
|
||||
while (!Check(Token::RBRACK)) {
|
||||
@ -2601,7 +2595,7 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseObjectLiteral(
|
||||
// '{' (PropertyDefinition (',' PropertyDefinition)* ','? )? '}'
|
||||
|
||||
int pos = peek_position();
|
||||
ScopedObjectPropertyListT properties(zone_, property_buffer_);
|
||||
ObjectPropertyListT properties(pointer_buffer());
|
||||
int number_of_boilerplate_properties = 0;
|
||||
|
||||
bool has_computed_names = false;
|
||||
@ -2658,7 +2652,7 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseObjectLiteral(
|
||||
|
||||
template <typename Impl>
|
||||
void ParserBase<Impl>::ParseArguments(
|
||||
typename ParserBase<Impl>::ScopedExpressionListT* args, bool* has_spread,
|
||||
typename ParserBase<Impl>::ExpressionListT* args, bool* has_spread,
|
||||
bool maybe_arrow, bool* ok) {
|
||||
// Arguments ::
|
||||
// '(' (AssignmentExpression)*[','] ')'
|
||||
@ -3235,7 +3229,7 @@ ParserBase<Impl>::ParseLeftHandSideContinuation(ExpressionT result, bool* ok) {
|
||||
}
|
||||
}
|
||||
bool has_spread;
|
||||
ScopedExpressionListT args(zone_, expression_buffer_);
|
||||
ExpressionListT args(pointer_buffer());
|
||||
if (impl()->IsIdentifier(result) &&
|
||||
impl()->IsAsync(impl()->AsIdentifier(result)) &&
|
||||
!scanner()->HasLineTerminatorBeforeNext()) {
|
||||
@ -3356,7 +3350,7 @@ ParserBase<Impl>::ParseMemberWithPresentNewPrefixesExpression(bool* ok) {
|
||||
if (peek() == Token::LPAREN) {
|
||||
// NewExpression with arguments.
|
||||
{
|
||||
ScopedExpressionListT args(zone_, expression_buffer_);
|
||||
ExpressionListT args(pointer_buffer());
|
||||
bool has_spread;
|
||||
ParseArguments(&args, &has_spread, CHECK_OK);
|
||||
|
||||
@ -3370,7 +3364,7 @@ ParserBase<Impl>::ParseMemberWithPresentNewPrefixesExpression(bool* ok) {
|
||||
return ParseMemberExpressionContinuation(result, ok);
|
||||
}
|
||||
// NewExpression without arguments.
|
||||
ScopedExpressionListT args(zone_, expression_buffer_);
|
||||
ExpressionListT args(pointer_buffer());
|
||||
return factory()->NewCallNew(result, args, new_pos);
|
||||
}
|
||||
|
||||
@ -4630,7 +4624,7 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseV8Intrinsic(
|
||||
return impl()->NullExpression();
|
||||
}
|
||||
bool has_spread;
|
||||
ScopedExpressionListT args(zone_, expression_buffer_);
|
||||
ExpressionListT args(pointer_buffer());
|
||||
ParseArguments(&args, &has_spread, CHECK_OK);
|
||||
|
||||
if (has_spread) {
|
||||
@ -5364,7 +5358,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseSwitchStatement(
|
||||
while (peek() != Token::RBRACE) {
|
||||
// An empty label indicates the default case.
|
||||
ExpressionT label = impl()->NullExpression();
|
||||
ScopedStatementListT statements(zone_, statement_buffer_);
|
||||
ScopedStatementListT statements(pointer_buffer());
|
||||
SourceRange clause_range;
|
||||
{
|
||||
SourceRangeScope range_scope(scanner(), &clause_range);
|
||||
|
@ -56,7 +56,7 @@ FunctionLiteral* Parser::DefaultConstructor(const AstRawString* name,
|
||||
constructor_args_name, VariableMode::kTemporary, is_optional, is_rest,
|
||||
ast_value_factory(), pos);
|
||||
|
||||
ScopedPtrList<Expression> args(zone(), expression_buffer());
|
||||
ScopedPtrList<Expression> args(pointer_buffer());
|
||||
Spread* spread_args = factory()->NewSpread(
|
||||
factory()->NewVariableProxy(constructor_args), pos, pos);
|
||||
|
||||
@ -289,7 +289,7 @@ Expression* Parser::BuildUnaryExpression(Expression* expression,
|
||||
Expression* Parser::NewThrowError(Runtime::FunctionId id,
|
||||
MessageTemplate message,
|
||||
const AstRawString* arg, int pos) {
|
||||
ScopedPtrList<Expression> args(zone(), expression_buffer());
|
||||
ScopedPtrList<Expression> args(pointer_buffer());
|
||||
args.Add(factory()->NewSmiLiteral(static_cast<int>(message), pos));
|
||||
args.Add(factory()->NewStringLiteral(arg, pos));
|
||||
CallRuntime* call_constructor = factory()->NewCallRuntime(id, args, pos);
|
||||
@ -325,7 +325,7 @@ Expression* Parser::NewTargetExpression(int pos) {
|
||||
}
|
||||
|
||||
Expression* Parser::ImportMetaExpression(int pos) {
|
||||
ScopedPtrList<Expression> args(zone(), expression_buffer());
|
||||
ScopedPtrList<Expression> args(pointer_buffer());
|
||||
return factory()->NewCallRuntime(Runtime::kInlineGetImportMetaObject, args,
|
||||
pos);
|
||||
}
|
||||
@ -1790,7 +1790,7 @@ void Parser::ParseAndRewriteAsyncGeneratorFunctionBody(
|
||||
// For AsyncGenerators, a top-level catch block will reject the Promise.
|
||||
Scope* catch_scope = NewHiddenCatchScope();
|
||||
|
||||
ScopedPtrList<Expression> reject_args(zone(), expression_buffer());
|
||||
ScopedPtrList<Expression> reject_args(pointer_buffer());
|
||||
reject_args.Add(factory()->NewVariableProxy(
|
||||
function_state_->scope()->generator_object_var()));
|
||||
reject_args.Add(factory()->NewVariableProxy(catch_scope->catch_variable()));
|
||||
@ -1807,7 +1807,7 @@ void Parser::ParseAndRewriteAsyncGeneratorFunctionBody(
|
||||
try_block->statements()->Add(try_catch, zone());
|
||||
|
||||
Block* finally_block = factory()->NewBlock(1, false);
|
||||
ScopedPtrList<Expression> close_args(zone(), expression_buffer());
|
||||
ScopedPtrList<Expression> close_args(pointer_buffer());
|
||||
VariableProxy* call_proxy = factory()->NewVariableProxy(
|
||||
function_state_->scope()->generator_object_var());
|
||||
close_args.Add(call_proxy);
|
||||
@ -1843,7 +1843,7 @@ Expression* Parser::BuildIteratorNextResult(VariableProxy* iterator,
|
||||
Variable* result, IteratorType type,
|
||||
int pos) {
|
||||
Expression* next_property = factory()->NewResolvedProperty(iterator, next);
|
||||
ScopedPtrList<Expression> next_arguments(zone(), expression_buffer());
|
||||
ScopedPtrList<Expression> next_arguments(pointer_buffer());
|
||||
Expression* next_call =
|
||||
factory()->NewCall(next_property, next_arguments, kNoSourcePosition);
|
||||
if (type == IteratorType::kAsync) {
|
||||
@ -1855,14 +1855,14 @@ Expression* Parser::BuildIteratorNextResult(VariableProxy* iterator,
|
||||
factory()->NewAssignment(Token::ASSIGN, result_proxy, next_call, pos);
|
||||
|
||||
// %_IsJSReceiver(...)
|
||||
ScopedPtrList<Expression> is_spec_object_args(zone(), expression_buffer());
|
||||
ScopedPtrList<Expression> is_spec_object_args(pointer_buffer());
|
||||
is_spec_object_args.Add(left);
|
||||
Expression* is_spec_object_call = factory()->NewCallRuntime(
|
||||
Runtime::kInlineIsJSReceiver, is_spec_object_args, pos);
|
||||
|
||||
// %ThrowIteratorResultNotAnObject(result)
|
||||
Expression* result_proxy_again = factory()->NewVariableProxy(result);
|
||||
ScopedPtrList<Expression> throw_arguments(zone(), expression_buffer());
|
||||
ScopedPtrList<Expression> throw_arguments(pointer_buffer());
|
||||
throw_arguments.Add(result_proxy_again);
|
||||
Expression* throw_call = factory()->NewCallRuntime(
|
||||
Runtime::kThrowIteratorResultNotAnObject, throw_arguments, pos);
|
||||
@ -2970,7 +2970,7 @@ Block* Parser::BuildRejectPromiseOnException(Block* inner_block) {
|
||||
|
||||
Expression* reject_promise;
|
||||
{
|
||||
ScopedPtrList<Expression> args(zone(), expression_buffer());
|
||||
ScopedPtrList<Expression> args(pointer_buffer());
|
||||
args.Add(factory()->NewVariableProxy(
|
||||
function_state_->scope()->generator_object_var()));
|
||||
args.Add(factory()->NewVariableProxy(catch_scope->catch_variable()));
|
||||
@ -3459,7 +3459,7 @@ Expression* Parser::CloseTemplateLiteral(TemplateLiteralState* state, int start,
|
||||
factory()->NewGetTemplateObject(cooked_strings, raw_strings, pos);
|
||||
|
||||
// Call TagFn
|
||||
ScopedPtrList<Expression> call_args(zone(), expression_buffer());
|
||||
ScopedPtrList<Expression> call_args(pointer_buffer());
|
||||
call_args.Add(template_object);
|
||||
call_args.AddAll(*expressions);
|
||||
return factory()->NewTaggedTemplate(tag, call_args, pos);
|
||||
@ -3503,7 +3503,7 @@ Expression* Parser::SpreadCall(Expression* function,
|
||||
return factory()->NewCall(function, args_list, pos);
|
||||
}
|
||||
|
||||
ScopedPtrList<Expression> args(zone(), expression_buffer());
|
||||
ScopedPtrList<Expression> args(pointer_buffer());
|
||||
if (function->IsProperty()) {
|
||||
// Method calls
|
||||
if (function->AsProperty()->IsSuperAccess()) {
|
||||
@ -3537,7 +3537,7 @@ Expression* Parser::SpreadCallNew(Expression* function,
|
||||
// Handle in BytecodeGenerator.
|
||||
return factory()->NewCallNew(function, args_list, pos);
|
||||
}
|
||||
ScopedPtrList<Expression> args(zone(), expression_buffer());
|
||||
ScopedPtrList<Expression> args(pointer_buffer());
|
||||
args.Add(function);
|
||||
args.Add(ArrayLiteralFromListWithSpread(args_list));
|
||||
|
||||
@ -3768,7 +3768,7 @@ void Parser::BuildIteratorClose(ZonePtrList<Statement>* statements,
|
||||
// output = %_Call(iteratorReturn, iterator, input);
|
||||
Statement* call_return;
|
||||
{
|
||||
ScopedPtrList<Expression> args(zone(), expression_buffer());
|
||||
ScopedPtrList<Expression> args(pointer_buffer());
|
||||
args.Add(factory()->NewVariableProxy(var_return));
|
||||
args.Add(factory()->NewVariableProxy(iterator));
|
||||
args.Add(factory()->NewVariableProxy(input));
|
||||
@ -3790,7 +3790,7 @@ void Parser::BuildIteratorClose(ZonePtrList<Statement>* statements,
|
||||
{
|
||||
Expression* is_receiver_call;
|
||||
{
|
||||
ScopedPtrList<Expression> args(zone(), expression_buffer());
|
||||
ScopedPtrList<Expression> args(pointer_buffer());
|
||||
args.Add(factory()->NewVariableProxy(var_output));
|
||||
is_receiver_call =
|
||||
factory()->NewCallRuntime(Runtime::kInlineIsJSReceiver, args, nopos);
|
||||
@ -3798,7 +3798,7 @@ void Parser::BuildIteratorClose(ZonePtrList<Statement>* statements,
|
||||
|
||||
Statement* throw_call;
|
||||
{
|
||||
ScopedPtrList<Expression> args(zone(), expression_buffer());
|
||||
ScopedPtrList<Expression> args(pointer_buffer());
|
||||
args.Add(factory()->NewVariableProxy(var_output));
|
||||
Expression* call = factory()->NewCallRuntime(
|
||||
Runtime::kThrowIteratorResultNotAnObject, args, nopos);
|
||||
@ -3896,7 +3896,7 @@ void Parser::FinalizeIteratorUse(Variable* completion, Expression* condition,
|
||||
// TryCatchStatementForReThrow below (which does not clear the pending
|
||||
// message), rather than a TryCatchStatement.
|
||||
{
|
||||
ScopedPtrList<Expression> args(zone(), expression_buffer());
|
||||
ScopedPtrList<Expression> args(pointer_buffer());
|
||||
args.Add(factory()->NewVariableProxy(catch_scope->catch_variable()));
|
||||
rethrow = factory()->NewExpressionStatement(
|
||||
factory()->NewCallRuntime(Runtime::kReThrow, args, nopos), nopos);
|
||||
@ -3986,7 +3986,7 @@ void Parser::BuildIteratorCloseForCompletion(ZonePtrList<Statement>* statements,
|
||||
// try { %_Call(iteratorReturn, iterator) } catch (_) { }
|
||||
Statement* try_call_return;
|
||||
{
|
||||
ScopedPtrList<Expression> args(zone(), expression_buffer());
|
||||
ScopedPtrList<Expression> args(pointer_buffer());
|
||||
args.Add(factory()->NewVariableProxy(var_return));
|
||||
args.Add(factory()->NewVariableProxy(iterator));
|
||||
|
||||
@ -4016,7 +4016,7 @@ void Parser::BuildIteratorCloseForCompletion(ZonePtrList<Statement>* statements,
|
||||
Variable* var_output = NewTemporary(ast_value_factory()->empty_string());
|
||||
Statement* call_return;
|
||||
{
|
||||
ScopedPtrList<Expression> args(zone(), expression_buffer());
|
||||
ScopedPtrList<Expression> args(pointer_buffer());
|
||||
args.Add(factory()->NewVariableProxy(var_return));
|
||||
args.Add(factory()->NewVariableProxy(iterator));
|
||||
Expression* call =
|
||||
@ -4034,7 +4034,7 @@ void Parser::BuildIteratorCloseForCompletion(ZonePtrList<Statement>* statements,
|
||||
|
||||
Expression* is_receiver_call;
|
||||
{
|
||||
ScopedPtrList<Expression> args(zone(), expression_buffer());
|
||||
ScopedPtrList<Expression> args(pointer_buffer());
|
||||
args.Add(factory()->NewVariableProxy(var_output));
|
||||
is_receiver_call =
|
||||
factory()->NewCallRuntime(Runtime::kInlineIsJSReceiver, args, nopos);
|
||||
@ -4042,7 +4042,7 @@ void Parser::BuildIteratorCloseForCompletion(ZonePtrList<Statement>* statements,
|
||||
|
||||
Statement* throw_call;
|
||||
{
|
||||
ScopedPtrList<Expression> args(zone(), expression_buffer());
|
||||
ScopedPtrList<Expression> args(pointer_buffer());
|
||||
args.Add(factory()->NewVariableProxy(var_output));
|
||||
Expression* call = factory()->NewCallRuntime(
|
||||
Runtime::kThrowIteratorResultNotAnObject, args, nopos);
|
||||
|
@ -131,16 +131,13 @@ struct ParserTypes<Parser> {
|
||||
typedef ClassLiteral::Property* ClassLiteralProperty;
|
||||
typedef v8::internal::Suspend* Suspend;
|
||||
typedef v8::internal::RewritableExpression* RewritableExpression;
|
||||
typedef ZonePtrList<v8::internal::Expression>* ExpressionList;
|
||||
typedef ZonePtrList<ObjectLiteral::Property>* ObjectPropertyList;
|
||||
typedef ZonePtrList<ClassLiteral::Property>* ClassPropertyList;
|
||||
typedef ParserFormalParameters FormalParameters;
|
||||
typedef v8::internal::Statement* Statement;
|
||||
typedef ZonePtrList<v8::internal::Statement>* StatementList;
|
||||
typedef ScopedPtrList<v8::internal::Statement> ScopedStatementList;
|
||||
typedef ScopedPtrList<v8::internal::Expression> ScopedExpressionList;
|
||||
typedef ScopedPtrList<v8::internal::ObjectLiteralProperty>
|
||||
ScopedObjectPropertyList;
|
||||
typedef ScopedPtrList<v8::internal::Expression> ExpressionList;
|
||||
typedef ScopedPtrList<v8::internal::ObjectLiteralProperty> ObjectPropertyList;
|
||||
typedef v8::internal::Block* Block;
|
||||
typedef v8::internal::BreakableStatement* BreakableStatement;
|
||||
typedef v8::internal::ForStatement* ForStatement;
|
||||
@ -742,7 +739,7 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
|
||||
// A shortcut for performing a ToString operation
|
||||
V8_INLINE Expression* ToString(Expression* expr) {
|
||||
if (expr->IsStringLiteral()) return expr;
|
||||
ScopedPtrList<Expression> args(zone(), expression_buffer());
|
||||
ScopedPtrList<Expression> args(pointer_buffer());
|
||||
args.Add(expr);
|
||||
return factory()->NewCallRuntime(Runtime::kInlineToString, args,
|
||||
expr->position());
|
||||
|
@ -111,9 +111,7 @@ class PatternRewriter final : public AstVisitor<PatternRewriter> {
|
||||
return parser_->ast_value_factory();
|
||||
}
|
||||
|
||||
ZonePtrList<Expression>* expression_buffer() {
|
||||
return parser_->expression_buffer();
|
||||
}
|
||||
std::vector<void*>* pointer_buffer() { return parser_->pointer_buffer(); }
|
||||
|
||||
Zone* zone() const { return parser_->zone(); }
|
||||
Scope* scope() const { return scope_; }
|
||||
@ -341,7 +339,7 @@ void PatternRewriter::VisitObjectLiteral(ObjectLiteral* pattern,
|
||||
Variable** temp_var) {
|
||||
auto temp = *temp_var = CreateTempVar(current_value_);
|
||||
|
||||
ScopedPtrList<Expression> rest_runtime_callargs(zone(), expression_buffer());
|
||||
ScopedPtrList<Expression> rest_runtime_callargs(pointer_buffer());
|
||||
if (pattern->has_rest_property()) {
|
||||
rest_runtime_callargs.Add(factory()->NewVariableProxy(temp));
|
||||
}
|
||||
@ -377,7 +375,7 @@ void PatternRewriter::VisitObjectLiteral(ObjectLiteral* pattern,
|
||||
|
||||
if (property->is_computed_name()) {
|
||||
DCHECK(!key->IsPropertyName() || !key->IsNumberLiteral());
|
||||
ScopedPtrList<Expression> args(zone(), expression_buffer());
|
||||
ScopedPtrList<Expression> args(pointer_buffer());
|
||||
args.Add(key);
|
||||
auto to_name_key = CreateTempVar(factory()->NewCallRuntime(
|
||||
Runtime::kToName, args, kNoSourcePosition));
|
||||
@ -555,7 +553,7 @@ void PatternRewriter::VisitArrayLiteral(ArrayLiteral* node,
|
||||
// let array = [];
|
||||
Variable* array;
|
||||
{
|
||||
ScopedPtrList<Expression> empty_exprs(zone(), expression_buffer());
|
||||
ScopedPtrList<Expression> empty_exprs(pointer_buffer());
|
||||
array = CreateTempVar(
|
||||
factory()->NewArrayLiteral(empty_exprs, kNoSourcePosition));
|
||||
}
|
||||
|
@ -402,17 +402,10 @@ class PreParserExpression {
|
||||
|
||||
friend class PreParser;
|
||||
friend class PreParserFactory;
|
||||
friend class PreParserScopedExpressionList;
|
||||
friend class PreParserExpressionList;
|
||||
};
|
||||
|
||||
|
||||
// The pre-parser doesn't need to build lists of expressions, identifiers, or
|
||||
// the like. If the PreParser is used in variable tracking mode, it needs to
|
||||
// build lists of variables though.
|
||||
class PreParserExpressionList {};
|
||||
|
||||
class PreParserStatement;
|
||||
|
||||
class PreParserStatementList {
|
||||
public:
|
||||
PreParserStatementList() : PreParserStatementList(false) {}
|
||||
@ -428,18 +421,19 @@ class PreParserStatementList {
|
||||
|
||||
class PreParserScopedStatementList {
|
||||
public:
|
||||
PreParserScopedStatementList(Zone* zone,
|
||||
const PreParserStatementList& buffer) {}
|
||||
explicit PreParserScopedStatementList(std::vector<void*>* buffer) {}
|
||||
void Add(const PreParserStatement& element) {}
|
||||
};
|
||||
|
||||
class PreParserScopedExpressionList {
|
||||
// The pre-parser doesn't need to build lists of expressions, identifiers, or
|
||||
// the like. If the PreParser is used in variable tracking mode, it needs to
|
||||
// build lists of variables though.
|
||||
class PreParserExpressionList {
|
||||
using VariableZoneThreadedListType =
|
||||
ZoneThreadedList<VariableProxy, VariableProxy::PreParserNext>;
|
||||
|
||||
public:
|
||||
PreParserScopedExpressionList(Zone* zone,
|
||||
const PreParserExpressionList& buffer)
|
||||
explicit PreParserExpressionList(std::vector<void*>* buffer)
|
||||
: length_(0), variables_(nullptr) {}
|
||||
|
||||
int length() const { return length_; }
|
||||
@ -583,9 +577,8 @@ class PreParserFactory {
|
||||
int js_flags, int pos) {
|
||||
return PreParserExpression::Default();
|
||||
}
|
||||
PreParserExpression NewArrayLiteral(
|
||||
const PreParserScopedExpressionList& values, int first_spread_index,
|
||||
int pos) {
|
||||
PreParserExpression NewArrayLiteral(const PreParserExpressionList& values,
|
||||
int first_spread_index, int pos) {
|
||||
return PreParserExpression::ArrayLiteral(values.variables_);
|
||||
}
|
||||
PreParserExpression NewClassLiteralProperty(const PreParserExpression& key,
|
||||
@ -608,8 +601,8 @@ class PreParserFactory {
|
||||
return PreParserExpression::Default(value.variables_);
|
||||
}
|
||||
PreParserExpression NewObjectLiteral(
|
||||
const PreParserScopedExpressionList& properties,
|
||||
int boilerplate_properties, int pos, bool has_rest_property) {
|
||||
const PreParserExpressionList& properties, int boilerplate_properties,
|
||||
int pos, bool has_rest_property) {
|
||||
return PreParserExpression::ObjectLiteral(properties.variables_);
|
||||
}
|
||||
PreParserExpression NewVariableProxy(void* variable) {
|
||||
@ -681,9 +674,8 @@ class PreParserFactory {
|
||||
return PreParserExpression::Default();
|
||||
}
|
||||
PreParserExpression NewCall(
|
||||
PreParserExpression expression,
|
||||
const PreParserScopedExpressionList& arguments, int pos,
|
||||
Call::PossiblyEval possibly_eval = Call::NOT_EVAL) {
|
||||
PreParserExpression expression, const PreParserExpressionList& arguments,
|
||||
int pos, Call::PossiblyEval possibly_eval = Call::NOT_EVAL) {
|
||||
if (possibly_eval == Call::IS_POSSIBLY_EVAL) {
|
||||
DCHECK(expression.IsIdentifier() && expression.AsIdentifier().IsEval());
|
||||
return PreParserExpression::CallEval();
|
||||
@ -691,12 +683,12 @@ class PreParserFactory {
|
||||
return PreParserExpression::Call();
|
||||
}
|
||||
PreParserExpression NewTaggedTemplate(
|
||||
PreParserExpression expression,
|
||||
const PreParserScopedExpressionList& arguments, int pos) {
|
||||
PreParserExpression expression, const PreParserExpressionList& arguments,
|
||||
int pos) {
|
||||
return PreParserExpression::CallTaggedTemplate();
|
||||
}
|
||||
PreParserExpression NewCallNew(const PreParserExpression& expression,
|
||||
const PreParserScopedExpressionList& arguments,
|
||||
const PreParserExpressionList& arguments,
|
||||
int pos) {
|
||||
return PreParserExpression::Default();
|
||||
}
|
||||
@ -900,6 +892,8 @@ class PreParserSourceRangeScope {
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(PreParserSourceRangeScope);
|
||||
};
|
||||
|
||||
class PreParserPropertyList {};
|
||||
|
||||
template <>
|
||||
struct ParserTypes<PreParser> {
|
||||
typedef ParserBase<PreParser> Base;
|
||||
@ -913,15 +907,13 @@ struct ParserTypes<PreParser> {
|
||||
typedef PreParserExpression ClassLiteralProperty;
|
||||
typedef PreParserExpression Suspend;
|
||||
typedef PreParserExpression RewritableExpression;
|
||||
typedef PreParserExpressionList ExpressionList;
|
||||
typedef PreParserExpressionList ObjectPropertyList;
|
||||
typedef PreParserExpressionList ClassPropertyList;
|
||||
typedef PreParserPropertyList ClassPropertyList;
|
||||
typedef PreParserFormalParameters FormalParameters;
|
||||
typedef PreParserStatement Statement;
|
||||
typedef PreParserStatementList StatementList;
|
||||
typedef PreParserScopedStatementList ScopedStatementList;
|
||||
typedef PreParserScopedExpressionList ScopedExpressionList;
|
||||
typedef PreParserScopedExpressionList ScopedObjectPropertyList;
|
||||
typedef PreParserExpressionList ExpressionList;
|
||||
typedef PreParserExpressionList ObjectPropertyList;
|
||||
typedef PreParserStatement Block;
|
||||
typedef PreParserStatement BreakableStatement;
|
||||
typedef PreParserStatement IterationStatement;
|
||||
@ -1082,13 +1074,13 @@ class PreParser : public ParserBase<PreParser> {
|
||||
}
|
||||
V8_INLINE void SetAsmModule() {}
|
||||
|
||||
V8_INLINE PreParserExpression
|
||||
SpreadCall(const PreParserExpression& function,
|
||||
const PreParserScopedExpressionList& args, int pos,
|
||||
Call::PossiblyEval possibly_eval);
|
||||
V8_INLINE PreParserExpression SpreadCall(const PreParserExpression& function,
|
||||
const PreParserExpressionList& args,
|
||||
int pos,
|
||||
Call::PossiblyEval possibly_eval);
|
||||
V8_INLINE PreParserExpression
|
||||
SpreadCallNew(const PreParserExpression& function,
|
||||
const PreParserScopedExpressionList& args, int pos);
|
||||
const PreParserExpressionList& args, int pos);
|
||||
|
||||
V8_INLINE void RewriteDestructuringAssignments() {}
|
||||
|
||||
@ -1631,25 +1623,17 @@ class PreParser : public ParserBase<PreParser> {
|
||||
const PreParserIdentifier& name, int start_position,
|
||||
InferName infer = InferName::kYes);
|
||||
|
||||
V8_INLINE PreParserExpressionList NewExpressionList(int size) const {
|
||||
return PreParserExpressionList();
|
||||
}
|
||||
|
||||
V8_INLINE PreParserExpressionList NewObjectPropertyList(int size) const {
|
||||
return PreParserExpressionList();
|
||||
}
|
||||
|
||||
V8_INLINE PreParserExpressionList NewClassPropertyList(int size) const {
|
||||
return PreParserExpressionList();
|
||||
V8_INLINE PreParserPropertyList NewClassPropertyList(int size) const {
|
||||
return PreParserPropertyList();
|
||||
}
|
||||
|
||||
V8_INLINE PreParserStatementList NewStatementList(int size) const {
|
||||
return PreParserStatementList();
|
||||
}
|
||||
|
||||
V8_INLINE PreParserExpression NewV8Intrinsic(
|
||||
const PreParserIdentifier& name,
|
||||
const PreParserScopedExpressionList& arguments, int pos, bool* ok) {
|
||||
V8_INLINE PreParserExpression
|
||||
NewV8Intrinsic(const PreParserIdentifier& name,
|
||||
const PreParserExpressionList& arguments, int pos, bool* ok) {
|
||||
return PreParserExpression::Default();
|
||||
}
|
||||
|
||||
@ -1710,7 +1694,7 @@ class PreParser : public ParserBase<PreParser> {
|
||||
}
|
||||
|
||||
V8_INLINE PreParserExpression
|
||||
ExpressionListToExpression(const PreParserScopedExpressionList& args) {
|
||||
ExpressionListToExpression(const PreParserExpressionList& args) {
|
||||
return PreParserExpression::Default(args.variables_);
|
||||
}
|
||||
|
||||
@ -1748,16 +1732,16 @@ class PreParser : public ParserBase<PreParser> {
|
||||
PreParsedScopeDataBuilder* preparsed_scope_data_builder_;
|
||||
};
|
||||
|
||||
PreParserExpression PreParser::SpreadCall(
|
||||
const PreParserExpression& function,
|
||||
const PreParserScopedExpressionList& args, int pos,
|
||||
Call::PossiblyEval possibly_eval) {
|
||||
PreParserExpression PreParser::SpreadCall(const PreParserExpression& function,
|
||||
const PreParserExpressionList& args,
|
||||
int pos,
|
||||
Call::PossiblyEval possibly_eval) {
|
||||
return factory()->NewCall(function, args, pos, possibly_eval);
|
||||
}
|
||||
|
||||
PreParserExpression PreParser::SpreadCallNew(
|
||||
const PreParserExpression& function,
|
||||
const PreParserScopedExpressionList& args, int pos) {
|
||||
const PreParserExpression& function, const PreParserExpressionList& args,
|
||||
int pos) {
|
||||
return factory()->NewCallNew(function, args, pos);
|
||||
}
|
||||
|
||||
|
@ -324,42 +324,49 @@ using ZonePtrList = ZoneList<T*>;
|
||||
template <typename T>
|
||||
class ScopedPtrList final {
|
||||
public:
|
||||
ScopedPtrList(Zone* zone, ZonePtrList<T>* buffer)
|
||||
: zone_(zone),
|
||||
buffer_(buffer),
|
||||
start_(buffer->length()),
|
||||
end_(buffer->length()) {}
|
||||
explicit ScopedPtrList(std::vector<void*>* buffer)
|
||||
: buffer_(*buffer), start_(buffer->size()), end_(buffer->size()) {}
|
||||
|
||||
~ScopedPtrList() {
|
||||
DCHECK_EQ(buffer_->length(), end_);
|
||||
buffer_->Rewind(start_);
|
||||
DCHECK_EQ(buffer_.size(), end_);
|
||||
buffer_.resize(start_);
|
||||
}
|
||||
|
||||
int length() const { return end_ - start_; }
|
||||
T* at(int i) const { return buffer_->at(i + start_); }
|
||||
int length() const { return static_cast<int>(end_ - start_); }
|
||||
T* at(int i) const {
|
||||
size_t index = start_ + i;
|
||||
DCHECK_LT(index, buffer_.size());
|
||||
return reinterpret_cast<T*>(buffer_[index]);
|
||||
}
|
||||
|
||||
void CopyTo(ZonePtrList<T>* target) const {
|
||||
target->Initialize(length(), zone_);
|
||||
target->AddAll(buffer_->ToVector(start_, length()), zone_);
|
||||
void CopyTo(ZonePtrList<T>* target, Zone* zone) const {
|
||||
DCHECK_LE(end_, buffer_.size());
|
||||
// Make sure we don't reference absent elements below.
|
||||
if (length() == 0) return;
|
||||
target->Initialize(length(), zone);
|
||||
T** data = reinterpret_cast<T**>(&buffer_[start_]);
|
||||
target->AddAll(Vector<T*>(data, length()), zone);
|
||||
}
|
||||
|
||||
void Add(T* value) {
|
||||
DCHECK_EQ(buffer_->length(), end_);
|
||||
buffer_->Add(value, zone_);
|
||||
DCHECK_EQ(buffer_.size(), end_);
|
||||
buffer_.push_back(value);
|
||||
++end_;
|
||||
}
|
||||
|
||||
void AddAll(const ZonePtrList<T>& list) {
|
||||
DCHECK_EQ(buffer_->length(), end_);
|
||||
buffer_->AddAll(list, zone_);
|
||||
DCHECK_EQ(buffer_.size(), end_);
|
||||
buffer_.reserve(buffer_.size() + list.length());
|
||||
for (int i = 0; i < list.length(); i++) {
|
||||
buffer_.push_back(list.at(i));
|
||||
}
|
||||
end_ += list.length();
|
||||
}
|
||||
|
||||
private:
|
||||
Zone* zone_;
|
||||
ZonePtrList<T>* buffer_;
|
||||
int start_;
|
||||
int end_;
|
||||
std::vector<void*>& buffer_;
|
||||
size_t start_;
|
||||
size_t end_;
|
||||
};
|
||||
|
||||
// ZoneThreadedList is a special variant of the ThreadedList that can be put
|
||||
|
Loading…
Reference in New Issue
Block a user