5e7b7dbcda
Change-Id: Ic6049f8262119f8d77c926ce4606c50b8323a327 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/210380 Commit-Queue: Johnni Winther <johnniwinther@google.com> Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
1482 lines
61 KiB
Dart
1482 lines
61 KiB
Dart
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
import '../ast.dart';
|
|
import 'coverage.dart';
|
|
|
|
/// Helper class used to generate ASTs that contain all different nodes.
|
|
class NodeCreator {
|
|
final Uri _uri;
|
|
|
|
/// File offset counter.
|
|
///
|
|
/// Used to generate distinct file offsets through [_needFileOffset].
|
|
int _fileOffset = 0;
|
|
|
|
/// The parent [Component] for all created libraries, classes, extensions,
|
|
/// typedefs and members.
|
|
final Component _component = new Component();
|
|
|
|
/// These fields contain maps of requested nodes different kinds that are
|
|
/// still pending. The mapped values are used to track how many nodes of the
|
|
/// specific kind have been created. When all variants of a kind have been
|
|
/// created, the entry is removed from the map.
|
|
final Map<ExpressionKind, int> _pendingExpressions;
|
|
final Map<StatementKind, int> _pendingStatements;
|
|
final Map<DartTypeKind, int> _pendingDartTypes;
|
|
final Map<ConstantKind, int> _pendingConstants;
|
|
final Map<InitializerKind, int> _pendingInitializers;
|
|
final Map<MemberKind, int> _pendingMembers;
|
|
final Map<NodeKind, int> _pendingNodes;
|
|
|
|
/// The set of all kinds of nodes created by this node creator.
|
|
final Set<Object> _createdKinds = {};
|
|
|
|
/// These fields contain list of nodes needed for the creation of other nodes.
|
|
///
|
|
/// Needed nodes are nodes that need to exist prior to the node that required
|
|
/// it. For instance, to create an [InterfaceType] node, a [Class] node must
|
|
/// exist for the [InterfaceType] to reference.
|
|
///
|
|
/// Needed nodes are added to the context of the created nodes. For instance,
|
|
/// a needed [Class] is added to the [_component] and a needed
|
|
/// [VariableDeclaration] is added to a enclosing [Block].
|
|
List<Library> _neededLibraries = [];
|
|
List<Class> _neededClasses = [];
|
|
List<Extension> _neededExtensions = [];
|
|
List<Typedef> _neededTypedefs = [];
|
|
List<TypeParameter> _neededTypeParameters = [];
|
|
List<Constructor> _neededConstructors = [];
|
|
List<Procedure> _neededRedirectingFactories = [];
|
|
List<Procedure> _neededProcedures = [];
|
|
List<Field> _neededFields = [];
|
|
List<LibraryDependency> _neededLibraryDependencies = [];
|
|
List<VariableDeclaration> _neededVariableDeclarations = [];
|
|
List<LabeledStatement> _neededLabeledStatements = [];
|
|
List<FunctionDeclaration> _neededFunctionDeclarations = [];
|
|
List<SwitchCase> _neededSwitchCases = [];
|
|
|
|
/// Creates a [NodeCreator] requested to create nodes of the specified kinds.
|
|
NodeCreator({
|
|
Iterable<ExpressionKind> expressions: ExpressionKind.values,
|
|
Iterable<StatementKind> statements: StatementKind.values,
|
|
Iterable<DartTypeKind> dartTypes: DartTypeKind.values,
|
|
Iterable<ConstantKind> constants: ConstantKind.values,
|
|
Iterable<InitializerKind> initializers: InitializerKind.values,
|
|
Iterable<MemberKind> members: MemberKind.values,
|
|
Iterable<NodeKind> nodes: NodeKind.values,
|
|
}) : _pendingExpressions = new Map<ExpressionKind, int>.fromIterables(
|
|
expressions, new List<int>.filled(expressions.length, 0)),
|
|
_pendingStatements = new Map<StatementKind, int>.fromIterables(
|
|
statements, new List<int>.filled(statements.length, 0)),
|
|
_pendingDartTypes = new Map<DartTypeKind, int>.fromIterables(
|
|
dartTypes, new List<int>.filled(dartTypes.length, 0)),
|
|
_pendingConstants = new Map<ConstantKind, int>.fromIterables(
|
|
constants, new List<int>.filled(constants.length, 0)),
|
|
_pendingInitializers = new Map<InitializerKind, int>.fromIterables(
|
|
initializers, new List<int>.filled(initializers.length, 0)),
|
|
_pendingMembers = new Map<MemberKind, int>.fromIterables(
|
|
members, new List<int>.filled(members.length, 0)),
|
|
_pendingNodes = new Map<NodeKind, int>.fromIterables(
|
|
nodes, new List<int>.filled(nodes.length, 0)),
|
|
_uri = Uri.parse('test:uri') {
|
|
_createdKinds.addAll(_pendingExpressions.keys);
|
|
_createdKinds.addAll(_pendingStatements.keys);
|
|
_createdKinds.addAll(_pendingDartTypes.keys);
|
|
_createdKinds.addAll(_pendingInitializers.keys);
|
|
_createdKinds.addAll(_pendingMembers.keys);
|
|
_createdKinds.addAll(_pendingNodes.keys);
|
|
}
|
|
|
|
/// The kinds created by this node creator.
|
|
Iterable<Object> get createdKinds => _createdKinds;
|
|
|
|
/// Wraps [statement] in nodes needed in the statement context.
|
|
///
|
|
/// For instance, if a [LabeledStatement] was needed for the creation of
|
|
/// [statement], [statement] is wrapped inside the labeled statement.
|
|
Statement _ensureContext(Statement statement) {
|
|
if (_neededSwitchCases.isNotEmpty) {
|
|
statement = SwitchStatement(NullLiteral(), [
|
|
..._neededSwitchCases,
|
|
SwitchCase([NullLiteral()], [TreeNode.noOffset], Block([statement]))
|
|
]);
|
|
}
|
|
_neededSwitchCases.clear();
|
|
for (LabeledStatement labeledStatement in _neededLabeledStatements) {
|
|
labeledStatement.body = statement;
|
|
statement = labeledStatement;
|
|
}
|
|
_neededLabeledStatements.clear();
|
|
statement = Block([
|
|
..._neededVariableDeclarations,
|
|
..._neededFunctionDeclarations,
|
|
statement
|
|
]);
|
|
_neededFunctionDeclarations.clear();
|
|
_neededVariableDeclarations.clear();
|
|
return statement;
|
|
}
|
|
|
|
/// Adds [statement] to [statements] including any nodes needed in the
|
|
/// context.
|
|
void _addStatement(List<Statement> statements, Statement statement) {
|
|
statements.add(_ensureContext(statement));
|
|
}
|
|
|
|
/// Adds [expression] to [statements] including any nodes needed in the
|
|
/// context.
|
|
void _addExpression(List<Statement> statements, Expression expression) {
|
|
_addStatement(statements, ExpressionStatement(expression));
|
|
}
|
|
|
|
/// Adds [type] to [statements] including any nodes needed in the context.
|
|
void _addDartType(List<Statement> statements, DartType type) {
|
|
_addExpression(statements, TypeLiteral(type));
|
|
}
|
|
|
|
/// Adds [constant] to [statements] including any nodes needed in the context.
|
|
void _addConstant(List<Statement> statements, Constant constant) {
|
|
_addExpression(statements, ConstantExpression(constant));
|
|
}
|
|
|
|
/// Generates a list of [Statement] containing all pending in-body nodes.
|
|
List<Statement> _generateBodies() {
|
|
List<Statement> statements = [];
|
|
while (_pendingStatements.isNotEmpty) {
|
|
_addStatement(statements, _createStatement());
|
|
}
|
|
while (_pendingExpressions.isNotEmpty) {
|
|
_addExpression(statements, _createExpression());
|
|
}
|
|
while (_pendingDartTypes.isNotEmpty) {
|
|
_addDartType(statements, _createDartType());
|
|
}
|
|
while (_pendingConstants.isNotEmpty) {
|
|
_addConstant(statements, _createConstant());
|
|
}
|
|
for (NodeKind kind in inBodyNodeKinds) {
|
|
while (_pendingNodes.containsKey(kind)) {
|
|
Node node = _createNodeFromKind(kind);
|
|
switch (kind) {
|
|
case NodeKind.Name:
|
|
_addExpression(
|
|
statements,
|
|
DynamicGet(DynamicAccessKind.Dynamic, _createExpression(),
|
|
node as Name));
|
|
break;
|
|
case NodeKind.Arguments:
|
|
_addExpression(
|
|
statements,
|
|
DynamicInvocation(DynamicAccessKind.Dynamic,
|
|
_createExpression(), _createName(), node as Arguments));
|
|
break;
|
|
case NodeKind.Catch:
|
|
_addStatement(
|
|
statements, TryCatch(_createStatement(), [node as Catch]));
|
|
break;
|
|
case NodeKind.FunctionNode:
|
|
_addExpression(
|
|
statements, FunctionExpression(node as FunctionNode));
|
|
break;
|
|
case NodeKind.MapLiteralEntry:
|
|
_addExpression(statements, MapLiteral([node as MapLiteralEntry]));
|
|
break;
|
|
case NodeKind.NamedExpression:
|
|
_addExpression(
|
|
statements,
|
|
DynamicInvocation(
|
|
DynamicAccessKind.Dynamic,
|
|
_createExpression(),
|
|
_createName(),
|
|
Arguments([], named: [node as NamedExpression])));
|
|
break;
|
|
case NodeKind.NamedType:
|
|
_addDartType(
|
|
statements,
|
|
FunctionType([], _createDartType(), Nullability.nonNullable,
|
|
namedParameters: [node as NamedType]));
|
|
break;
|
|
case NodeKind.SwitchCase:
|
|
_addStatement(statements,
|
|
SwitchStatement(_createExpression(), [node as SwitchCase]));
|
|
break;
|
|
case NodeKind.TypeParameter:
|
|
_addExpression(
|
|
statements,
|
|
FunctionExpression(FunctionNode(Block([]),
|
|
typeParameters: [node as TypeParameter])));
|
|
break;
|
|
default:
|
|
throw new UnimplementedError('Unhandled in body node $kind.');
|
|
}
|
|
}
|
|
}
|
|
return statements;
|
|
}
|
|
|
|
/// Generates [Statement]s containing occurrences of all requested nodes.
|
|
List<Statement> generateBodies() {
|
|
List<Statement> statements = _generateBodies();
|
|
Set<Object> unsupportedKinds = {};
|
|
if (_pendingInitializers.isNotEmpty) {
|
|
unsupportedKinds.addAll(_pendingInitializers.keys);
|
|
}
|
|
if (_pendingMembers.isNotEmpty) {
|
|
unsupportedKinds.addAll(_pendingMembers.keys);
|
|
}
|
|
if (_pendingNodes.isNotEmpty) {
|
|
assert(
|
|
_pendingNodes.keys.every((kind) => !inBodyNodeKinds.contains(kind)));
|
|
unsupportedKinds.addAll(_pendingNodes.keys);
|
|
}
|
|
if (unsupportedKinds.isNotEmpty) {
|
|
throw new UnsupportedError('Cannot create these node in a body context: '
|
|
'${unsupportedKinds.join(', ')}');
|
|
}
|
|
return statements;
|
|
}
|
|
|
|
/// Generates a [Component] containing occurrences of all requested and needed
|
|
/// nodes.
|
|
Component generateComponent() {
|
|
Class cls = _needClass();
|
|
for (Statement statement in _generateBodies()) {
|
|
cls.addProcedure(Procedure(
|
|
_createName(), ProcedureKind.Method, FunctionNode(statement),
|
|
fileUri: _uri));
|
|
}
|
|
while (_pendingInitializers.isNotEmpty) {
|
|
Initializer initializer = _createInitializer();
|
|
cls.addConstructor(Constructor(FunctionNode(null),
|
|
name: _createName(), fileUri: _uri, initializers: [initializer]));
|
|
}
|
|
while (_pendingMembers.isNotEmpty) {
|
|
Member member = _createMember();
|
|
if (member is Procedure) {
|
|
cls.addProcedure(member);
|
|
} else if (member is Field) {
|
|
cls.addField(member);
|
|
} else if (member is Constructor) {
|
|
cls.addConstructor(member);
|
|
} else if (member is RedirectingFactory) {
|
|
cls.addRedirectingFactory(member);
|
|
} else {
|
|
throw new UnsupportedError(
|
|
'Unexpected member $member (${member.runtimeType})');
|
|
}
|
|
}
|
|
while (_pendingNodes.isNotEmpty) {
|
|
NodeKind kind = _pendingNodes.keys.first;
|
|
Node node = _createNodeFromKind(kind);
|
|
switch (kind) {
|
|
case NodeKind.Name:
|
|
case NodeKind.Arguments:
|
|
case NodeKind.Catch:
|
|
case NodeKind.FunctionNode:
|
|
case NodeKind.MapLiteralEntry:
|
|
case NodeKind.NamedExpression:
|
|
case NodeKind.NamedType:
|
|
case NodeKind.SwitchCase:
|
|
case NodeKind.TypeParameter:
|
|
throw new UnimplementedError('Expected in body node $kind.');
|
|
case NodeKind.Class:
|
|
_needLibrary().addClass(node as Class);
|
|
break;
|
|
case NodeKind.Combinator:
|
|
_needLibrary().addDependency(LibraryDependency.import(_needLibrary(),
|
|
combinators: [node as Combinator]));
|
|
break;
|
|
case NodeKind.Component:
|
|
assert(identical(node, _component),
|
|
"Cannot create multiple Component nodes.");
|
|
break;
|
|
case NodeKind.Extension:
|
|
_needLibrary().addExtension(node as Extension);
|
|
break;
|
|
case NodeKind.Library:
|
|
_component.libraries.add(node as Library);
|
|
break;
|
|
case NodeKind.LibraryDependency:
|
|
_needLibrary().addDependency(node as LibraryDependency);
|
|
break;
|
|
case NodeKind.LibraryPart:
|
|
_needLibrary().addPart(node as LibraryPart);
|
|
break;
|
|
case NodeKind.Supertype:
|
|
_needLibrary().addClass(
|
|
Class(name: 'foo', fileUri: _uri, supertype: node as Supertype));
|
|
break;
|
|
case NodeKind.Typedef:
|
|
_needLibrary().addTypedef(node as Typedef);
|
|
break;
|
|
}
|
|
}
|
|
return _component;
|
|
}
|
|
|
|
/// Returns a [Library] node that fits the requirements.
|
|
///
|
|
/// If no such [Library] exists in [_neededLibraries], a new [Library] is
|
|
/// created and added to [_neededLibraries].
|
|
// TODO(johnniwinther): Add requirements when/where needed.
|
|
Library _needLibrary() {
|
|
for (Library library in _neededLibraries) {
|
|
return library;
|
|
}
|
|
Library library = Library(_uri, fileUri: _uri);
|
|
_neededLibraries.add(library);
|
|
_component.libraries.add(library);
|
|
return library;
|
|
}
|
|
|
|
/// Returns a [LibraryDependency] node that fits the requirements.
|
|
///
|
|
/// If no such [LibraryDependency] exists in [_neededLibraryDependencies], a
|
|
/// new [LibraryDependency] is created and added to
|
|
/// [_neededLibraryDependencies].
|
|
LibraryDependency _needLibraryDependency({bool deferred: false}) {
|
|
for (LibraryDependency libraryDependency in _neededLibraryDependencies) {
|
|
if (!deferred || libraryDependency.isDeferred) {
|
|
return libraryDependency;
|
|
}
|
|
}
|
|
LibraryDependency libraryDependency = deferred
|
|
? LibraryDependency.deferredImport(_needLibrary(), 'foo')
|
|
: LibraryDependency.import(_needLibrary());
|
|
_neededLibraryDependencies.add(libraryDependency);
|
|
return libraryDependency;
|
|
}
|
|
|
|
/// Returns a [Class] node that fits the requirements.
|
|
///
|
|
/// If no such [Class] exists in [_neededClasses], a new [Class] is
|
|
/// created and added to [_neededClasses].
|
|
// TODO(johnniwinther): Add requirements when/where needed.
|
|
Class _needClass() {
|
|
for (Class cls in _neededClasses) {
|
|
return cls;
|
|
}
|
|
Class cls = Class(name: 'Foo', fileUri: _uri);
|
|
_neededClasses.add(cls);
|
|
_needLibrary().addClass(cls);
|
|
return cls;
|
|
}
|
|
|
|
/// Returns a [Extension] node that fits the requirements.
|
|
///
|
|
/// If no such [Extension] exists in [_neededExtensions], a new [Extension] is
|
|
/// created and added to [_neededExtensions].
|
|
// TODO(johnniwinther): Add requirements when/where needed.
|
|
Extension _needExtension() {
|
|
for (Extension extension in _neededExtensions) {
|
|
return extension;
|
|
}
|
|
Extension extension = Extension(name: 'foo', fileUri: _uri)
|
|
..onType = DynamicType();
|
|
_neededExtensions.add(extension);
|
|
_needLibrary().addExtension(extension);
|
|
return extension;
|
|
}
|
|
|
|
/// Returns a [Typedef] node that fits the requirements.
|
|
///
|
|
/// If no such [Typedef] exists in [_neededTypedefs], a new [Typedef] is
|
|
/// created and added to [_neededTypedefs].
|
|
// TODO(johnniwinther): Add requirements when/where needed.
|
|
Typedef _needTypedef() {
|
|
for (Typedef typedef in _neededTypedefs) {
|
|
return typedef;
|
|
}
|
|
Typedef typedef = Typedef('foo', DynamicType(), fileUri: _uri);
|
|
_neededTypedefs.add(typedef);
|
|
_needLibrary().addTypedef(typedef);
|
|
return typedef;
|
|
}
|
|
|
|
/// Returns a [TypeParameter] node that fits the requirements.
|
|
///
|
|
/// If no such [TypeParameter] exists in [_neededTypeParameters], a new
|
|
/// [TypeParameter] is created and added to [_neededTypeParameters].
|
|
// TODO(johnniwinther): Add requirements when/where needed.
|
|
TypeParameter _needTypeParameter() {
|
|
for (TypeParameter typeParameter in _neededTypeParameters) {
|
|
return typeParameter;
|
|
}
|
|
TypeParameter typeParameter =
|
|
TypeParameter('foo', DynamicType(), DynamicType());
|
|
_neededTypeParameters.add(typeParameter);
|
|
// TODO(johnniwinther): Add the type parameter to a context; class, method
|
|
// or function type.
|
|
return typeParameter;
|
|
}
|
|
|
|
/// Returns a [Procedure] node that fits the requirements.
|
|
///
|
|
/// If no such [Procedure] exists in [_neededProcedures], a new [Library] is
|
|
/// created and added to [_neededProcedures].
|
|
///
|
|
/// [index] is used to create multiple distinct [Procedure] nodes even when
|
|
/// these have the same requirements.
|
|
Procedure _needProcedure({int? index, bool? isStatic}) {
|
|
for (Procedure procedure in _neededProcedures) {
|
|
if (isStatic == null || isStatic == procedure.isStatic) {
|
|
if (index == null || index == 0) {
|
|
return procedure;
|
|
} else {
|
|
index--;
|
|
}
|
|
}
|
|
}
|
|
isStatic ??= true;
|
|
Procedure procedure = Procedure(
|
|
Name('foo'), ProcedureKind.Method, FunctionNode(Block([])),
|
|
fileUri: _uri, isStatic: isStatic);
|
|
_neededProcedures.add(procedure);
|
|
if (isStatic) {
|
|
_needLibrary().addProcedure(procedure);
|
|
} else {
|
|
_needClass().addProcedure(procedure);
|
|
}
|
|
return procedure;
|
|
}
|
|
|
|
/// Returns a [Constructor] node that fits the requirements.
|
|
///
|
|
/// If no such [Constructor] exists in [_neededConstructors], a new
|
|
/// [Constructor] is created and added to [_neededConstructors].
|
|
// TODO(johnniwinther): Add requirements when/where needed.
|
|
Constructor _needConstructor() {
|
|
for (Constructor constructor in _neededConstructors) {
|
|
return constructor;
|
|
}
|
|
Constructor constructor =
|
|
Constructor(FunctionNode(null), name: Name('foo'), fileUri: _uri);
|
|
_needClass().addConstructor(constructor);
|
|
return constructor;
|
|
}
|
|
|
|
/// Returns a redirecting factory [Procedure] node that fits the requirements.
|
|
///
|
|
/// If no such [Library] exists in [_neededRedirectingFactories], a new
|
|
/// [Procedure] is created and added to [_neededRedirectingFactories].
|
|
// TODO(johnniwinther): Add requirements when/where needed.
|
|
Procedure _needRedirectingFactory() {
|
|
for (Procedure redirectingFactory in _neededRedirectingFactories) {
|
|
return redirectingFactory;
|
|
}
|
|
Procedure redirectingFactory = Procedure(
|
|
Name('foo'), ProcedureKind.Method, FunctionNode(null),
|
|
fileUri: _uri)
|
|
..isRedirectingFactory = true;
|
|
_needClass().addProcedure(redirectingFactory);
|
|
return redirectingFactory;
|
|
}
|
|
|
|
/// Returns a [Field] node that fits the requirements.
|
|
///
|
|
/// If no such [Field] exists in [_neededFields], a new [Field] is
|
|
/// created and added to [_neededFields].
|
|
///
|
|
/// [index] is used to create multiple distinct [Field] nodes even when
|
|
/// these have the same requirements.
|
|
Field _needField({int? index, bool? isStatic, bool? hasSetter}) {
|
|
for (Field field in _neededFields) {
|
|
if (isStatic == null ||
|
|
isStatic == field.isStatic && hasSetter == null ||
|
|
hasSetter == field.hasSetter) {
|
|
if (index == null || index == 0) {
|
|
return field;
|
|
} else {
|
|
index--;
|
|
}
|
|
}
|
|
}
|
|
hasSetter ??= false;
|
|
isStatic ??= true;
|
|
Field field = hasSetter
|
|
? new Field.immutable(Name('foo'), fileUri: _uri, isStatic: isStatic)
|
|
: new Field.mutable(Name('foo'), fileUri: _uri, isStatic: isStatic);
|
|
_neededFields.add(field);
|
|
if (isStatic) {
|
|
_needLibrary().addField(field);
|
|
} else {
|
|
_needClass().addField(field);
|
|
}
|
|
return field;
|
|
}
|
|
|
|
/// Returns a [VariableDeclaration] node that fits the requirements.
|
|
///
|
|
/// If no such [VariableDeclaration] exists in [_neededVariableDeclarations],
|
|
/// a new [VariableDeclaration] is created and added to
|
|
/// [_neededVariableDeclarations].
|
|
// TODO(johnniwinther): Add requirements when/where needed.
|
|
VariableDeclaration _needVariableDeclaration() {
|
|
for (VariableDeclaration variableDeclaration
|
|
in _neededVariableDeclarations) {
|
|
return variableDeclaration;
|
|
}
|
|
VariableDeclaration variableDeclaration = VariableDeclaration('foo');
|
|
_neededVariableDeclarations.add(variableDeclaration);
|
|
return variableDeclaration;
|
|
}
|
|
|
|
/// Returns a [LabeledStatement] node that fits the requirements.
|
|
///
|
|
/// If no such [LabeledStatement] exists in [_neededLabeledStatements], a new
|
|
/// [LabeledStatement] is created and added to [_neededLabeledStatements].
|
|
// TODO(johnniwinther): Add requirements when/where needed.
|
|
LabeledStatement _needLabeledStatement() {
|
|
for (LabeledStatement labeledStatement in _neededLabeledStatements) {
|
|
return labeledStatement;
|
|
}
|
|
LabeledStatement labeledStatement = LabeledStatement(null);
|
|
_neededLabeledStatements.add(labeledStatement);
|
|
return labeledStatement;
|
|
}
|
|
|
|
/// Returns a [SwitchCase] node that fits the requirements.
|
|
///
|
|
/// If no such [SwitchCase] exists in [_neededSwitchCases], a new [SwitchCase]
|
|
/// is created and added to [_neededSwitchCases].
|
|
// TODO(johnniwinther): Add requirements when/where needed.
|
|
SwitchCase _needSwitchCase() {
|
|
for (SwitchCase switchCase in _neededSwitchCases) {
|
|
return switchCase;
|
|
}
|
|
SwitchCase switchCase =
|
|
SwitchCase([NullLiteral()], [TreeNode.noOffset], EmptyStatement());
|
|
_neededSwitchCases.add(switchCase);
|
|
return switchCase;
|
|
}
|
|
|
|
/// Returns a [FunctionDeclaration] node that fits the requirements.
|
|
///
|
|
/// If no such [FunctionDeclaration] exists in [_neededFunctionDeclarations],
|
|
/// a new [FunctionDeclaration] is created and added to
|
|
/// [_neededFunctionDeclarations].
|
|
// TODO(johnniwinther): Add requirements when/where needed.
|
|
FunctionDeclaration _needFunctionDeclaration() {
|
|
for (FunctionDeclaration functionDeclaration
|
|
in _neededFunctionDeclarations) {
|
|
return functionDeclaration;
|
|
}
|
|
FunctionDeclaration functionDeclaration = FunctionDeclaration(
|
|
VariableDeclaration('foo'), FunctionNode(Block([])));
|
|
_neededFunctionDeclarations.add(functionDeclaration);
|
|
return functionDeclaration;
|
|
}
|
|
|
|
/// Returns a fresh file offset value.
|
|
int _needFileOffset() => _fileOffset++;
|
|
|
|
/// Creates an [Expression] node.
|
|
///
|
|
/// If there are any pending expressions, one of these is created.
|
|
Expression _createExpression() {
|
|
if (_pendingExpressions.isEmpty) {
|
|
return NullLiteral()..fileOffset = _needFileOffset();
|
|
}
|
|
ExpressionKind kind = _pendingExpressions.keys.first;
|
|
return _createExpressionFromKind(kind);
|
|
}
|
|
|
|
/// Creates an [Expression] node of the specified [kind].
|
|
///
|
|
/// If there are any pending expressions of this [kind], one of these is
|
|
/// created.
|
|
Expression _createExpressionFromKind(ExpressionKind kind) {
|
|
int? index = _pendingExpressions.remove(kind);
|
|
switch (kind) {
|
|
case ExpressionKind.AsExpression:
|
|
return AsExpression(_createExpression(), _createDartType())
|
|
..fileOffset = _needFileOffset();
|
|
case ExpressionKind.AwaitExpression:
|
|
return AwaitExpression(_createExpression())
|
|
..fileOffset = _needFileOffset();
|
|
case ExpressionKind.BlockExpression:
|
|
return BlockExpression(
|
|
_createStatementFromKind(StatementKind.Block) as Block,
|
|
_createExpression())
|
|
..fileOffset = _needFileOffset();
|
|
case ExpressionKind.BoolLiteral:
|
|
return BoolLiteral(true)..fileOffset = _needFileOffset();
|
|
case ExpressionKind.CheckLibraryIsLoaded:
|
|
return CheckLibraryIsLoaded(_needLibraryDependency(deferred: true))
|
|
..fileOffset = _needFileOffset();
|
|
case ExpressionKind.ConditionalExpression:
|
|
return ConditionalExpression(_createExpression(), _createExpression(),
|
|
_createExpression(), _createDartType())
|
|
..fileOffset = _needFileOffset();
|
|
case ExpressionKind.ConstantExpression:
|
|
return ConstantExpression(_createConstant(), _createDartType())
|
|
..fileOffset = _needFileOffset();
|
|
case ExpressionKind.ConstructorInvocation:
|
|
return _createOneOf(_pendingExpressions, kind, index, [
|
|
() => ConstructorInvocation(_needConstructor(), _createArguments(),
|
|
isConst: false)
|
|
..fileOffset = _needFileOffset(),
|
|
() => ConstructorInvocation(_needConstructor(), _createArguments(),
|
|
isConst: true)
|
|
..fileOffset = _needFileOffset(),
|
|
]);
|
|
case ExpressionKind.ConstructorTearOff:
|
|
return ConstructorTearOff(_needConstructor())
|
|
..fileOffset = _needFileOffset();
|
|
case ExpressionKind.DoubleLiteral:
|
|
return DoubleLiteral(42.5)..fileOffset = _needFileOffset();
|
|
case ExpressionKind.DynamicGet:
|
|
return DynamicGet(
|
|
DynamicAccessKind.Dynamic, _createExpression(), _createName())
|
|
..fileOffset = _needFileOffset();
|
|
case ExpressionKind.DynamicInvocation:
|
|
return DynamicInvocation(DynamicAccessKind.Dynamic, _createExpression(),
|
|
_createName(), _createArguments())
|
|
..fileOffset = _needFileOffset();
|
|
case ExpressionKind.DynamicSet:
|
|
return DynamicSet(DynamicAccessKind.Dynamic, _createExpression(),
|
|
_createName(), _createExpression())
|
|
..fileOffset = _needFileOffset();
|
|
case ExpressionKind.EqualsCall:
|
|
return EqualsCall(_createExpression(), _createExpression(),
|
|
functionType: _createFunctionType(),
|
|
interfaceTarget: _needProcedure())
|
|
..fileOffset = _needFileOffset();
|
|
case ExpressionKind.EqualsNull:
|
|
return EqualsNull(_createExpression())..fileOffset = _needFileOffset();
|
|
case ExpressionKind.FileUriExpression:
|
|
return FileUriExpression(_createExpression(), _uri)
|
|
..fileOffset = _needFileOffset();
|
|
case ExpressionKind.FunctionExpression:
|
|
return FunctionExpression(_createFunctionNode())
|
|
..fileOffset = _needFileOffset();
|
|
case ExpressionKind.FunctionInvocation:
|
|
return FunctionInvocation(FunctionAccessKind.FunctionType,
|
|
_createExpression(), _createArguments(),
|
|
functionType: _createFunctionType())
|
|
..fileOffset = _needFileOffset();
|
|
case ExpressionKind.FunctionTearOff:
|
|
return FunctionTearOff(_createExpression())
|
|
..fileOffset = _needFileOffset();
|
|
case ExpressionKind.InstanceCreation:
|
|
return InstanceCreation(_needClass().reference, [], {}, [], [])
|
|
..fileOffset = _needFileOffset();
|
|
case ExpressionKind.InstanceGet:
|
|
return InstanceGet(
|
|
InstanceAccessKind.Instance, _createExpression(), _createName(),
|
|
interfaceTarget: _needField(), resultType: _createDartType())
|
|
..fileOffset = _needFileOffset();
|
|
case ExpressionKind.InstanceGetterInvocation:
|
|
return InstanceGetterInvocation(InstanceAccessKind.Instance,
|
|
_createExpression(), _createName(), _createArguments(),
|
|
interfaceTarget: _needField(), functionType: _createFunctionType())
|
|
..fileOffset = _needFileOffset();
|
|
case ExpressionKind.InstanceInvocation:
|
|
return InstanceInvocation(InstanceAccessKind.Instance,
|
|
_createExpression(), _createName(), _createArguments(),
|
|
interfaceTarget: _needProcedure(),
|
|
functionType: _createFunctionType())
|
|
..fileOffset = _needFileOffset()
|
|
..isBoundsSafe = true;
|
|
case ExpressionKind.InstanceSet:
|
|
return InstanceSet(InstanceAccessKind.Instance, _createExpression(),
|
|
_createName(), _createExpression(),
|
|
interfaceTarget: _needField())
|
|
..fileOffset = _needFileOffset();
|
|
case ExpressionKind.InstanceTearOff:
|
|
return InstanceTearOff(
|
|
InstanceAccessKind.Instance, _createExpression(), _createName(),
|
|
interfaceTarget: _needProcedure(), resultType: _createDartType())
|
|
..fileOffset = _needFileOffset();
|
|
case ExpressionKind.Instantiation:
|
|
return _createOneOf(_pendingExpressions, kind, index, [
|
|
() => Instantiation(_createExpression(), [])
|
|
..fileOffset = _needFileOffset(),
|
|
() => Instantiation(_createExpression(), [_createDartType()])
|
|
..fileOffset = _needFileOffset(),
|
|
() => Instantiation(
|
|
_createExpression(), [_createDartType(), _createDartType()])
|
|
..fileOffset = _needFileOffset(),
|
|
]);
|
|
case ExpressionKind.IntLiteral:
|
|
return IntLiteral(42)..fileOffset = _needFileOffset();
|
|
case ExpressionKind.InvalidExpression:
|
|
return _createOneOf(_pendingExpressions, kind, index, [
|
|
() => InvalidExpression(null)..fileOffset = _needFileOffset(),
|
|
() => InvalidExpression('foo')..fileOffset = _needFileOffset(),
|
|
() => InvalidExpression('foo', _createExpression())
|
|
..fileOffset = _needFileOffset(),
|
|
]);
|
|
case ExpressionKind.IsExpression:
|
|
return IsExpression(
|
|
_createExpression(),
|
|
_createDartType(),
|
|
)..fileOffset = _needFileOffset();
|
|
case ExpressionKind.Let:
|
|
return Let(
|
|
_createStatementFromKind(StatementKind.VariableDeclaration)
|
|
as VariableDeclaration,
|
|
_createExpression())
|
|
..fileOffset = _needFileOffset();
|
|
case ExpressionKind.ListConcatenation:
|
|
return _createOneOf(_pendingExpressions, kind, index, [
|
|
() => ListConcatenation([], typeArgument: _createDartType())
|
|
..fileOffset = _needFileOffset(),
|
|
() => ListConcatenation([_createExpression()],
|
|
typeArgument: _createDartType())
|
|
..fileOffset = _needFileOffset(),
|
|
() => ListConcatenation([_createExpression(), _createExpression()],
|
|
typeArgument: _createDartType())
|
|
..fileOffset = _needFileOffset()
|
|
]);
|
|
case ExpressionKind.ListLiteral:
|
|
return _createOneOf(_pendingExpressions, kind, index, [
|
|
() => ListLiteral([], typeArgument: _createDartType(), isConst: false)
|
|
..fileOffset = _needFileOffset(),
|
|
() => ListLiteral([_createExpression()],
|
|
typeArgument: _createDartType(), isConst: false)
|
|
..fileOffset = _needFileOffset(),
|
|
() => ListLiteral([_createExpression(), _createExpression()],
|
|
typeArgument: _createDartType(), isConst: false)
|
|
..fileOffset = _needFileOffset(),
|
|
() => ListLiteral([_createExpression(), _createExpression()],
|
|
typeArgument: _createDartType(), isConst: true)
|
|
..fileOffset = _needFileOffset(),
|
|
]);
|
|
case ExpressionKind.LoadLibrary:
|
|
return LoadLibrary(_needLibraryDependency(deferred: true))
|
|
..fileOffset = _needFileOffset();
|
|
case ExpressionKind.LocalFunctionInvocation:
|
|
return LocalFunctionInvocation(
|
|
_needFunctionDeclaration().variable, _createArguments(),
|
|
functionType: _createFunctionType())
|
|
..fileOffset = _needFileOffset();
|
|
case ExpressionKind.LogicalExpression:
|
|
return _createOneOf(_pendingExpressions, kind, index, [
|
|
() => LogicalExpression(_createExpression(),
|
|
LogicalExpressionOperator.AND, _createExpression())
|
|
..fileOffset = _needFileOffset(),
|
|
() => LogicalExpression(_createExpression(),
|
|
LogicalExpressionOperator.OR, _createExpression())
|
|
..fileOffset = _needFileOffset(),
|
|
]);
|
|
case ExpressionKind.MapConcatenation:
|
|
return _createOneOf(_pendingExpressions, kind, index, [
|
|
() => MapConcatenation([],
|
|
keyType: _createDartType(), valueType: _createDartType())
|
|
..fileOffset = _needFileOffset(),
|
|
() => MapConcatenation([_createExpression()],
|
|
keyType: _createDartType(), valueType: _createDartType())
|
|
..fileOffset = _needFileOffset(),
|
|
() => MapConcatenation([_createExpression(), _createExpression()],
|
|
keyType: _createDartType(), valueType: _createDartType())
|
|
..fileOffset = _needFileOffset(),
|
|
]);
|
|
case ExpressionKind.MapLiteral:
|
|
return _createOneOf(_pendingExpressions, kind, index, [
|
|
() => MapLiteral([],
|
|
keyType: _createDartType(),
|
|
valueType: _createDartType(),
|
|
isConst: false)
|
|
..fileOffset = _needFileOffset(),
|
|
() => MapLiteral([_createMapLiteralEntry()],
|
|
keyType: _createDartType(),
|
|
valueType: _createDartType(),
|
|
isConst: false)
|
|
..fileOffset = _needFileOffset(),
|
|
() => MapLiteral([
|
|
_createMapLiteralEntry(),
|
|
_createMapLiteralEntry(),
|
|
],
|
|
keyType: _createDartType(),
|
|
valueType: _createDartType(),
|
|
isConst: false)
|
|
..fileOffset = _needFileOffset(),
|
|
() => MapLiteral([
|
|
_createMapLiteralEntry(),
|
|
_createMapLiteralEntry(),
|
|
],
|
|
keyType: _createDartType(),
|
|
valueType: _createDartType(),
|
|
isConst: true)
|
|
..fileOffset = _needFileOffset(),
|
|
]);
|
|
case ExpressionKind.Not:
|
|
return Not(_createExpression())..fileOffset = _needFileOffset();
|
|
case ExpressionKind.NullCheck:
|
|
return NullCheck(_createExpression())..fileOffset = _needFileOffset();
|
|
case ExpressionKind.NullLiteral:
|
|
return NullLiteral();
|
|
case ExpressionKind.RedirectingFactoryTearOff:
|
|
return RedirectingFactoryTearOff(_needRedirectingFactory())
|
|
..fileOffset = _needFileOffset();
|
|
case ExpressionKind.Rethrow:
|
|
return Rethrow()..fileOffset = _needFileOffset();
|
|
case ExpressionKind.SetConcatenation:
|
|
return _createOneOf(_pendingExpressions, kind, index, [
|
|
() => SetConcatenation([], typeArgument: _createDartType())
|
|
..fileOffset = _needFileOffset(),
|
|
() => SetConcatenation([_createExpression()],
|
|
typeArgument: _createDartType())
|
|
..fileOffset = _needFileOffset(),
|
|
() => SetConcatenation([_createExpression(), _createExpression()],
|
|
typeArgument: _createDartType())
|
|
..fileOffset = _needFileOffset(),
|
|
]);
|
|
case ExpressionKind.SetLiteral:
|
|
return _createOneOf(_pendingExpressions, kind, index, [
|
|
() => SetLiteral([], typeArgument: _createDartType(), isConst: false)
|
|
..fileOffset = _needFileOffset(),
|
|
() => SetLiteral([_createExpression()],
|
|
typeArgument: _createDartType(), isConst: false)
|
|
..fileOffset = _needFileOffset(),
|
|
() => SetLiteral([_createExpression(), _createExpression()],
|
|
typeArgument: _createDartType(), isConst: false)
|
|
..fileOffset = _needFileOffset(),
|
|
() => SetLiteral([_createExpression(), _createExpression()],
|
|
typeArgument: _createDartType(), isConst: true)
|
|
..fileOffset = _needFileOffset(),
|
|
]);
|
|
case ExpressionKind.StaticGet:
|
|
return StaticGet(_needField())..fileOffset = _needFileOffset();
|
|
case ExpressionKind.StaticInvocation:
|
|
return StaticInvocation(_needProcedure(), _createArguments())
|
|
..fileOffset = _needFileOffset();
|
|
case ExpressionKind.StaticSet:
|
|
return StaticSet(_needField(), _createExpression())
|
|
..fileOffset = _needFileOffset();
|
|
case ExpressionKind.StaticTearOff:
|
|
return StaticTearOff(_needProcedure())..fileOffset = _needFileOffset();
|
|
case ExpressionKind.StringConcatenation:
|
|
return _createOneOf(_pendingExpressions, kind, index, [
|
|
() => StringConcatenation([])..fileOffset = _needFileOffset(),
|
|
() => StringConcatenation([_createExpression()])
|
|
..fileOffset = _needFileOffset(),
|
|
() => StringConcatenation([_createExpression(), _createExpression()])
|
|
..fileOffset = _needFileOffset(),
|
|
]);
|
|
case ExpressionKind.StringLiteral:
|
|
return StringLiteral('foo');
|
|
case ExpressionKind.SuperMethodInvocation:
|
|
return _createOneOf(_pendingExpressions, kind, index, [
|
|
() => SuperMethodInvocation(_createName(), _createArguments())
|
|
..fileOffset = _needFileOffset(),
|
|
() => SuperMethodInvocation(
|
|
_createName(), _createArguments(), _needProcedure())
|
|
..fileOffset = _needFileOffset(),
|
|
]);
|
|
case ExpressionKind.SuperPropertyGet:
|
|
return _createOneOf(_pendingExpressions, kind, index, [
|
|
() => SuperPropertyGet(_createName())..fileOffset = _needFileOffset(),
|
|
() => SuperPropertyGet(_createName(), _needField())
|
|
..fileOffset = _needFileOffset(),
|
|
]);
|
|
case ExpressionKind.SuperPropertySet:
|
|
return _createOneOf(_pendingExpressions, kind, index, [
|
|
() => SuperPropertySet(_createName(), _createExpression(), null)
|
|
..fileOffset = _needFileOffset(),
|
|
() =>
|
|
SuperPropertySet(_createName(), _createExpression(), _needField())
|
|
..fileOffset = _needFileOffset(),
|
|
]);
|
|
case ExpressionKind.SymbolLiteral:
|
|
return SymbolLiteral('foo')..fileOffset = _needFileOffset();
|
|
case ExpressionKind.ThisExpression:
|
|
return ThisExpression()..fileOffset = _needFileOffset();
|
|
case ExpressionKind.Throw:
|
|
return Throw(_createExpression())..fileOffset = _needFileOffset();
|
|
case ExpressionKind.TypeLiteral:
|
|
return TypeLiteral(_createDartType())..fileOffset = _needFileOffset();
|
|
case ExpressionKind.TypedefTearOff:
|
|
// TODO(johnniwinther): Add non-trivial cases.
|
|
return TypedefTearOff([], _createExpression(), [])
|
|
..fileOffset = _needFileOffset();
|
|
case ExpressionKind.VariableGet:
|
|
return _createOneOf(_pendingExpressions, kind, index, [
|
|
() => VariableGet(_needVariableDeclaration())
|
|
..fileOffset = _needFileOffset(),
|
|
() => VariableGet(_needVariableDeclaration(), _createDartType())
|
|
..fileOffset = _needFileOffset(),
|
|
]);
|
|
case ExpressionKind.VariableSet:
|
|
return VariableSet(_needVariableDeclaration(), _createExpression())
|
|
..fileOffset = _needFileOffset();
|
|
}
|
|
}
|
|
|
|
/// Creates a [Statement] node.
|
|
///
|
|
/// If there are any pending statements, one of these is created.
|
|
Statement _createStatement() {
|
|
if (_pendingStatements.isEmpty) {
|
|
return EmptyStatement()..fileOffset = _needFileOffset();
|
|
}
|
|
StatementKind kind = _pendingStatements.keys.first;
|
|
return _createStatementFromKind(kind);
|
|
}
|
|
|
|
/// Creates a [Statement] of the specified [kind].
|
|
///
|
|
/// If there are any pending statements of this [kind], one of these is
|
|
/// created.
|
|
Statement _createStatementFromKind(StatementKind kind) {
|
|
int? index = _pendingStatements.remove(kind);
|
|
switch (kind) {
|
|
case StatementKind.AssertBlock:
|
|
return _createOneOf(_pendingStatements, kind, index, [
|
|
() => AssertBlock([])..fileOffset = _needFileOffset(),
|
|
() =>
|
|
AssertBlock([_createStatement()])..fileOffset = _needFileOffset(),
|
|
() => AssertBlock([_createStatement(), _createStatement()])
|
|
..fileOffset = _needFileOffset(),
|
|
]);
|
|
case StatementKind.AssertStatement:
|
|
return _createOneOf(_pendingStatements, kind, index, [
|
|
() => AssertStatement(_createExpression(),
|
|
conditionStartOffset: _needFileOffset(),
|
|
conditionEndOffset: _needFileOffset())
|
|
..fileOffset = _needFileOffset(),
|
|
() => AssertStatement(_createExpression(),
|
|
message: _createExpression(),
|
|
conditionStartOffset: _needFileOffset(),
|
|
conditionEndOffset: _needFileOffset())
|
|
..fileOffset = _needFileOffset(),
|
|
]);
|
|
case StatementKind.Block:
|
|
return _createOneOf(_pendingStatements, kind, index, [
|
|
() => Block([])..fileOffset = _needFileOffset(),
|
|
() => Block([_createStatement()])..fileOffset = _needFileOffset(),
|
|
() => Block([_createStatement(), _createStatement()])
|
|
..fileOffset = _needFileOffset(),
|
|
]);
|
|
case StatementKind.BreakStatement:
|
|
return BreakStatement(_needLabeledStatement())
|
|
..fileOffset = _needFileOffset();
|
|
case StatementKind.ContinueSwitchStatement:
|
|
return ContinueSwitchStatement(_needSwitchCase())
|
|
..fileOffset = _needFileOffset();
|
|
case StatementKind.DoStatement:
|
|
return DoStatement(_createStatement(), _createExpression())
|
|
..fileOffset = _needFileOffset();
|
|
case StatementKind.EmptyStatement:
|
|
return EmptyStatement()..fileOffset = _needFileOffset();
|
|
case StatementKind.ExpressionStatement:
|
|
return ExpressionStatement(_createExpression())
|
|
..fileOffset = _needFileOffset();
|
|
case StatementKind.ForInStatement:
|
|
return _createOneOf(_pendingStatements, kind, index, [
|
|
() => ForInStatement(_createVariableDeclaration(),
|
|
_createExpression(), _createStatement(),
|
|
isAsync: false)
|
|
..fileOffset = _needFileOffset(),
|
|
() => ForInStatement(_createVariableDeclaration(),
|
|
_createExpression(), _createStatement(),
|
|
isAsync: true)
|
|
..fileOffset = _needFileOffset(),
|
|
]);
|
|
case StatementKind.ForStatement:
|
|
return _createOneOf(_pendingStatements, kind, index, [
|
|
() => ForStatement([], null, [], _createStatement())
|
|
..fileOffset = _needFileOffset(),
|
|
() => ForStatement([_createVariableDeclaration()],
|
|
_createExpression(), [_createExpression()], _createStatement())
|
|
..fileOffset = _needFileOffset(),
|
|
() => ForStatement(
|
|
[_createVariableDeclaration(), _createVariableDeclaration()],
|
|
_createExpression(),
|
|
[_createExpression(), _createExpression()],
|
|
_createStatement())
|
|
..fileOffset = _needFileOffset(),
|
|
]);
|
|
case StatementKind.FunctionDeclaration:
|
|
return FunctionDeclaration(
|
|
VariableDeclaration(null), _createFunctionNode())
|
|
..fileOffset = _needFileOffset();
|
|
case StatementKind.IfStatement:
|
|
return _createOneOf(_pendingStatements, kind, index, [
|
|
() => IfStatement(_createExpression(), _createStatement(), null)
|
|
..fileOffset = _needFileOffset(),
|
|
() => IfStatement(
|
|
_createExpression(), _createStatement(), _createStatement())
|
|
..fileOffset = _needFileOffset(),
|
|
]);
|
|
case StatementKind.LabeledStatement:
|
|
return LabeledStatement(_createStatement())
|
|
..fileOffset = _needFileOffset();
|
|
case StatementKind.ReturnStatement:
|
|
return _createOneOf(_pendingStatements, kind, index, [
|
|
() => ReturnStatement()..fileOffset = _needFileOffset(),
|
|
() => ReturnStatement(_createExpression())
|
|
..fileOffset = _needFileOffset(),
|
|
]);
|
|
case StatementKind.SwitchStatement:
|
|
return _createOneOf(_pendingStatements, kind, index, [
|
|
() => SwitchStatement(_createExpression(), [])
|
|
..fileOffset = _needFileOffset(),
|
|
() => SwitchStatement(_createExpression(), [_createSwitchCase()])
|
|
..fileOffset = _needFileOffset(),
|
|
() => SwitchStatement(
|
|
_createExpression(), [_createSwitchCase(), _createSwitchCase()])
|
|
..fileOffset = _needFileOffset(),
|
|
]);
|
|
case StatementKind.TryCatch:
|
|
return _createOneOf(_pendingStatements, kind, index, [
|
|
() =>
|
|
TryCatch(_createStatement(), [])..fileOffset = _needFileOffset(),
|
|
() => TryCatch(_createStatement(), [_createCatch()])
|
|
..fileOffset = _needFileOffset(),
|
|
() => TryCatch(_createStatement(), [_createCatch(), _createCatch()])
|
|
..fileOffset = _needFileOffset(),
|
|
]);
|
|
case StatementKind.TryFinally:
|
|
return TryFinally(_createStatement(), _createStatement())
|
|
..fileOffset = _needFileOffset();
|
|
case StatementKind.VariableDeclaration:
|
|
return _createOneOf(_pendingStatements, kind, index, [
|
|
() => VariableDeclaration('foo')..fileOffset = _needFileOffset(),
|
|
() => VariableDeclaration('foo', initializer: _createExpression())
|
|
..fileOffset = _needFileOffset(),
|
|
() => VariableDeclaration('foo', type: _createDartType())
|
|
..fileOffset = _needFileOffset(),
|
|
]);
|
|
case StatementKind.WhileStatement:
|
|
return WhileStatement(_createExpression(), _createStatement())
|
|
..fileOffset = _needFileOffset();
|
|
case StatementKind.YieldStatement:
|
|
return _createOneOf(_pendingStatements, kind, index, [
|
|
() => YieldStatement(_createExpression(), isYieldStar: false)
|
|
..fileOffset = _needFileOffset(),
|
|
() => YieldStatement(_createExpression(), isYieldStar: true)
|
|
..fileOffset = _needFileOffset(),
|
|
]);
|
|
}
|
|
}
|
|
|
|
/// Creates a [VariableDeclaration] node.
|
|
///
|
|
/// If there are any pending [VariableDeclaration] nodes, one of these is
|
|
/// created.
|
|
VariableDeclaration _createVariableDeclaration() {
|
|
return _createStatementFromKind(StatementKind.VariableDeclaration)
|
|
as VariableDeclaration;
|
|
}
|
|
|
|
/// Creates a [DartType] node.
|
|
///
|
|
/// If there are any pending types, one of these is created.
|
|
DartType _createDartType() {
|
|
if (_pendingDartTypes.isEmpty) {
|
|
return VoidType();
|
|
}
|
|
DartTypeKind kind = _pendingDartTypes.keys.first;
|
|
return _createDartTypeFromKind(kind);
|
|
}
|
|
|
|
/// Creates a [DartType] node of the specified [kind].
|
|
///
|
|
/// If there are any pending types of this [kind], one of these is created.
|
|
DartType _createDartTypeFromKind(DartTypeKind kind) {
|
|
int? index = _pendingDartTypes.remove(kind);
|
|
switch (kind) {
|
|
case DartTypeKind.DynamicType:
|
|
return DynamicType();
|
|
case DartTypeKind.ExtensionType:
|
|
return ExtensionType(_needExtension(), Nullability.nonNullable);
|
|
case DartTypeKind.FunctionType:
|
|
return _createOneOf(_pendingDartTypes, kind, index, [
|
|
// TODO(johnniwinther): Create non-trivial cases.
|
|
() => FunctionType([], _createDartType(), Nullability.nonNullable),
|
|
]);
|
|
case DartTypeKind.FutureOrType:
|
|
return FutureOrType(_createDartType(), Nullability.nonNullable);
|
|
case DartTypeKind.InterfaceType:
|
|
return _createOneOf(_pendingDartTypes, kind, index, [
|
|
// TODO(johnniwinther): Create non-trivial cases.
|
|
() => InterfaceType(_needClass(), Nullability.nonNullable, []),
|
|
]);
|
|
case DartTypeKind.InvalidType:
|
|
return InvalidType();
|
|
case DartTypeKind.NeverType:
|
|
return NeverType.nonNullable();
|
|
case DartTypeKind.NullType:
|
|
return NullType();
|
|
case DartTypeKind.TypeParameterType:
|
|
return _createOneOf(_pendingDartTypes, kind, index, [
|
|
() =>
|
|
TypeParameterType(_needTypeParameter(), Nullability.nonNullable),
|
|
() => TypeParameterType(
|
|
_needTypeParameter(), Nullability.nonNullable, _createDartType()),
|
|
]);
|
|
case DartTypeKind.TypedefType:
|
|
return _createOneOf(_pendingDartTypes, kind, index, [
|
|
// TODO(johnniwinther): Create non-trivial cases.
|
|
() => TypedefType(_needTypedef(), Nullability.nonNullable, []),
|
|
]);
|
|
case DartTypeKind.VoidType:
|
|
return VoidType();
|
|
}
|
|
}
|
|
|
|
/// Creates a [FunctionType] node.
|
|
///
|
|
/// If there are any pending [FunctionType] nodes, one of these is created.
|
|
FunctionType _createFunctionType() {
|
|
return _createDartTypeFromKind(DartTypeKind.FunctionType) as FunctionType;
|
|
}
|
|
|
|
/// Creates a [Constant] node.
|
|
///
|
|
/// If there are any pending constants, one of these is created.
|
|
Constant _createConstant() {
|
|
if (_pendingConstants.isEmpty) {
|
|
return NullConstant();
|
|
}
|
|
ConstantKind kind = _pendingConstants.keys.first;
|
|
return _createConstantFromKind(kind);
|
|
}
|
|
|
|
/// Creates a [Constant] node of the specified [kind].
|
|
///
|
|
/// If there are any pending constants of this [kind], one of these is
|
|
/// created.
|
|
Constant _createConstantFromKind(ConstantKind kind) {
|
|
int? index = _pendingConstants.remove(kind);
|
|
switch (kind) {
|
|
case ConstantKind.BoolConstant:
|
|
return BoolConstant(true);
|
|
case ConstantKind.ConstructorTearOffConstant:
|
|
return ConstructorTearOffConstant(_needConstructor());
|
|
case ConstantKind.DoubleConstant:
|
|
return DoubleConstant(42.5);
|
|
case ConstantKind.InstanceConstant:
|
|
return _createOneOf(_pendingConstants, kind, index, [
|
|
() => InstanceConstant(_needClass().reference, [], {}),
|
|
() => InstanceConstant(_needClass().reference, [
|
|
_createDartType()
|
|
], {
|
|
_needField(isStatic: false, hasSetter: false).getterReference:
|
|
_createConstant()
|
|
}),
|
|
() => InstanceConstant(_needClass().reference, [
|
|
_createDartType()
|
|
], {
|
|
_needField(index: 0, isStatic: false, hasSetter: false)
|
|
.getterReference: _createConstant(),
|
|
_needField(index: 1, isStatic: false, hasSetter: false)
|
|
.getterReference: _createConstant()
|
|
}),
|
|
]);
|
|
case ConstantKind.InstantiationConstant:
|
|
return InstantiationConstant(_createConstant(), [_createDartType()]);
|
|
case ConstantKind.IntConstant:
|
|
return IntConstant(42);
|
|
case ConstantKind.ListConstant:
|
|
return _createOneOf(_pendingConstants, kind, index, [
|
|
() => ListConstant(_createDartType(), []),
|
|
() => ListConstant(_createDartType(), [_createConstant()]),
|
|
() => ListConstant(
|
|
_createDartType(), [_createConstant(), _createConstant()]),
|
|
]);
|
|
case ConstantKind.MapConstant:
|
|
return _createOneOf(_pendingConstants, kind, index, [
|
|
() => MapConstant(_createDartType(), _createDartType(), []),
|
|
() => MapConstant(_createDartType(), _createDartType(),
|
|
[ConstantMapEntry(_createConstant(), _createConstant())]),
|
|
() => MapConstant(_createDartType(), _createDartType(), [
|
|
ConstantMapEntry(_createConstant(), _createConstant()),
|
|
ConstantMapEntry(_createConstant(), _createConstant())
|
|
]),
|
|
]);
|
|
case ConstantKind.NullConstant:
|
|
return NullConstant();
|
|
case ConstantKind.RedirectingFactoryTearOffConstant:
|
|
return RedirectingFactoryTearOffConstant(_needRedirectingFactory());
|
|
case ConstantKind.SetConstant:
|
|
return _createOneOf(_pendingConstants, kind, index, [
|
|
() => SetConstant(_createDartType(), []),
|
|
() => SetConstant(_createDartType(), [_createConstant()]),
|
|
() => SetConstant(
|
|
_createDartType(), [_createConstant(), _createConstant()]),
|
|
]);
|
|
case ConstantKind.StaticTearOffConstant:
|
|
return StaticTearOffConstant(_needProcedure());
|
|
case ConstantKind.StringConstant:
|
|
return StringConstant('foo');
|
|
case ConstantKind.SymbolConstant:
|
|
return _createOneOf(_pendingConstants, kind, index, [
|
|
() => SymbolConstant('foo', null),
|
|
() => SymbolConstant('_foo', _needLibrary().reference),
|
|
]);
|
|
case ConstantKind.TypeLiteralConstant:
|
|
return TypeLiteralConstant(_createDartType());
|
|
case ConstantKind.TypedefTearOffConstant:
|
|
// TODO(johnniwinther): Add non-trivial cases.
|
|
return TypedefTearOffConstant(
|
|
[],
|
|
_createConstantFromKind(ConstantKind.ConstructorTearOffConstant)
|
|
as TearOffConstant,
|
|
[]);
|
|
case ConstantKind.UnevaluatedConstant:
|
|
return UnevaluatedConstant(_createExpression());
|
|
}
|
|
}
|
|
|
|
/// Creates an [Initializer] node.
|
|
///
|
|
/// If there are any pending initializers, one of these is created.
|
|
Initializer _createInitializer() {
|
|
if (_pendingInitializers.isEmpty) {
|
|
return InvalidInitializer()..fileOffset = _needFileOffset();
|
|
}
|
|
InitializerKind kind = _pendingInitializers.keys.first;
|
|
return _createInitializerFromKind(kind);
|
|
}
|
|
|
|
/// Creates an [Initializer] node of the specified [kind].
|
|
///
|
|
/// If there are any pending initializers of this [kind], one of these is
|
|
/// created.
|
|
Initializer _createInitializerFromKind(InitializerKind kind) {
|
|
// ignore: unused_local_variable
|
|
int? index = _pendingInitializers.remove(kind);
|
|
switch (kind) {
|
|
case InitializerKind.AssertInitializer:
|
|
return AssertInitializer(
|
|
_createStatementFromKind(StatementKind.AssertStatement)
|
|
as AssertStatement)
|
|
..fileOffset = _needFileOffset();
|
|
case InitializerKind.FieldInitializer:
|
|
return FieldInitializer(
|
|
_needField(isStatic: false), _createExpression())
|
|
..fileOffset = _needFileOffset();
|
|
case InitializerKind.InvalidInitializer:
|
|
return InvalidInitializer()..fileOffset = _needFileOffset();
|
|
case InitializerKind.LocalInitializer:
|
|
return LocalInitializer(_createVariableDeclaration())
|
|
..fileOffset = _needFileOffset();
|
|
case InitializerKind.RedirectingInitializer:
|
|
return RedirectingInitializer(_needConstructor(), _createArguments())
|
|
..fileOffset = _needFileOffset();
|
|
case InitializerKind.SuperInitializer:
|
|
return SuperInitializer(_needConstructor(), _createArguments())
|
|
..fileOffset = _needFileOffset();
|
|
}
|
|
}
|
|
|
|
/// Creates a [Member] node.
|
|
///
|
|
/// If there are any pending members, one of these is created.
|
|
Member _createMember() {
|
|
if (_pendingMembers.isEmpty) {
|
|
return Field.immutable(_createName(), fileUri: _uri)
|
|
..fileOffset = _needFileOffset();
|
|
}
|
|
MemberKind kind = _pendingMembers.keys.first;
|
|
return _createMemberKind(kind);
|
|
}
|
|
|
|
/// Creates a [Member] node of the specified [kind].
|
|
///
|
|
/// If there are any pending members of this [kind], one of these is created.
|
|
Member _createMemberKind(MemberKind kind) {
|
|
int? index = _pendingMembers.remove(kind);
|
|
switch (kind) {
|
|
case MemberKind.Constructor:
|
|
return Constructor(_createFunctionNode(),
|
|
name: _createName(), fileUri: _uri)
|
|
..fileOffset = _needFileOffset();
|
|
case MemberKind.Field:
|
|
return _createOneOf(_pendingMembers, kind, index, [
|
|
() => Field.mutable(_createName(), fileUri: _uri)
|
|
..fileOffset = _needFileOffset(),
|
|
() => Field.immutable(_createName(), fileUri: _uri)
|
|
..fileOffset = _needFileOffset(),
|
|
]);
|
|
case MemberKind.Procedure:
|
|
return _createOneOf(_pendingMembers, kind, index, [
|
|
() => Procedure(
|
|
_createName(), ProcedureKind.Method, _createFunctionNode(),
|
|
fileUri: _uri)
|
|
..fileOffset = _needFileOffset(),
|
|
() => Procedure(
|
|
_createName(), ProcedureKind.Operator, _createFunctionNode(),
|
|
fileUri: _uri)
|
|
..fileOffset = _needFileOffset(),
|
|
() => Procedure(
|
|
_createName(), ProcedureKind.Getter, _createFunctionNode(),
|
|
fileUri: _uri)
|
|
..fileOffset = _needFileOffset(),
|
|
() => Procedure(
|
|
_createName(), ProcedureKind.Setter, _createFunctionNode(),
|
|
fileUri: _uri)
|
|
..fileOffset = _needFileOffset(),
|
|
]);
|
|
case MemberKind.RedirectingFactory:
|
|
return RedirectingFactory(null,
|
|
name: _createName(), function: _createFunctionNode(), fileUri: _uri)
|
|
..fileOffset = _needFileOffset();
|
|
}
|
|
}
|
|
|
|
/// Creates an [Arguments] node.
|
|
///
|
|
/// If there are any pending [Arguments] nodes, one of these is created.
|
|
Arguments _createArguments() {
|
|
return _createNodeFromKind(NodeKind.Arguments) as Arguments;
|
|
}
|
|
|
|
/// Creates a [Name] node.
|
|
///
|
|
/// If there are any pending [Name] nodes, one of these is created.
|
|
Name _createName() {
|
|
return _createNodeFromKind(NodeKind.Name) as Name;
|
|
}
|
|
|
|
/// Creates a [FunctionNode] node.
|
|
///
|
|
/// If there are any pending [FunctionNode] nodes, one of these is created.
|
|
FunctionNode _createFunctionNode() {
|
|
return _createNodeFromKind(NodeKind.FunctionNode) as FunctionNode;
|
|
}
|
|
|
|
/// Creates a [MapLiteralEntry] node.
|
|
///
|
|
/// If there are any pending [MapLiteralEntry] nodes, one of these is created.
|
|
MapLiteralEntry _createMapLiteralEntry() {
|
|
return _createNodeFromKind(NodeKind.MapLiteralEntry) as MapLiteralEntry;
|
|
}
|
|
|
|
/// Creates a [SwitchCase] node.
|
|
///
|
|
/// If there are any pending [SwitchCase] nodes, one of these is created.
|
|
SwitchCase _createSwitchCase() {
|
|
return _createNodeFromKind(NodeKind.SwitchCase) as SwitchCase;
|
|
}
|
|
|
|
/// Creates a [Catch] node.
|
|
///
|
|
/// If there are any pending [Catch] nodes, one of these is created.
|
|
Catch _createCatch() {
|
|
return _createNodeFromKind(NodeKind.Catch) as Catch;
|
|
}
|
|
|
|
/// Creates a [Node] of the specified [kind].
|
|
///
|
|
/// If there are any pending nodes of this [kind], one of these is created.
|
|
Node _createNodeFromKind(NodeKind kind) {
|
|
int? index = _pendingNodes.remove(kind);
|
|
switch (kind) {
|
|
case NodeKind.Arguments:
|
|
return _createOneOf(_pendingNodes, kind, index, [
|
|
// TODO(johnniwinther): Add non-trivial cases.
|
|
() => Arguments([])..fileOffset = _needFileOffset(),
|
|
]);
|
|
case NodeKind.Catch:
|
|
// TODO(johnniwinther): Add non-trivial cases.
|
|
return Catch(null, _createStatement())..fileOffset = _needFileOffset();
|
|
case NodeKind.Class:
|
|
return Class(name: 'foo', fileUri: _uri)
|
|
..fileOffset = _needFileOffset();
|
|
case NodeKind.Combinator:
|
|
return _createOneOf(_pendingNodes, kind, index, [
|
|
() => Combinator.show([])..fileOffset = _needFileOffset(),
|
|
() => Combinator.show(['foo'])..fileOffset = _needFileOffset(),
|
|
() => Combinator.show(['foo', 'bar'])..fileOffset = _needFileOffset(),
|
|
() => Combinator.hide([])..fileOffset = _needFileOffset(),
|
|
() => Combinator.hide(['foo'])..fileOffset = _needFileOffset(),
|
|
() => Combinator.hide(['foo', 'bar'])..fileOffset = _needFileOffset(),
|
|
]);
|
|
case NodeKind.Component:
|
|
return _component;
|
|
case NodeKind.Extension:
|
|
// TODO(johnniwinther): Add non-trivial cases.
|
|
return Extension(name: 'foo', fileUri: _uri)
|
|
..fileOffset = _needFileOffset()
|
|
..onType = _createDartType();
|
|
case NodeKind.FunctionNode:
|
|
// TODO(johnniwinther): Add non-trivial cases.
|
|
return FunctionNode(_createStatement())..fileOffset = _needFileOffset();
|
|
case NodeKind.Library:
|
|
return Library(_uri, fileUri: _uri)..fileOffset = _needFileOffset();
|
|
case NodeKind.LibraryDependency:
|
|
return _createOneOf(_pendingNodes, kind, index, [
|
|
// TODO(johnniwinther): Add more cases.
|
|
() => LibraryDependency.import(_needLibrary())
|
|
..fileOffset = _needFileOffset(),
|
|
() => LibraryDependency.import(_needLibrary(), name: 'foo')
|
|
..fileOffset = _needFileOffset(),
|
|
() => LibraryDependency.export(_needLibrary())
|
|
..fileOffset = _needFileOffset(),
|
|
]);
|
|
case NodeKind.LibraryPart:
|
|
// TODO(johnniwinther): Add non-trivial cases.
|
|
// TODO(johnniwinther): Do we need to use a valid part uri?
|
|
return LibraryPart([], 'foo')..fileOffset = _needFileOffset();
|
|
case NodeKind.MapLiteralEntry:
|
|
return MapLiteralEntry(_createExpression(), _createExpression())
|
|
..fileOffset = _needFileOffset();
|
|
case NodeKind.Name:
|
|
return _createOneOf(_pendingNodes, kind, index, [
|
|
() => Name('foo'),
|
|
() => Name('_foo', _needLibrary()),
|
|
]);
|
|
case NodeKind.NamedExpression:
|
|
return NamedExpression('foo', _createExpression())
|
|
..fileOffset = _needFileOffset();
|
|
case NodeKind.NamedType:
|
|
return NamedType('foo', _createDartType());
|
|
case NodeKind.Supertype:
|
|
return _createOneOf(_pendingNodes, kind, index, [
|
|
() => Supertype(_needClass(), []),
|
|
() => Supertype(_needClass(), [_createDartType()]),
|
|
() => Supertype(_needClass(), [_createDartType(), _createDartType()]),
|
|
]);
|
|
case NodeKind.SwitchCase:
|
|
// TODO(johnniwinther): Add non-trivial cases.
|
|
return SwitchCase(
|
|
[NullLiteral()], [TreeNode.noOffset], _createStatement())
|
|
..fileOffset = _needFileOffset();
|
|
case NodeKind.TypeParameter:
|
|
return TypeParameter('foo', _createDartType(), _createDartType())
|
|
..fileOffset = _needFileOffset();
|
|
case NodeKind.Typedef:
|
|
return Typedef('foo', _createDartType(), fileUri: _uri)
|
|
..fileOffset = _needFileOffset();
|
|
}
|
|
}
|
|
|
|
/// Helper that creates a node of type [V] using the list of [creators].
|
|
///
|
|
/// The [index] indicates how many nodes of the [kind] that have currently
|
|
/// been created. If there are more [creators] left after having created the
|
|
/// [index]th node, [pending] is updated with the next pending index. If all
|
|
/// pending nodes of the given [kind] have been created, [index] is `null` and
|
|
/// the first [creators] function is used.
|
|
V _createOneOf<K, V>(
|
|
Map<K, int> pending, K kind, int? index, List<V Function()> creators) {
|
|
if (index == null) {
|
|
// All pending nodes have been created so we just create the first.
|
|
return creators[0]();
|
|
}
|
|
if (index + 1 < creators.length) {
|
|
pending[kind] = index + 1;
|
|
}
|
|
return creators[index]();
|
|
}
|
|
}
|
|
|
|
/// [NodeKind]s for nodes that can occur inside member bodies.
|
|
const Set<NodeKind> inBodyNodeKinds = {
|
|
NodeKind.Arguments,
|
|
NodeKind.Catch,
|
|
NodeKind.FunctionNode,
|
|
NodeKind.MapLiteralEntry,
|
|
NodeKind.Name,
|
|
NodeKind.NamedExpression,
|
|
NodeKind.NamedType,
|
|
NodeKind.SwitchCase,
|
|
NodeKind.TypeParameter,
|
|
};
|