summaryrefslogtreecommitdiff
path: root/deps/v8/src/ast
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/ast')
-rw-r--r--deps/v8/src/ast/ast-traversal-visitor.h6
-rw-r--r--deps/v8/src/ast/ast.cc12
-rw-r--r--deps/v8/src/ast/ast.h291
-rw-r--r--deps/v8/src/ast/modules.cc7
-rw-r--r--deps/v8/src/ast/prettyprinter.cc80
-rw-r--r--deps/v8/src/ast/prettyprinter.h8
-rw-r--r--deps/v8/src/ast/scopes.cc165
-rw-r--r--deps/v8/src/ast/scopes.h95
-rw-r--r--deps/v8/src/ast/source-range-ast-visitor.cc8
-rw-r--r--deps/v8/src/ast/source-range-ast-visitor.h1
-rw-r--r--deps/v8/src/ast/variables.h58
11 files changed, 441 insertions, 290 deletions
diff --git a/deps/v8/src/ast/ast-traversal-visitor.h b/deps/v8/src/ast/ast-traversal-visitor.h
index b4836ff784..2796e59a8d 100644
--- a/deps/v8/src/ast/ast-traversal-visitor.h
+++ b/deps/v8/src/ast/ast-traversal-visitor.h
@@ -383,6 +383,12 @@ void AstTraversalVisitor<Subclass>::VisitThrow(Throw* expr) {
}
template <class Subclass>
+void AstTraversalVisitor<Subclass>::VisitOptionalChain(OptionalChain* expr) {
+ PROCESS_EXPRESSION(expr);
+ RECURSE_EXPRESSION(Visit(expr->expression()));
+}
+
+template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitProperty(Property* expr) {
PROCESS_EXPRESSION(expr);
RECURSE_EXPRESSION(Visit(expr->obj()));
diff --git a/deps/v8/src/ast/ast.cc b/deps/v8/src/ast/ast.cc
index 9987eb2844..4b6c4805de 100644
--- a/deps/v8/src/ast/ast.cc
+++ b/deps/v8/src/ast/ast.cc
@@ -122,6 +122,10 @@ bool Expression::IsUndefinedLiteral() const {
var_proxy->raw_name()->IsOneByteEqualTo("undefined");
}
+bool Expression::IsLiteralButNotNullOrUndefined() const {
+ return IsLiteral() && !IsNullOrUndefinedLiteral();
+}
+
bool Expression::ToBooleanIsTrue() const {
return IsLiteral() && AsLiteral()->ToBooleanIsTrue();
}
@@ -217,13 +221,7 @@ bool FunctionLiteral::AllowsLazyCompilation() {
}
bool FunctionLiteral::SafeToSkipArgumentsAdaptor() const {
- // TODO(bmeurer,verwaest): The --fast_calls_with_arguments_mismatches
- // is mostly here for checking the real-world impact of the calling
- // convention. There's not really a point in turning off this flag
- // otherwise, so we should remove it at some point, when we're done
- // with the experiments (https://crbug.com/v8/8895).
- return FLAG_fast_calls_with_arguments_mismatches &&
- language_mode() == LanguageMode::kStrict &&
+ return language_mode() == LanguageMode::kStrict &&
scope()->arguments() == nullptr &&
scope()->rest_parameter() == nullptr;
}
diff --git a/deps/v8/src/ast/ast.h b/deps/v8/src/ast/ast.h
index bd52d1b2c0..ced9f775dd 100644
--- a/deps/v8/src/ast/ast.h
+++ b/deps/v8/src/ast/ast.h
@@ -16,6 +16,7 @@
#include "src/common/globals.h"
#include "src/execution/isolate.h"
#include "src/heap/factory.h"
+#include "src/objects/function-syntax-kind.h"
#include "src/objects/literal-objects.h"
#include "src/objects/smi.h"
#include "src/parsing/token.h"
@@ -94,6 +95,7 @@ namespace internal {
V(ImportCallExpression) \
V(Literal) \
V(NativeFunctionLiteral) \
+ V(OptionalChain) \
V(Property) \
V(ResolvedProperty) \
V(Spread) \
@@ -168,11 +170,13 @@ class AstNode: public ZoneObject {
void* operator new(size_t size);
int position_;
- class NodeTypeField : public BitField<NodeType, 0, 6> {};
+ using NodeTypeField = BitField<NodeType, 0, 6>;
protected:
uint32_t bit_field_;
- static const uint8_t kNextBitFieldIndex = NodeTypeField::kNext;
+
+ template <class T, int size>
+ using NextBitField = NodeTypeField::Next<T, size>;
AstNode(int position, NodeType type)
: position_(position), bit_field_(NodeTypeField::encode(type)) {}
@@ -182,8 +186,6 @@ class AstNode: public ZoneObject {
class Statement : public AstNode {
protected:
Statement(int position, NodeType type) : AstNode(position, type) {}
-
- static const uint8_t kNextBitFieldIndex = AstNode::kNextBitFieldIndex;
};
@@ -245,6 +247,14 @@ class Expression : public AstNode {
// that this also checks for loads of the global "undefined" variable.
bool IsUndefinedLiteral() const;
+ // True if either null literal or undefined literal.
+ inline bool IsNullOrUndefinedLiteral() const {
+ return IsNullLiteral() || IsUndefinedLiteral();
+ }
+
+ // True if a literal and not null or undefined.
+ bool IsLiteralButNotNullOrUndefined() const;
+
bool IsCompileTimeValue();
bool IsPattern() {
@@ -265,15 +275,15 @@ class Expression : public AstNode {
}
private:
- class IsParenthesizedField
- : public BitField<bool, AstNode::kNextBitFieldIndex, 1> {};
+ using IsParenthesizedField = AstNode::NextBitField<bool, 1>;
protected:
Expression(int pos, NodeType type) : AstNode(pos, type) {
DCHECK(!is_parenthesized());
}
- static const uint8_t kNextBitFieldIndex = IsParenthesizedField::kNext;
+ template <class T, int size>
+ using NextBitField = IsParenthesizedField::Next<T, size>;
};
class FailureExpression : public Expression {
@@ -321,8 +331,7 @@ class BreakableStatement : public Statement {
}
private:
- class BreakableTypeField
- : public BitField<BreakableType, Statement::kNextBitFieldIndex, 1> {};
+ using BreakableTypeField = Statement::NextBitField<BreakableType, 1>;
protected:
BreakableStatement(BreakableType breakable_type, int position, NodeType type)
@@ -330,7 +339,8 @@ class BreakableStatement : public Statement {
bit_field_ |= BreakableTypeField::encode(breakable_type);
}
- static const uint8_t kNextBitFieldIndex = BreakableTypeField::kNext;
+ template <class T, int size>
+ using NextBitField = BreakableTypeField::Next<T, size>;
};
class Block : public BreakableStatement {
@@ -357,10 +367,8 @@ class Block : public BreakableStatement {
ZonePtrList<Statement> statements_;
Scope* scope_;
- class IgnoreCompletionField
- : public BitField<bool, BreakableStatement::kNextBitFieldIndex, 1> {};
- class IsLabeledField
- : public BitField<bool, IgnoreCompletionField::kNext, 1> {};
+ using IgnoreCompletionField = BreakableStatement::NextBitField<bool, 1>;
+ using IsLabeledField = IgnoreCompletionField::Next<bool, 1>;
protected:
Block(Zone* zone, ZonePtrList<const AstRawString>* labels, int capacity,
@@ -448,8 +456,7 @@ class VariableDeclaration : public Declaration {
private:
friend class AstNodeFactory;
- class IsNestedField
- : public BitField<bool, Declaration::kNextBitFieldIndex, 1> {};
+ using IsNestedField = Declaration::NextBitField<bool, 1>;
protected:
explicit VariableDeclaration(int pos, bool is_nested = false)
@@ -457,7 +464,8 @@ class VariableDeclaration : public Declaration {
bit_field_ = IsNestedField::update(bit_field_, is_nested);
}
- static const uint8_t kNextBitFieldIndex = IsNestedField::kNext;
+ template <class T, int size>
+ using NextBitField = IsNestedField::Next<T, size>;
};
// For var declarations that appear in a block scope.
@@ -524,9 +532,6 @@ class IterationStatement : public BreakableStatement {
body_(nullptr) {}
void Initialize(Statement* body) { body_ = body; }
- static const uint8_t kNextBitFieldIndex =
- BreakableStatement::kNextBitFieldIndex;
-
private:
ZonePtrList<const AstRawString>* labels_;
ZonePtrList<const AstRawString>* own_labels_;
@@ -740,8 +745,7 @@ class ReturnStatement final : public JumpStatement {
Expression* expression_;
int end_position_;
- class TypeField
- : public BitField<Type, JumpStatement::kNextBitFieldIndex, 1> {};
+ using TypeField = JumpStatement::NextBitField<Type, 1>;
};
@@ -977,8 +981,7 @@ class SloppyBlockFunctionStatement final : public Statement {
private:
friend class AstNodeFactory;
- class TokenField
- : public BitField<Token::Value, Statement::kNextBitFieldIndex, 8> {};
+ using TokenField = Statement::NextBitField<Token::Value, 8>;
SloppyBlockFunctionStatement(int pos, Variable* var, Token::Value init,
Statement* statement)
@@ -1079,7 +1082,7 @@ class Literal final : public Expression {
private:
friend class AstNodeFactory;
- class TypeField : public BitField<Type, Expression::kNextBitFieldIndex, 4> {};
+ using TypeField = Expression::NextBitField<Type, 4>;
Literal(int smi, int position) : Expression(position, kLiteral), smi_(smi) {
bit_field_ = TypeField::update(bit_field_, kSmi);
@@ -1210,10 +1213,9 @@ class AggregateLiteral : public MaterializedLiteral {
private:
int depth_ : 31;
- class NeedsInitialAllocationSiteField
- : public BitField<bool, MaterializedLiteral::kNextBitFieldIndex, 1> {};
- class IsSimpleField
- : public BitField<bool, NeedsInitialAllocationSiteField::kNext, 1> {};
+ using NeedsInitialAllocationSiteField =
+ MaterializedLiteral::NextBitField<bool, 1>;
+ using IsSimpleField = NeedsInitialAllocationSiteField::Next<bool, 1>;
protected:
friend class AstNodeFactory;
@@ -1236,7 +1238,8 @@ class AggregateLiteral : public MaterializedLiteral {
bit_field_ = NeedsInitialAllocationSiteField::update(bit_field_, required);
}
- static const uint8_t kNextBitFieldIndex = IsSimpleField::kNext;
+ template <class T, int size>
+ using NextBitField = IsSimpleField::Next<T, size>;
};
// Common supertype for ObjectLiteralProperty and ClassLiteralProperty
@@ -1375,12 +1378,6 @@ class ObjectLiteral final : public AggregateLiteral {
static_cast<int>(AggregateLiteral::kNeedsInitialAllocationSite) <
static_cast<int>(kFastElements));
- struct Accessors: public ZoneObject {
- Accessors() : getter(nullptr), setter(nullptr) {}
- ObjectLiteralProperty* getter;
- ObjectLiteralProperty* setter;
- };
-
private:
friend class AstNodeFactory;
@@ -1408,19 +1405,14 @@ class ObjectLiteral final : public AggregateLiteral {
void set_has_null_protoype(bool has_null_prototype) {
bit_field_ = HasNullPrototypeField::update(bit_field_, has_null_prototype);
}
-
uint32_t boilerplate_properties_;
Handle<ObjectBoilerplateDescription> boilerplate_description_;
ZoneList<Property*> properties_;
- class HasElementsField
- : public BitField<bool, AggregateLiteral::kNextBitFieldIndex, 1> {};
- class HasRestPropertyField
- : public BitField<bool, HasElementsField::kNext, 1> {};
- class FastElementsField
- : public BitField<bool, HasRestPropertyField::kNext, 1> {};
- class HasNullPrototypeField
- : public BitField<bool, FastElementsField::kNext, 1> {};
+ using HasElementsField = AggregateLiteral::NextBitField<bool, 1>;
+ using HasRestPropertyField = HasElementsField::Next<bool, 1>;
+ using FastElementsField = HasRestPropertyField::Next<bool, 1>;
+ using HasNullPrototypeField = FastElementsField::Next<bool, 1>;
};
// An array literal has a literals object that is used
@@ -1512,6 +1504,9 @@ class VariableProxy final : public Expression {
var()->SetMaybeAssigned();
}
}
+ void clear_is_assigned() {
+ bit_field_ = IsAssignedField::update(bit_field_, false);
+ }
bool is_resolved() const { return IsResolvedField::decode(bit_field_); }
void set_is_resolved() {
@@ -1586,15 +1581,11 @@ class VariableProxy final : public Expression {
explicit VariableProxy(const VariableProxy* copy_from);
- class IsAssignedField
- : public BitField<bool, Expression::kNextBitFieldIndex, 1> {};
- class IsResolvedField : public BitField<bool, IsAssignedField::kNext, 1> {};
- class IsRemovedFromUnresolvedField
- : public BitField<bool, IsResolvedField::kNext, 1> {};
- class IsNewTargetField
- : public BitField<bool, IsRemovedFromUnresolvedField::kNext, 1> {};
- class HoleCheckModeField
- : public BitField<HoleCheckMode, IsNewTargetField::kNext, 1> {};
+ using IsAssignedField = Expression::NextBitField<bool, 1>;
+ using IsResolvedField = IsAssignedField::Next<bool, 1>;
+ using IsRemovedFromUnresolvedField = IsResolvedField::Next<bool, 1>;
+ using IsNewTargetField = IsRemovedFromUnresolvedField::Next<bool, 1>;
+ using HoleCheckModeField = IsNewTargetField::Next<HoleCheckMode, 1>;
union {
const AstRawString* raw_name_; // if !is_resolved_
@@ -1607,20 +1598,41 @@ class VariableProxy final : public Expression {
friend base::ThreadedListTraits<VariableProxy>;
};
+// Wraps an optional chain to provide a wrapper for jump labels.
+class OptionalChain final : public Expression {
+ public:
+ Expression* expression() const { return expression_; }
+
+ private:
+ friend class AstNodeFactory;
+
+ explicit OptionalChain(Expression* expression)
+ : Expression(0, kOptionalChain), expression_(expression) {}
+
+ Expression* expression_;
+};
+
// Assignments to a property will use one of several types of property access.
// Otherwise, the assignment is to a non-property (a global, a local slot, a
// parameter slot, or a destructuring pattern).
enum AssignType {
- NON_PROPERTY, // destructuring
- NAMED_PROPERTY, // obj.key
- KEYED_PROPERTY, // obj[key]
- NAMED_SUPER_PROPERTY, // super.key
- KEYED_SUPER_PROPERTY, // super[key]
- PRIVATE_METHOD // obj.#key: #key is a private method
+ NON_PROPERTY, // destructuring
+ NAMED_PROPERTY, // obj.key
+ KEYED_PROPERTY, // obj[key]
+ NAMED_SUPER_PROPERTY, // super.key
+ KEYED_SUPER_PROPERTY, // super[key]
+ PRIVATE_METHOD, // obj.#key: #key is a private method
+ PRIVATE_GETTER_ONLY, // obj.#key: #key only has a getter defined
+ PRIVATE_SETTER_ONLY, // obj.#key: #key only has a setter defined
+ PRIVATE_GETTER_AND_SETTER // obj.#key: #key has both accessors defined
};
class Property final : public Expression {
public:
+ bool is_optional_chain_link() const {
+ return IsOptionalChainLinkField::decode(bit_field_);
+ }
+
bool IsValidReferenceExpression() const { return true; }
Expression* obj() const { return obj_; }
@@ -1637,8 +1649,21 @@ class Property final : public Expression {
VariableProxy* proxy = property->key()->AsVariableProxy();
DCHECK_NOT_NULL(proxy);
Variable* var = proxy->var();
- // Use KEYED_PROPERTY for private fields.
- return var->requires_brand_check() ? PRIVATE_METHOD : KEYED_PROPERTY;
+
+ switch (var->mode()) {
+ case VariableMode::kPrivateMethod:
+ return PRIVATE_METHOD;
+ case VariableMode::kConst:
+ return KEYED_PROPERTY; // Use KEYED_PROPERTY for private fields.
+ case VariableMode::kPrivateGetterOnly:
+ return PRIVATE_GETTER_ONLY;
+ case VariableMode::kPrivateSetterOnly:
+ return PRIVATE_SETTER_ONLY;
+ case VariableMode::kPrivateGetterAndSetter:
+ return PRIVATE_GETTER_AND_SETTER;
+ default:
+ UNREACHABLE();
+ }
}
bool super_access = property->IsSuperAccess();
return (property->key()->IsPropertyName())
@@ -1649,10 +1674,13 @@ class Property final : public Expression {
private:
friend class AstNodeFactory;
- Property(Expression* obj, Expression* key, int pos)
+ Property(Expression* obj, Expression* key, int pos, bool optional_chain)
: Expression(pos, kProperty), obj_(obj), key_(key) {
+ bit_field_ |= IsOptionalChainLinkField::encode(optional_chain);
}
+ using IsOptionalChainLinkField = Expression::NextBitField<bool, 1>;
+
Expression* obj_;
Expression* key_;
};
@@ -1690,6 +1718,10 @@ class Call final : public Expression {
return IsTaggedTemplateField::decode(bit_field_);
}
+ bool is_optional_chain_link() const {
+ return IsOptionalChainLinkField::decode(bit_field_);
+ }
+
bool only_last_arg_is_spread() {
return !arguments_.is_empty() && arguments_.last()->IsSpread();
}
@@ -1722,13 +1754,14 @@ class Call final : public Expression {
Call(Zone* zone, Expression* expression,
const ScopedPtrList<Expression>& arguments, int pos,
- PossiblyEval possibly_eval)
+ PossiblyEval possibly_eval, bool optional_chain)
: Expression(pos, kCall),
expression_(expression),
arguments_(0, nullptr) {
bit_field_ |=
IsPossiblyEvalField::encode(possibly_eval == IS_POSSIBLY_EVAL) |
- IsTaggedTemplateField::encode(false);
+ IsTaggedTemplateField::encode(false) |
+ IsOptionalChainLinkField::encode(optional_chain);
arguments.CopyTo(&arguments_, zone);
}
@@ -1739,14 +1772,14 @@ class Call final : public Expression {
expression_(expression),
arguments_(0, nullptr) {
bit_field_ |= IsPossiblyEvalField::encode(false) |
- IsTaggedTemplateField::encode(true);
+ IsTaggedTemplateField::encode(true) |
+ IsOptionalChainLinkField::encode(false);
arguments.CopyTo(&arguments_, zone);
}
- class IsPossiblyEvalField
- : public BitField<bool, Expression::kNextBitFieldIndex, 1> {};
- class IsTaggedTemplateField
- : public BitField<bool, IsPossiblyEvalField::kNext, 1> {};
+ using IsPossiblyEvalField = Expression::NextBitField<bool, 1>;
+ using IsTaggedTemplateField = IsPossiblyEvalField::Next<bool, 1>;
+ using IsOptionalChainLinkField = IsTaggedTemplateField::Next<bool, 1>;
Expression* expression_;
ZonePtrList<Expression> arguments_;
@@ -1838,8 +1871,7 @@ class UnaryOperation final : public Expression {
Expression* expression_;
- class OperatorField
- : public BitField<Token::Value, Expression::kNextBitFieldIndex, 7> {};
+ using OperatorField = Expression::NextBitField<Token::Value, 7>;
};
@@ -1865,8 +1897,7 @@ class BinaryOperation final : public Expression {
Expression* left_;
Expression* right_;
- class OperatorField
- : public BitField<Token::Value, Expression::kNextBitFieldIndex, 7> {};
+ using OperatorField = Expression::NextBitField<Token::Value, 7>;
};
class NaryOperation final : public Expression {
@@ -1925,8 +1956,7 @@ class NaryOperation final : public Expression {
};
ZoneVector<NaryOperationEntry> subsequent_;
- class OperatorField
- : public BitField<Token::Value, Expression::kNextBitFieldIndex, 7> {};
+ using OperatorField = Expression::NextBitField<Token::Value, 7>;
};
class CountOperation final : public Expression {
@@ -1946,9 +1976,8 @@ class CountOperation final : public Expression {
bit_field_ |= IsPrefixField::encode(is_prefix) | TokenField::encode(op);
}
- class IsPrefixField
- : public BitField<bool, Expression::kNextBitFieldIndex, 1> {};
- class TokenField : public BitField<Token::Value, IsPrefixField::kNext, 7> {};
+ using IsPrefixField = Expression::NextBitField<bool, 1>;
+ using TokenField = IsPrefixField::Next<Token::Value, 7>;
Expression* expression_;
};
@@ -1978,8 +2007,7 @@ class CompareOperation final : public Expression {
Expression* left_;
Expression* right_;
- class OperatorField
- : public BitField<Token::Value, Expression::kNextBitFieldIndex, 7> {};
+ using OperatorField = Expression::NextBitField<Token::Value, 7>;
};
@@ -2071,10 +2099,8 @@ class Assignment : public Expression {
private:
friend class AstNodeFactory;
- class TokenField
- : public BitField<Token::Value, Expression::kNextBitFieldIndex, 7> {};
- class LookupHoistingModeField : public BitField<bool, TokenField::kNext, 1> {
- };
+ using TokenField = Expression::NextBitField<Token::Value, 7>;
+ using LookupHoistingModeField = TokenField::Next<bool, 1>;
Expression* target_;
Expression* value_;
@@ -2132,8 +2158,7 @@ class Suspend : public Expression {
Expression* expression_;
- class OnAbruptResumeField
- : public BitField<OnAbruptResume, Expression::kNextBitFieldIndex, 1> {};
+ using OnAbruptResumeField = Expression::NextBitField<OnAbruptResume, 1>;
};
class Yield final : public Suspend {
@@ -2175,14 +2200,6 @@ class Throw final : public Expression {
class FunctionLiteral final : public Expression {
public:
- enum FunctionType {
- kAnonymousExpression,
- kNamedExpression,
- kDeclaration,
- kAccessorOrMethod,
- kWrapped,
- };
-
enum ParameterFlag : uint8_t {
kNoDuplicateParameters,
kHasDuplicateParameters
@@ -2204,12 +2221,8 @@ class FunctionLiteral final : public Expression {
int function_token_position() const { return function_token_position_; }
int start_position() const;
int end_position() const;
- bool is_declaration() const { return function_type() == kDeclaration; }
- bool is_named_expression() const {
- return function_type() == kNamedExpression;
- }
bool is_anonymous_expression() const {
- return function_type() == kAnonymousExpression;
+ return syntax_kind() == FunctionSyntaxKind::kAnonymousExpression;
}
void mark_as_oneshot_iife() {
@@ -2219,7 +2232,6 @@ class FunctionLiteral final : public Expression {
bool is_toplevel() const {
return function_literal_id() == kFunctionLiteralIdTopLevel;
}
- bool is_wrapped() const { return function_type() == kWrapped; }
V8_EXPORT_PRIVATE LanguageMode language_mode() const;
static bool NeedsHomeObject(Expression* expr);
@@ -2289,8 +2301,8 @@ class FunctionLiteral final : public Expression {
V8_EXPORT_PRIVATE bool ShouldEagerCompile() const;
V8_EXPORT_PRIVATE void SetShouldEagerCompile();
- FunctionType function_type() const {
- return FunctionTypeBits::decode(bit_field_);
+ FunctionSyntaxKind syntax_kind() const {
+ return FunctionSyntaxKindBits::decode(bit_field_);
}
FunctionKind kind() const;
@@ -2342,7 +2354,7 @@ class FunctionLiteral final : public Expression {
AstValueFactory* ast_value_factory, DeclarationScope* scope,
const ScopedPtrList<Statement>& body,
int expected_property_count, int parameter_count,
- int function_length, FunctionType function_type,
+ int function_length, FunctionSyntaxKind function_syntax_kind,
ParameterFlag has_duplicate_parameters,
EagerCompileHint eager_compile_hint, int position,
bool has_braces, int function_literal_id,
@@ -2359,28 +2371,28 @@ class FunctionLiteral final : public Expression {
body_(0, nullptr),
raw_inferred_name_(ast_value_factory->empty_cons_string()),
produced_preparse_data_(produced_preparse_data) {
- bit_field_ |=
- FunctionTypeBits::encode(function_type) | Pretenure::encode(false) |
- HasDuplicateParameters::encode(has_duplicate_parameters ==
- kHasDuplicateParameters) |
- DontOptimizeReasonField::encode(BailoutReason::kNoReason) |
- RequiresInstanceMembersInitializer::encode(false) |
- HasBracesField::encode(has_braces) | OneshotIIFEBit::encode(false);
+ bit_field_ |= FunctionSyntaxKindBits::encode(function_syntax_kind) |
+ Pretenure::encode(false) |
+ HasDuplicateParameters::encode(has_duplicate_parameters ==
+ kHasDuplicateParameters) |
+ DontOptimizeReasonField::encode(BailoutReason::kNoReason) |
+ RequiresInstanceMembersInitializer::encode(false) |
+ HasBracesField::encode(has_braces) |
+ OneshotIIFEBit::encode(false);
if (eager_compile_hint == kShouldEagerCompile) SetShouldEagerCompile();
body.CopyTo(&body_, zone);
}
- class FunctionTypeBits
- : public BitField<FunctionType, Expression::kNextBitFieldIndex, 3> {};
- class Pretenure : public BitField<bool, FunctionTypeBits::kNext, 1> {};
- class HasDuplicateParameters : public BitField<bool, Pretenure::kNext, 1> {};
- class DontOptimizeReasonField
- : public BitField<BailoutReason, HasDuplicateParameters::kNext, 8> {};
- class RequiresInstanceMembersInitializer
- : public BitField<bool, DontOptimizeReasonField::kNext, 1> {};
- class HasBracesField
- : public BitField<bool, RequiresInstanceMembersInitializer::kNext, 1> {};
- class OneshotIIFEBit : public BitField<bool, HasBracesField::kNext, 1> {};
+ using FunctionSyntaxKindBits =
+ Expression::NextBitField<FunctionSyntaxKind, 3>;
+ using Pretenure = FunctionSyntaxKindBits::Next<bool, 1>;
+ using HasDuplicateParameters = Pretenure::Next<bool, 1>;
+ using DontOptimizeReasonField =
+ HasDuplicateParameters::Next<BailoutReason, 8>;
+ using RequiresInstanceMembersInitializer =
+ DontOptimizeReasonField::Next<bool, 1>;
+ using HasBracesField = RequiresInstanceMembersInitializer::Next<bool, 1>;
+ using OneshotIIFEBit = HasBracesField::Next<bool, 1>;
// expected_property_count_ is the sum of instance fields and properties.
// It can vary depending on whether a function is lazily or eagerly parsed.
@@ -2432,6 +2444,11 @@ class ClassLiteralProperty final : public LiteralProperty {
return private_or_computed_name_var_;
}
+ bool NeedsHomeObjectOnClassPrototype() const {
+ return is_private() && kind_ == METHOD &&
+ FunctionLiteral::NeedsHomeObject(value_);
+ }
+
private:
friend class AstNodeFactory;
@@ -2525,12 +2542,9 @@ class ClassLiteral final : public Expression {
ZonePtrList<Property>* properties_;
FunctionLiteral* static_fields_initializer_;
FunctionLiteral* instance_members_initializer_function_;
- class HasNameStaticProperty
- : public BitField<bool, Expression::kNextBitFieldIndex, 1> {};
- class HasStaticComputedNames
- : public BitField<bool, HasNameStaticProperty::kNext, 1> {};
- class IsAnonymousExpression
- : public BitField<bool, HasStaticComputedNames::kNext, 1> {};
+ using HasNameStaticProperty = Expression::NextBitField<bool, 1>;
+ using HasStaticComputedNames = HasNameStaticProperty::Next<bool, 1>;
+ using IsAnonymousExpression = HasStaticComputedNames::Next<bool, 1>;
};
@@ -3046,8 +3060,13 @@ class AstNodeFactory final {
return new (zone_) Variable(variable);
}
- Property* NewProperty(Expression* obj, Expression* key, int pos) {
- return new (zone_) Property(obj, key, pos);
+ OptionalChain* NewOptionalChain(Expression* expression) {
+ return new (zone_) OptionalChain(expression);
+ }
+
+ Property* NewProperty(Expression* obj, Expression* key, int pos,
+ bool optional_chain = false) {
+ return new (zone_) Property(obj, key, pos, optional_chain);
}
ResolvedProperty* NewResolvedProperty(VariableProxy* obj,
@@ -3058,8 +3077,10 @@ class AstNodeFactory final {
Call* NewCall(Expression* expression,
const ScopedPtrList<Expression>& arguments, int pos,
- Call::PossiblyEval possibly_eval = Call::NOT_EVAL) {
- return new (zone_) Call(zone_, expression, arguments, pos, possibly_eval);
+ Call::PossiblyEval possibly_eval = Call::NOT_EVAL,
+ bool optional_chain = false) {
+ return new (zone_)
+ Call(zone_, expression, arguments, pos, possibly_eval, optional_chain);
}
Call* NewTaggedTemplate(Expression* expression,
@@ -3189,13 +3210,13 @@ class AstNodeFactory final {
const ScopedPtrList<Statement>& body, int expected_property_count,
int parameter_count, int function_length,
FunctionLiteral::ParameterFlag has_duplicate_parameters,
- FunctionLiteral::FunctionType function_type,
+ FunctionSyntaxKind function_syntax_kind,
FunctionLiteral::EagerCompileHint eager_compile_hint, int position,
bool has_braces, int function_literal_id,
ProducedPreparseData* produced_preparse_data = nullptr) {
return new (zone_) FunctionLiteral(
zone_, name, ast_value_factory_, scope, body, expected_property_count,
- parameter_count, function_length, function_type,
+ parameter_count, function_length, function_syntax_kind,
has_duplicate_parameters, eager_compile_hint, position, has_braces,
function_literal_id, produced_preparse_data);
}
@@ -3209,7 +3230,7 @@ class AstNodeFactory final {
return new (zone_) FunctionLiteral(
zone_, ast_value_factory_->empty_string(), ast_value_factory_, scope,
body, expected_property_count, parameter_count, parameter_count,
- FunctionLiteral::kAnonymousExpression,
+ FunctionSyntaxKind::kAnonymousExpression,
FunctionLiteral::kNoDuplicateParameters,
FunctionLiteral::kShouldLazyCompile, 0, /* has_braces */ false,
kFunctionLiteralIdTopLevel);
diff --git a/deps/v8/src/ast/modules.cc b/deps/v8/src/ast/modules.cc
index 261b72c352..dbd20f50a8 100644
--- a/deps/v8/src/ast/modules.cc
+++ b/deps/v8/src/ast/modules.cc
@@ -84,10 +84,11 @@ void SourceTextModuleDescriptor::AddStarExport(
}
namespace {
-Handle<Object> ToStringOrUndefined(Isolate* isolate, const AstRawString* s) {
+Handle<HeapObject> ToStringOrUndefined(Isolate* isolate,
+ const AstRawString* s) {
return (s == nullptr)
- ? Handle<Object>::cast(isolate->factory()->undefined_value())
- : Handle<Object>::cast(s->string());
+ ? Handle<HeapObject>::cast(isolate->factory()->undefined_value())
+ : Handle<HeapObject>::cast(s->string());
}
} // namespace
diff --git a/deps/v8/src/ast/prettyprinter.cc b/deps/v8/src/ast/prettyprinter.cc
index c0fe3baff3..581517ee4e 100644
--- a/deps/v8/src/ast/prettyprinter.cc
+++ b/deps/v8/src/ast/prettyprinter.cc
@@ -27,6 +27,8 @@ CallPrinter::CallPrinter(Isolate* isolate, bool is_user_js)
is_call_error_ = false;
is_iterator_error_ = false;
is_async_iterator_error_ = false;
+ destructuring_prop_ = nullptr;
+ destructuring_assignment_ = nullptr;
is_user_js_ = is_user_js;
function_kind_ = kNormalFunction;
InitializeAstVisitor(isolate);
@@ -299,24 +301,50 @@ void CallPrinter::VisitVariableProxy(VariableProxy* node) {
void CallPrinter::VisitAssignment(Assignment* node) {
- Find(node->target());
- if (node->target()->IsArrayLiteral()) {
- // Special case the visit for destructuring array assignment.
- bool was_found = false;
- if (node->value()->position() == position_) {
- is_iterator_error_ = true;
+ bool was_found = false;
+ if (node->target()->IsObjectLiteral()) {
+ ObjectLiteral* target = node->target()->AsObjectLiteral();
+ if (target->position() == position_) {
was_found = !found_;
- if (was_found) {
- found_ = true;
+ found_ = true;
+ destructuring_assignment_ = node;
+ } else {
+ for (ObjectLiteralProperty* prop : *target->properties()) {
+ if (prop->value()->position() == position_) {
+ was_found = !found_;
+ found_ = true;
+ destructuring_prop_ = prop;
+ destructuring_assignment_ = node;
+ break;
+ }
}
}
- Find(node->value(), true);
- if (was_found) {
- done_ = true;
- found_ = false;
+ }
+ if (!was_found) {
+ Find(node->target());
+ if (node->target()->IsArrayLiteral()) {
+ // Special case the visit for destructuring array assignment.
+ bool was_found = false;
+ if (node->value()->position() == position_) {
+ is_iterator_error_ = true;
+ was_found = !found_;
+ found_ = true;
+ }
+ Find(node->value(), true);
+ if (was_found) {
+ done_ = true;
+ found_ = false;
+ }
+ } else {
+ Find(node->value());
}
} else {
- Find(node->value());
+ Find(node->value(), true);
+ }
+
+ if (was_found) {
+ done_ = true;
+ found_ = false;
}
}
@@ -342,6 +370,9 @@ void CallPrinter::VisitAwait(Await* node) { Find(node->expression()); }
void CallPrinter::VisitThrow(Throw* node) { Find(node->exception()); }
+void CallPrinter::VisitOptionalChain(OptionalChain* node) {
+ Find(node->expression());
+}
void CallPrinter::VisitProperty(Property* node) {
Expression* key = node->key();
@@ -349,12 +380,18 @@ void CallPrinter::VisitProperty(Property* node) {
if (literal != nullptr &&
literal->BuildValue(isolate_)->IsInternalizedString()) {
Find(node->obj(), true);
+ if (node->is_optional_chain_link()) {
+ Print("?");
+ }
Print(".");
// TODO(adamk): Teach Literal how to print its values without
// allocating on the heap.
PrintLiteral(literal->BuildValue(isolate_), false);
} else {
Find(node->obj(), true);
+ if (node->is_optional_chain_link()) {
+ Print("?.");
+ }
Print("[");
Find(key, true);
Print("]");
@@ -1272,6 +1309,11 @@ void AstPrinter::VisitThrow(Throw* node) {
Visit(node->exception());
}
+void AstPrinter::VisitOptionalChain(OptionalChain* node) {
+ IndentedScope indent(this, "OPTIONAL_CHAIN", node->position());
+ Visit(node->expression());
+}
+
void AstPrinter::VisitProperty(Property* node) {
EmbeddedVector<char, 128> buf;
SNPrintF(buf, "PROPERTY");
@@ -1289,6 +1331,18 @@ void AstPrinter::VisitProperty(Property* node) {
PrintIndentedVisit("PRIVATE_METHOD", node->key());
break;
}
+ case PRIVATE_GETTER_ONLY: {
+ PrintIndentedVisit("PRIVATE_GETTER_ONLY", node->key());
+ break;
+ }
+ case PRIVATE_SETTER_ONLY: {
+ PrintIndentedVisit("PRIVATE_SETTER_ONLY", node->key());
+ break;
+ }
+ case PRIVATE_GETTER_AND_SETTER: {
+ PrintIndentedVisit("PRIVATE_GETTER_AND_SETTER", node->key());
+ break;
+ }
case KEYED_PROPERTY:
case KEYED_SUPER_PROPERTY: {
PrintIndentedVisit("KEY", node->key());
diff --git a/deps/v8/src/ast/prettyprinter.h b/deps/v8/src/ast/prettyprinter.h
index cceb5fc269..322fd9fb14 100644
--- a/deps/v8/src/ast/prettyprinter.h
+++ b/deps/v8/src/ast/prettyprinter.h
@@ -31,6 +31,12 @@ class CallPrinter final : public AstVisitor<CallPrinter> {
kCallAndAsyncIterator
};
ErrorHint GetErrorHint() const;
+ ObjectLiteralProperty* destructuring_prop() const {
+ return destructuring_prop_;
+ }
+ Assignment* destructuring_assignment() const {
+ return destructuring_assignment_;
+ }
// Individual nodes
#define DECLARE_VISIT(type) void Visit##type(type* node);
@@ -54,6 +60,8 @@ class CallPrinter final : public AstVisitor<CallPrinter> {
bool is_iterator_error_;
bool is_async_iterator_error_;
bool is_call_error_;
+ ObjectLiteralProperty* destructuring_prop_;
+ Assignment* destructuring_assignment_;
FunctionKind function_kind_;
DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
diff --git a/deps/v8/src/ast/scopes.cc b/deps/v8/src/ast/scopes.cc
index 237d98ec60..c4d0999978 100644
--- a/deps/v8/src/ast/scopes.cc
+++ b/deps/v8/src/ast/scopes.cc
@@ -40,7 +40,6 @@ Variable* VariableMap::Declare(Zone* zone, Scope* scope,
VariableKind kind,
InitializationFlag initialization_flag,
MaybeAssignedFlag maybe_assigned_flag,
- RequiresBrandCheckFlag requires_brand_check,
bool* was_added) {
// AstRawStrings are unambiguous, i.e., the same string is always represented
// by the same AstRawString*.
@@ -52,9 +51,8 @@ Variable* VariableMap::Declare(Zone* zone, Scope* scope,
if (*was_added) {
// The variable has not been declared yet -> insert it.
DCHECK_EQ(name, p->key);
- Variable* variable =
- new (zone) Variable(scope, name, mode, kind, initialization_flag,
- maybe_assigned_flag, requires_brand_check);
+ Variable* variable = new (zone) Variable(
+ scope, name, mode, kind, initialization_flag, maybe_assigned_flag);
p->value = variable;
}
return reinterpret_cast<Variable*>(p->value);
@@ -170,7 +168,6 @@ Scope::Scope(Zone* zone, ScopeType scope_type, Handle<ScopeInfo> scope_info)
#ifdef DEBUG
already_resolved_ = true;
#endif
- if (scope_info->CallsSloppyEval()) scope_calls_eval_ = true;
set_language_mode(scope_info->language_mode());
num_heap_slots_ = scope_info->ContextLength();
DCHECK_LE(Context::MIN_CONTEXT_SLOTS, num_heap_slots_);
@@ -186,6 +183,10 @@ DeclarationScope::DeclarationScope(Zone* zone, ScopeType scope_type,
params_(0, zone) {
DCHECK_NE(scope_type, SCRIPT_SCOPE);
SetDefaults();
+ if (scope_info->SloppyEvalCanExtendVars()) {
+ DCHECK(!is_eval_scope());
+ sloppy_eval_can_extend_vars_ = true;
+ }
}
Scope::Scope(Zone* zone, const AstRawString* catch_variable_name,
@@ -258,7 +259,8 @@ void Scope::SetDefaults() {
set_language_mode(LanguageMode::kSloppy);
- scope_calls_eval_ = false;
+ calls_eval_ = false;
+ sloppy_eval_can_extend_vars_ = false;
scope_nonlinear_ = false;
is_hidden_ = false;
is_debug_evaluate_scope_ = false;
@@ -380,11 +382,8 @@ Scope* Scope::DeserializeScopeChain(Isolate* isolate, Zone* zone,
if (deserialization_mode == DeserializationMode::kIncludingVariables &&
script_scope->scope_info_.is_null()) {
- Handle<ScriptContextTable> table(
- isolate->native_context()->script_context_table(), isolate);
- Handle<Context> first = ScriptContextTable::GetContext(isolate, table, 0);
- Handle<ScopeInfo> scope_info(first->scope_info(), isolate);
- script_scope->SetScriptScopeInfo(scope_info);
+ script_scope->SetScriptScopeInfo(
+ ReadOnlyRoots(isolate).global_this_binding_scope_info_handle());
}
if (innermost_scope == nullptr) return script_scope;
@@ -626,7 +625,7 @@ Variable* DeclarationScope::DeclareFunctionVar(const AstRawString* name,
: NORMAL_VARIABLE;
function_ = new (zone())
Variable(this, name, VariableMode::kConst, kind, kCreatedInitialized);
- if (calls_sloppy_eval()) {
+ if (sloppy_eval_can_extend_vars()) {
cache->NonLocal(name, VariableMode::kDynamic);
} else {
cache->variables_.Add(zone(), function_);
@@ -652,7 +651,8 @@ Scope* Scope::FinalizeBlockScope() {
#endif
if (variables_.occupancy() > 0 ||
- (is_declaration_scope() && AsDeclarationScope()->calls_sloppy_eval())) {
+ (is_declaration_scope() &&
+ AsDeclarationScope()->sloppy_eval_can_extend_vars())) {
return this;
}
@@ -682,10 +682,10 @@ Scope* Scope::FinalizeBlockScope() {
if (inner_scope_calls_eval_) outer_scope()->inner_scope_calls_eval_ = true;
- // No need to propagate scope_calls_eval_, since if it was relevant to
- // this scope we would have had to bail out at the top.
- DCHECK(!scope_calls_eval_ || !is_declaration_scope() ||
- !is_sloppy(language_mode()));
+ // No need to propagate sloppy_eval_can_extend_vars_, since if it was relevant
+ // to this scope we would have had to bail out at the top.
+ DCHECK(!is_declaration_scope() ||
+ !AsDeclarationScope()->sloppy_eval_can_extend_vars());
// This block does not need a context.
num_heap_slots_ = 0;
@@ -750,8 +750,8 @@ void Scope::Snapshot::Reparent(DeclarationScope* new_parent) {
outer_closure->locals_.Rewind(top_local_);
// Move eval calls since Snapshot's creation into new_parent.
- if (outer_scope_and_calls_eval_->scope_calls_eval_) {
- new_parent->scope_calls_eval_ = true;
+ if (outer_scope_and_calls_eval_->calls_eval_) {
+ new_parent->RecordDeclarationScopeEvalCall();
new_parent->inner_scope_calls_eval_ = true;
}
@@ -787,13 +787,11 @@ Variable* Scope::LookupInScopeInfo(const AstRawString* name, Scope* cache) {
VariableMode mode;
InitializationFlag init_flag;
MaybeAssignedFlag maybe_assigned_flag;
- RequiresBrandCheckFlag requires_brand_check = kNoBrandCheck;
{
location = VariableLocation::CONTEXT;
index = ScopeInfo::ContextSlotIndex(*scope_info_, name_handle, &mode,
- &init_flag, &maybe_assigned_flag,
- &requires_brand_check);
+ &init_flag, &maybe_assigned_flag);
found = index >= 0;
}
@@ -818,9 +816,9 @@ Variable* Scope::LookupInScopeInfo(const AstRawString* name, Scope* cache) {
}
bool was_added;
- Variable* var = cache->variables_.Declare(
- zone(), this, name, mode, NORMAL_VARIABLE, init_flag, maybe_assigned_flag,
- requires_brand_check, &was_added);
+ Variable* var =
+ cache->variables_.Declare(zone(), this, name, mode, NORMAL_VARIABLE,
+ init_flag, maybe_assigned_flag, &was_added);
DCHECK(was_added);
var->AllocateTo(location, index);
return var;
@@ -873,6 +871,8 @@ Variable* Scope::DeclareLocal(const AstRawString* name, VariableMode mode,
VariableKind kind, bool* was_added,
InitializationFlag init_flag) {
DCHECK(!already_resolved_);
+ // Private methods should be declared with ClassScope::DeclarePrivateName()
+ DCHECK(!IsPrivateMethodOrAccessorVariableMode(mode));
// This function handles VariableMode::kVar, VariableMode::kLet, and
// VariableMode::kConst modes. VariableMode::kDynamic variables are
// introduced during variable allocation, and VariableMode::kTemporary
@@ -905,6 +905,8 @@ Variable* Scope::DeclareVariable(
VariableMode mode, VariableKind kind, InitializationFlag init,
bool* was_added, bool* sloppy_mode_block_scope_function_redefinition,
bool* ok) {
+ // Private methods should be declared with ClassScope::DeclarePrivateName()
+ DCHECK(!IsPrivateMethodOrAccessorVariableMode(mode));
DCHECK(IsDeclaredVariableMode(mode));
DCHECK(!already_resolved_);
DCHECK(!GetDeclarationScope()->is_being_lazily_parsed());
@@ -990,7 +992,8 @@ Variable* Scope::DeclareVariableName(const AstRawString* name,
DCHECK(IsDeclaredVariableMode(mode));
DCHECK(!already_resolved_);
DCHECK(GetDeclarationScope()->is_being_lazily_parsed());
-
+ // Private methods should be declared with ClassScope::DeclarePrivateName()
+ DCHECK(!IsPrivateMethodOrAccessorVariableMode(mode));
if (mode == VariableMode::kVar && !is_declaration_scope()) {
return GetDeclarationScope()->DeclareVariableName(name, mode, was_added,
kind);
@@ -1044,7 +1047,7 @@ Variable* DeclarationScope::DeclareDynamicGlobal(const AstRawString* name,
bool was_added;
return cache->variables_.Declare(
zone(), this, name, VariableMode::kDynamicGlobal, kind,
- kCreatedInitialized, kNotAssigned, kNoBrandCheck, &was_added);
+ kCreatedInitialized, kNotAssigned, &was_added);
// TODO(neis): Mark variable as maybe-assigned?
}
@@ -1243,7 +1246,7 @@ int Scope::ContextChainLengthUntilOutermostSloppyEval() const {
if (!s->NeedsContext()) continue;
length++;
if (s->is_declaration_scope() &&
- s->AsDeclarationScope()->calls_sloppy_eval()) {
+ s->AsDeclarationScope()->sloppy_eval_can_extend_vars()) {
result = length;
}
}
@@ -1384,9 +1387,10 @@ void Scope::CollectNonLocals(DeclarationScope* max_outer_scope,
void Scope::AnalyzePartially(DeclarationScope* max_outer_scope,
AstNodeFactory* ast_node_factory,
- UnresolvedList* new_unresolved_list) {
- this->ForEach([max_outer_scope, ast_node_factory,
- new_unresolved_list](Scope* scope) {
+ UnresolvedList* new_unresolved_list,
+ bool maybe_in_arrowhead) {
+ this->ForEach([max_outer_scope, ast_node_factory, new_unresolved_list,
+ maybe_in_arrowhead](Scope* scope) {
DCHECK_IMPLIES(scope->is_declaration_scope(),
!scope->AsDeclarationScope()->was_lazily_parsed());
@@ -1399,7 +1403,8 @@ void Scope::AnalyzePartially(DeclarationScope* max_outer_scope,
// Don't copy unresolved references to the script scope, unless it's a
// reference to a private name or method. In that case keep it so we
// can fail later.
- if (!max_outer_scope->outer_scope()->is_script_scope()) {
+ if (!max_outer_scope->outer_scope()->is_script_scope() ||
+ maybe_in_arrowhead) {
VariableProxy* copy = ast_node_factory->CopyVariableProxy(proxy);
new_unresolved_list->Add(copy);
}
@@ -1434,6 +1439,7 @@ void DeclarationScope::ResetAfterPreparsing(AstValueFactory* ast_value_factory,
sloppy_block_functions_.Clear();
rare_data_ = nullptr;
has_rest_ = false;
+ function_ = nullptr;
DCHECK_NE(zone_, ast_value_factory->zone());
zone_->ReleaseMemory();
@@ -1487,17 +1493,19 @@ void DeclarationScope::SavePreparseDataForDeclarationScope(Parser* parser) {
}
void DeclarationScope::AnalyzePartially(Parser* parser,
- AstNodeFactory* ast_node_factory) {
+ AstNodeFactory* ast_node_factory,
+ bool maybe_in_arrowhead) {
DCHECK(!force_eager_compilation_);
UnresolvedList new_unresolved_list;
if (!IsArrowFunction(function_kind_) &&
- (!outer_scope_->is_script_scope() ||
+ (!outer_scope_->is_script_scope() || maybe_in_arrowhead ||
(preparse_data_builder_ != nullptr &&
preparse_data_builder_->HasInnerFunctions()))) {
// Try to resolve unresolved variables for this Scope and migrate those
// which cannot be resolved inside. It doesn't make sense to try to resolve
// them in the outer Scopes here, because they are incomplete.
- Scope::AnalyzePartially(this, ast_node_factory, &new_unresolved_list);
+ Scope::AnalyzePartially(this, ast_node_factory, &new_unresolved_list,
+ maybe_in_arrowhead);
// Migrate function_ to the right Zone.
if (function_ != nullptr) {
@@ -1596,10 +1604,6 @@ void PrintVar(int indent, Variable* var) {
if (comma) PrintF(", ");
PrintF("hole initialization elided");
}
- if (var->requires_brand_check()) {
- if (comma) PrintF(", ");
- PrintF("requires brand check");
- }
PrintF("\n");
}
@@ -1676,7 +1680,8 @@ void Scope::Print(int n) {
Indent(n1, "// strict mode scope\n");
}
if (IsAsmModule()) Indent(n1, "// scope is an asm module\n");
- if (is_declaration_scope() && AsDeclarationScope()->calls_sloppy_eval()) {
+ if (is_declaration_scope() &&
+ AsDeclarationScope()->sloppy_eval_can_extend_vars()) {
Indent(n1, "// scope calls sloppy 'eval'\n");
}
if (is_declaration_scope() && AsDeclarationScope()->NeedsHomeObject()) {
@@ -1774,9 +1779,9 @@ Variable* Scope::NonLocal(const AstRawString* name, VariableMode mode) {
// Declare a new non-local.
DCHECK(IsDynamicVariableMode(mode));
bool was_added;
- Variable* var = variables_.Declare(zone(), this, name, mode, NORMAL_VARIABLE,
- kCreatedInitialized, kNotAssigned,
- kNoBrandCheck, &was_added);
+ Variable* var =
+ variables_.Declare(zone(), this, name, mode, NORMAL_VARIABLE,
+ kCreatedInitialized, kNotAssigned, &was_added);
// Allocate it by giving it a dynamic lookup.
var->AllocateTo(VariableLocation::LOOKUP, -1);
return var;
@@ -1814,7 +1819,18 @@ Variable* Scope::Lookup(VariableProxy* proxy, Scope* scope,
// We found a variable and we are done. (Even if there is an 'eval' in this
// scope which introduces the same variable again, the resulting variable
// remains the same.)
- if (var != nullptr) {
+ //
+ // For sloppy eval though, we skip dynamic variable to avoid resolving to a
+ // variable when the variable and proxy are in the same eval execution. The
+ // variable is not available on subsequent lazy executions of functions in
+ // the eval, so this avoids inner functions from looking up different
+ // variables during eager and lazy compilation.
+ //
+ // TODO(leszeks): Maybe we want to restrict this to e.g. lookups of a proxy
+ // living in a different scope to the current one, or some other
+ // optimisation.
+ if (var != nullptr &&
+ !(scope->is_eval_scope() && var->mode() == VariableMode::kDynamic)) {
if (mode == kParsedScope && force_context_allocation &&
!var->is_dynamic()) {
var->ForceContextAllocation();
@@ -1829,8 +1845,9 @@ Variable* Scope::Lookup(VariableProxy* proxy, Scope* scope,
return LookupWith(proxy, scope, outer_scope_end, entry_point,
force_context_allocation);
}
- if (V8_UNLIKELY(scope->is_declaration_scope() &&
- scope->AsDeclarationScope()->calls_sloppy_eval())) {
+ if (V8_UNLIKELY(
+ scope->is_declaration_scope() &&
+ scope->AsDeclarationScope()->sloppy_eval_can_extend_vars())) {
return LookupSloppyEval(proxy, scope, outer_scope_end, entry_point,
force_context_allocation);
}
@@ -1901,7 +1918,7 @@ Variable* Scope::LookupSloppyEval(VariableProxy* proxy, Scope* scope,
Scope* outer_scope_end, Scope* entry_point,
bool force_context_allocation) {
DCHECK(scope->is_declaration_scope() &&
- scope->AsDeclarationScope()->calls_sloppy_eval());
+ scope->AsDeclarationScope()->sloppy_eval_can_extend_vars());
// If we're compiling eval, it's possible that the outer scope is the first
// ScopeInfo-backed scope.
@@ -2065,7 +2082,7 @@ bool Scope::MustAllocate(Variable* var) {
if (!var->raw_name()->IsEmpty() &&
(inner_scope_calls_eval_ || is_catch_scope() || is_script_scope())) {
var->set_is_used();
- if (inner_scope_calls_eval_) var->SetMaybeAssigned();
+ if (inner_scope_calls_eval_ && !var->is_this()) var->SetMaybeAssigned();
}
DCHECK(!var->has_forced_context_allocation() || var->is_used());
// Global variables do not need to be allocated.
@@ -2081,11 +2098,14 @@ bool Scope::MustAllocateInContext(Variable* var) {
//
// Temporary variables are always stack-allocated. Catch-bound variables are
// always context-allocated.
- if (var->mode() == VariableMode::kTemporary) return false;
+ VariableMode mode = var->mode();
+ if (mode == VariableMode::kTemporary) return false;
if (is_catch_scope()) return true;
- if ((is_script_scope() || is_eval_scope()) &&
- IsLexicalVariableMode(var->mode())) {
- return true;
+ if (is_script_scope() || is_eval_scope()) {
+ if (IsLexicalVariableMode(mode) ||
+ IsPrivateMethodOrAccessorVariableMode(mode)) {
+ return true;
+ }
}
return var->has_forced_context_allocation() || inner_scope_calls_eval_;
}
@@ -2248,9 +2268,9 @@ void Scope::AllocateVariablesRecursively() {
scope->is_with_scope() || scope->is_module_scope() ||
scope->IsAsmModule() || scope->ForceContextForLanguageMode() ||
(scope->is_function_scope() &&
- scope->AsDeclarationScope()->calls_sloppy_eval()) ||
+ scope->AsDeclarationScope()->sloppy_eval_can_extend_vars()) ||
(scope->is_block_scope() && scope->is_declaration_scope() &&
- scope->AsDeclarationScope()->calls_sloppy_eval());
+ scope->AsDeclarationScope()->sloppy_eval_can_extend_vars());
// If we didn't allocate any locals in the local context, then we only
// need the minimal number of slots if we must have a context.
@@ -2326,15 +2346,28 @@ int Scope::ContextLocalCount() const {
(is_function_var_in_context ? 1 : 0);
}
-Variable* ClassScope::DeclarePrivateName(
- const AstRawString* name, RequiresBrandCheckFlag requires_brand_check,
- bool* was_added) {
+bool IsComplementaryAccessorPair(VariableMode a, VariableMode b) {
+ switch (a) {
+ case VariableMode::kPrivateGetterOnly:
+ return b == VariableMode::kPrivateSetterOnly;
+ case VariableMode::kPrivateSetterOnly:
+ return b == VariableMode::kPrivateGetterOnly;
+ default:
+ return false;
+ }
+}
+
+Variable* ClassScope::DeclarePrivateName(const AstRawString* name,
+ VariableMode mode, bool* was_added) {
Variable* result = EnsureRareData()->private_name_map.Declare(
- zone(), this, name, VariableMode::kConst, NORMAL_VARIABLE,
+ zone(), this, name, mode, NORMAL_VARIABLE,
InitializationFlag::kNeedsInitialization,
- MaybeAssignedFlag::kMaybeAssigned, requires_brand_check, was_added);
+ MaybeAssignedFlag::kMaybeAssigned, was_added);
if (*was_added) {
locals_.Add(result);
+ } else if (IsComplementaryAccessorPair(result->mode(), mode)) {
+ *was_added = true;
+ result->set_mode(VariableMode::kPrivateGetterAndSetter);
}
result->ForceContextAllocation();
return result;
@@ -2416,22 +2449,20 @@ Variable* ClassScope::LookupPrivateNameInScopeInfo(const AstRawString* name) {
VariableMode mode;
InitializationFlag init_flag;
MaybeAssignedFlag maybe_assigned_flag;
- RequiresBrandCheckFlag requires_brand_check;
- int index =
- ScopeInfo::ContextSlotIndex(*scope_info_, name_handle, &mode, &init_flag,
- &maybe_assigned_flag, &requires_brand_check);
+ int index = ScopeInfo::ContextSlotIndex(*scope_info_, name_handle, &mode,
+ &init_flag, &maybe_assigned_flag);
if (index < 0) {
return nullptr;
}
- DCHECK_EQ(mode, VariableMode::kConst);
+ DCHECK(IsConstVariableMode(mode));
DCHECK_EQ(init_flag, InitializationFlag::kNeedsInitialization);
DCHECK_EQ(maybe_assigned_flag, MaybeAssignedFlag::kMaybeAssigned);
// Add the found private name to the map to speed up subsequent
// lookups for the same name.
bool was_added;
- Variable* var = DeclarePrivateName(name, requires_brand_check, &was_added);
+ Variable* var = DeclarePrivateName(name, mode, &was_added);
DCHECK(was_added);
var->AllocateTo(VariableLocation::CONTEXT, index);
return var;
@@ -2450,7 +2481,9 @@ Variable* ClassScope::LookupPrivateName(VariableProxy* proxy) {
if (var == nullptr && !class_scope->scope_info_.is_null()) {
var = class_scope->LookupPrivateNameInScopeInfo(proxy->raw_name());
}
- return var;
+ if (var != nullptr) {
+ return var;
+ }
}
return nullptr;
}
diff --git a/deps/v8/src/ast/scopes.h b/deps/v8/src/ast/scopes.h
index 932d5c70b9..73e6e8fd89 100644
--- a/deps/v8/src/ast/scopes.h
+++ b/deps/v8/src/ast/scopes.h
@@ -5,6 +5,7 @@
#ifndef V8_AST_SCOPES_H_
#define V8_AST_SCOPES_H_
+#include <numeric>
#include "src/ast/ast.h"
#include "src/base/compiler-specific.h"
#include "src/base/hashmap.h"
@@ -13,6 +14,7 @@
#include "src/objects/function-kind.h"
#include "src/objects/objects.h"
#include "src/utils/pointer-with-payload.h"
+#include "src/utils/utils.h"
#include "src/zone/zone.h"
namespace v8 {
@@ -42,7 +44,6 @@ class VariableMap : public ZoneHashMap {
VariableMode mode, VariableKind kind,
InitializationFlag initialization_flag,
MaybeAssignedFlag maybe_assigned_flag,
- RequiresBrandCheckFlag requires_brand_check,
bool* was_added);
V8_EXPORT_PRIVATE Variable* Lookup(const AstRawString* name);
@@ -111,8 +112,10 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
}
void RestoreEvalFlag() {
- outer_scope_and_calls_eval_->scope_calls_eval_ =
- outer_scope_and_calls_eval_.GetPayload();
+ if (outer_scope_and_calls_eval_.GetPayload()) {
+ // This recreates both calls_eval and sloppy_eval_can_extend_vars.
+ outer_scope_and_calls_eval_.GetPointer()->RecordEvalCall();
+ }
}
void Reparent(DeclarationScope* new_parent);
@@ -265,9 +268,7 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
// Inform the scope and outer scopes that the corresponding code contains an
// eval call.
- void RecordEvalCall() {
- scope_calls_eval_ = true;
- }
+ inline void RecordEvalCall();
void RecordInnerScopeEvalCall() {
inner_scope_calls_eval_ = true;
@@ -460,7 +461,7 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
int ContextChainLength(Scope* scope) const;
// The number of contexts between this and the outermost context that has a
- // sloppy eval call. One if this->calls_sloppy_eval().
+ // sloppy eval call. One if this->sloppy_eval_can_extend_vars().
int ContextChainLengthUntilOutermostSloppyEval() const;
// Find the closest class scope in the current scope and outer scopes. If no
@@ -558,7 +559,7 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
MaybeAssignedFlag maybe_assigned_flag, bool* was_added) {
Variable* result =
variables_.Declare(zone, this, name, mode, kind, initialization_flag,
- maybe_assigned_flag, kNoBrandCheck, was_added);
+ maybe_assigned_flag, was_added);
if (*was_added) locals_.Add(result);
return result;
}
@@ -610,7 +611,8 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
// list along the way, so full resolution cannot be done afterwards.
void AnalyzePartially(DeclarationScope* max_outer_scope,
AstNodeFactory* ast_node_factory,
- UnresolvedList* new_unresolved_list);
+ UnresolvedList* new_unresolved_list,
+ bool maybe_in_arrowhead);
void CollectNonLocals(DeclarationScope* max_outer_scope, Isolate* isolate,
ParseInfo* info, Handle<StringSet>* non_locals);
@@ -703,9 +705,11 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
// The language mode of this scope.
STATIC_ASSERT(LanguageModeSize == 2);
bool is_strict_ : 1;
- // This scope or a nested catch scope or with scope contain an 'eval' call. At
- // the 'eval' call site this scope is the declaration scope.
- bool scope_calls_eval_ : 1;
+ // This scope contains an 'eval' call.
+ bool calls_eval_ : 1;
+ // The context associated with this scope can be extended by a sloppy eval
+ // called inside of it.
+ bool sloppy_eval_can_extend_vars_ : 1;
// This scope's declarations might not be executed in order (e.g., switch).
bool scope_nonlinear_ : 1;
bool is_hidden_ : 1;
@@ -753,11 +757,50 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
IsClassConstructor(function_kind())));
}
- bool calls_sloppy_eval() const {
- // TODO(delphick): Calculate this when setting and change the name of
- // scope_calls_eval_.
- return !is_script_scope() && scope_calls_eval_ &&
- is_sloppy(language_mode());
+ // Inform the scope and outer scopes that the corresponding code contains an
+ // eval call.
+ void RecordDeclarationScopeEvalCall() {
+ calls_eval_ = true;
+
+ // If this isn't a sloppy eval, we don't care about it.
+ if (language_mode() != LanguageMode::kSloppy) return;
+
+ // Sloppy eval in script scopes can only introduce global variables anyway,
+ // so we don't care that it calls sloppy eval.
+ if (is_script_scope()) return;
+
+ // Sloppy eval in a eval scope can only introduce variables into the outer
+ // (non-eval) declaration scope, not into this eval scope.
+ if (is_eval_scope()) {
+#ifdef DEBUG
+ // One of three things must be true:
+ // 1. The outer non-eval declaration scope should already be marked as
+ // being extendable by sloppy eval, by the current sloppy eval rather
+ // than the inner one,
+ // 2. The outer non-eval declaration scope is a script scope and thus
+ // isn't extendable anyway, or
+ // 3. This is a debug evaluate and all bets are off.
+ DeclarationScope* outer_decl_scope = outer_scope()->GetDeclarationScope();
+ while (outer_decl_scope->is_eval_scope()) {
+ outer_decl_scope = outer_decl_scope->GetDeclarationScope();
+ }
+ if (outer_decl_scope->is_debug_evaluate_scope()) {
+ // Don't check anything.
+ // TODO(9662): Figure out where variables declared by an eval inside a
+ // debug-evaluate actually go.
+ } else if (!outer_decl_scope->is_script_scope()) {
+ DCHECK(outer_decl_scope->sloppy_eval_can_extend_vars_);
+ }
+#endif
+
+ return;
+ }
+
+ sloppy_eval_can_extend_vars_ = true;
+ }
+
+ bool sloppy_eval_can_extend_vars() const {
+ return sloppy_eval_can_extend_vars_;
}
bool was_lazily_parsed() const { return was_lazily_parsed_; }
@@ -972,7 +1015,8 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
// this records variables which cannot be resolved inside the Scope (we don't
// yet know what they will resolve to since the outer Scopes are incomplete)
// and recreates them with the correct Zone with ast_node_factory.
- void AnalyzePartially(Parser* parser, AstNodeFactory* ast_node_factory);
+ void AnalyzePartially(Parser* parser, AstNodeFactory* ast_node_factory,
+ bool maybe_in_arrowhead);
// Allocate ScopeInfos for top scope and any inner scopes that need them.
// Does nothing if ScopeInfo is already allocated.
@@ -1138,13 +1182,21 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
RareData* rare_data_ = nullptr;
};
+void Scope::RecordEvalCall() {
+ calls_eval_ = true;
+ GetDeclarationScope()->RecordDeclarationScopeEvalCall();
+ RecordInnerScopeEvalCall();
+}
+
Scope::Snapshot::Snapshot(Scope* scope)
- : outer_scope_and_calls_eval_(scope, scope->scope_calls_eval_),
+ : outer_scope_and_calls_eval_(scope, scope->calls_eval_),
top_inner_scope_(scope->inner_scope_),
top_unresolved_(scope->unresolved_list_.end()),
top_local_(scope->GetClosureScope()->locals_.end()) {
// Reset in order to record eval calls during this Snapshot's lifetime.
- outer_scope_and_calls_eval_.GetPointer()->scope_calls_eval_ = false;
+ outer_scope_and_calls_eval_.GetPointer()->calls_eval_ = false;
+ outer_scope_and_calls_eval_.GetPointer()->sloppy_eval_can_extend_vars_ =
+ false;
}
class ModuleScope final : public DeclarationScope {
@@ -1175,8 +1227,7 @@ class V8_EXPORT_PRIVATE ClassScope : public Scope {
// Declare a private name in the private name map and add it to the
// local variables of this scope.
- Variable* DeclarePrivateName(const AstRawString* name,
- RequiresBrandCheckFlag requires_brand_check,
+ Variable* DeclarePrivateName(const AstRawString* name, VariableMode mode,
bool* was_added);
void AddUnresolvedPrivateName(VariableProxy* proxy);
diff --git a/deps/v8/src/ast/source-range-ast-visitor.cc b/deps/v8/src/ast/source-range-ast-visitor.cc
index 2fcf151999..d171e30587 100644
--- a/deps/v8/src/ast/source-range-ast-visitor.cc
+++ b/deps/v8/src/ast/source-range-ast-visitor.cc
@@ -25,14 +25,6 @@ void SourceRangeAstVisitor::VisitBlock(Block* stmt) {
}
}
-void SourceRangeAstVisitor::VisitSwitchStatement(SwitchStatement* stmt) {
- AstTraversalVisitor::VisitSwitchStatement(stmt);
- ZonePtrList<CaseClause>* clauses = stmt->cases();
- for (CaseClause* clause : *clauses) {
- MaybeRemoveLastContinuationRange(clause->statements());
- }
-}
-
void SourceRangeAstVisitor::VisitFunctionLiteral(FunctionLiteral* expr) {
AstTraversalVisitor::VisitFunctionLiteral(expr);
ZonePtrList<Statement>* stmts = expr->body();
diff --git a/deps/v8/src/ast/source-range-ast-visitor.h b/deps/v8/src/ast/source-range-ast-visitor.h
index 4ba5feb2d2..4ea36a947f 100644
--- a/deps/v8/src/ast/source-range-ast-visitor.h
+++ b/deps/v8/src/ast/source-range-ast-visitor.h
@@ -34,7 +34,6 @@ class SourceRangeAstVisitor final
friend class AstTraversalVisitor<SourceRangeAstVisitor>;
void VisitBlock(Block* stmt);
- void VisitSwitchStatement(SwitchStatement* stmt);
void VisitFunctionLiteral(FunctionLiteral* expr);
bool VisitNode(AstNode* node);
diff --git a/deps/v8/src/ast/variables.h b/deps/v8/src/ast/variables.h
index 7805fa20c8..1ff6f9f422 100644
--- a/deps/v8/src/ast/variables.h
+++ b/deps/v8/src/ast/variables.h
@@ -21,8 +21,7 @@ class Variable final : public ZoneObject {
public:
Variable(Scope* scope, const AstRawString* name, VariableMode mode,
VariableKind kind, InitializationFlag initialization_flag,
- MaybeAssignedFlag maybe_assigned_flag = kNotAssigned,
- RequiresBrandCheckFlag requires_brand_check = kNoBrandCheck)
+ MaybeAssignedFlag maybe_assigned_flag = kNotAssigned)
: scope_(scope),
name_(name),
local_if_not_shadowed_(nullptr),
@@ -32,7 +31,6 @@ class Variable final : public ZoneObject {
bit_field_(MaybeAssignedFlagField::encode(maybe_assigned_flag) |
InitializationFlagField::encode(initialization_flag) |
VariableModeField::encode(mode) |
- RequiresBrandCheckField::encode(requires_brand_check) |
IsUsedField::encode(false) |
ForceContextAllocationField::encode(false) |
ForceHoleInitializationField::encode(false) |
@@ -58,6 +56,9 @@ class Variable final : public ZoneObject {
Handle<String> name() const { return name_->string(); }
const AstRawString* raw_name() const { return name_; }
VariableMode mode() const { return VariableModeField::decode(bit_field_); }
+ void set_mode(VariableMode mode) {
+ bit_field_ = VariableModeField::update(bit_field_, mode);
+ }
bool has_forced_context_allocation() const {
return ForceContextAllocationField::decode(bit_field_);
}
@@ -72,6 +73,8 @@ class Variable final : public ZoneObject {
return MaybeAssignedFlagField::decode(bit_field_);
}
void SetMaybeAssigned() {
+ if (mode() == VariableMode::kConst) return;
+
// If this variable is dynamically shadowing another variable, then that
// variable could also be assigned (in the non-shadowing case).
if (has_local_if_not_shadowed()) {
@@ -80,22 +83,14 @@ class Variable final : public ZoneObject {
if (!maybe_assigned()) {
local_if_not_shadowed()->SetMaybeAssigned();
}
- DCHECK(local_if_not_shadowed()->maybe_assigned());
+ DCHECK_IMPLIES(local_if_not_shadowed()->mode() != VariableMode::kConst,
+ local_if_not_shadowed()->maybe_assigned());
}
set_maybe_assigned();
}
- RequiresBrandCheckFlag get_requires_brand_check_flag() const {
- return RequiresBrandCheckField::decode(bit_field_);
- }
-
bool requires_brand_check() const {
- return get_requires_brand_check_flag() == kRequiresBrandCheck;
- }
-
- void set_requires_brand_check() {
- bit_field_ =
- RequiresBrandCheckField::update(bit_field_, kRequiresBrandCheck);
+ return IsPrivateMethodOrAccessorVariableMode(mode());
}
int initializer_position() { return initializer_position_; }
@@ -125,7 +120,8 @@ class Variable final : public ZoneObject {
// declaration time. Only returns valid results after scope analysis.
bool binding_needs_init() const {
DCHECK_IMPLIES(initialization_flag() == kNeedsInitialization,
- IsLexicalVariableMode(mode()));
+ IsLexicalVariableMode(mode()) ||
+ IsPrivateMethodOrAccessorVariableMode(mode()));
DCHECK_IMPLIES(ForceHoleInitializationField::decode(bit_field_),
initialization_flag() == kNeedsInitialization);
@@ -149,7 +145,8 @@ class Variable final : public ZoneObject {
// be required at runtime.
void ForceHoleInitialization() {
DCHECK_EQ(kNeedsInitialization, initialization_flag());
- DCHECK(IsLexicalVariableMode(mode()));
+ DCHECK(IsLexicalVariableMode(mode()) ||
+ IsPrivateMethodOrAccessorVariableMode(mode()));
bit_field_ = ForceHoleInitializationField::update(bit_field_, true);
}
@@ -243,25 +240,16 @@ class Variable final : public ZoneObject {
bit_field_ = MaybeAssignedFlagField::update(bit_field_, kMaybeAssigned);
}
- class VariableModeField : public BitField16<VariableMode, 0, 3> {};
- class VariableKindField
- : public BitField16<VariableKind, VariableModeField::kNext, 3> {};
- class LocationField
- : public BitField16<VariableLocation, VariableKindField::kNext, 3> {};
- class ForceContextAllocationField
- : public BitField16<bool, LocationField::kNext, 1> {};
- class IsUsedField
- : public BitField16<bool, ForceContextAllocationField::kNext, 1> {};
- class InitializationFlagField
- : public BitField16<InitializationFlag, IsUsedField::kNext, 1> {};
- class ForceHoleInitializationField
- : public BitField16<bool, InitializationFlagField::kNext, 1> {};
- class MaybeAssignedFlagField
- : public BitField16<MaybeAssignedFlag,
- ForceHoleInitializationField::kNext, 1> {};
- class RequiresBrandCheckField
- : public BitField16<RequiresBrandCheckFlag, MaybeAssignedFlagField::kNext,
- 1> {};
+ using VariableModeField = BitField16<VariableMode, 0, 4>;
+ using VariableKindField = VariableModeField::Next<VariableKind, 3>;
+ using LocationField = VariableKindField::Next<VariableLocation, 3>;
+ using ForceContextAllocationField = LocationField::Next<bool, 1>;
+ using IsUsedField = ForceContextAllocationField::Next<bool, 1>;
+ using InitializationFlagField = IsUsedField::Next<InitializationFlag, 1>;
+ using ForceHoleInitializationField = InitializationFlagField::Next<bool, 1>;
+ using MaybeAssignedFlagField =
+ ForceHoleInitializationField::Next<MaybeAssignedFlag, 1>;
+
Variable** next() { return &next_; }
friend List;
friend base::ThreadedListTraits<Variable>;