e30cd0322c
This separates VariableDeclaration from Statement. VariableDeclaration no longer implements Statement and variable declared in a block or in a for-statement are now wrapped by a VariableStatement. Currently there are two VariableStatement implementations; LegacyVariableStatement for variables in the current model, called LegacyVariable, and VariableInitialization for variables used in the new, still experimental, encoding that supports scope computation. This CL is a step towards realigning the AST nodes to the new model in which each kind of variable has its own distinct subclass. (LocalVariable, PositionalParameter, NamedParameter, SyntheticVariable, etc.) Note that it is not the intent to use VariableStatement in ForStatement going forward but that will be handled in a follow-up. TEST=existing. Change-Id: I5b309cd62c9b138f95b74fb054686edffa49a393 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/502681 Reviewed-by: Chloe Stefantsova <cstefantsova@google.com> Reviewed-by: Nicholas Shahan <nshahan@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com> Commit-Queue: Johnni Winther <johnniwinther@google.com>
3696 lines
100 KiB
Dart
3696 lines
100 KiB
Dart
// Copyright (c) 2016, 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.
|
|
|
|
library kernel.ast_to_text;
|
|
|
|
import 'dart:core';
|
|
import 'dart:convert' show json;
|
|
|
|
import '../ast.dart';
|
|
import '../import_table.dart';
|
|
import '../src/text_util.dart';
|
|
|
|
abstract mixin class Namer<T> {
|
|
int index = 0;
|
|
final Map<T, String> map = <T, String>{};
|
|
|
|
String getName(T key) => map.putIfAbsent(key, () => '$prefix${++index}');
|
|
|
|
String get prefix;
|
|
}
|
|
|
|
class NormalNamer<T> extends Namer<T> {
|
|
@override
|
|
final String prefix;
|
|
|
|
NormalNamer(this.prefix);
|
|
}
|
|
|
|
class ConstantNamer extends RecursiveVisitor with Namer<Constant> {
|
|
@override
|
|
final String prefix;
|
|
|
|
ConstantNamer(this.prefix);
|
|
|
|
@override
|
|
String getName(Constant constant) {
|
|
if (!map.containsKey(constant)) {
|
|
// When printing a non-fully linked kernel AST (i.e. some [Reference]s
|
|
// are not bound) to text, we need to avoid dereferencing any
|
|
// references.
|
|
//
|
|
// The normal visitor API causes references to be dereferenced in order
|
|
// to call the `visit<name>(<name>)` / `visit<name>Reference(<name>)`.
|
|
//
|
|
// We therefore handle any subclass of [Constant] which has [Reference]s
|
|
// specially here.
|
|
//
|
|
if (constant is InstanceConstant) {
|
|
// Avoid visiting `InstanceConstant.classReference`.
|
|
for (final Constant value in constant.fieldValues.values) {
|
|
// Name everything in post-order visit of DAG.
|
|
getName(value);
|
|
}
|
|
} else if (constant is TearOffConstant) {
|
|
// We only care about naming the constants themselves.
|
|
// [TearOffConstant]s has no Constant children.
|
|
// Avoid visiting `TearOffConstant.targetReference`.
|
|
} else {
|
|
// Name everything in post-order visit of DAG.
|
|
constant.visitChildren(this);
|
|
}
|
|
}
|
|
return super.getName(constant);
|
|
}
|
|
|
|
@override
|
|
void defaultConstantReference(Constant constant) {
|
|
getName(constant);
|
|
}
|
|
|
|
@override
|
|
void defaultDartType(DartType type) {
|
|
// No need to recurse into dart types, we only care about naming the
|
|
// constants themselves.
|
|
}
|
|
}
|
|
|
|
class Disambiguator<T, U> {
|
|
final Map<T, String> namesT = <T, String>{};
|
|
final Map<U, String> namesU = <U, String>{};
|
|
final Set<String> usedNames = new Set<String>();
|
|
|
|
String disambiguate(T? key1, U? key2, String proposeName()) {
|
|
String getNewName() {
|
|
String proposedName = proposeName();
|
|
if (usedNames.add(proposedName)) return proposedName;
|
|
int i = 2;
|
|
while (!usedNames.add('$proposedName$i')) {
|
|
++i;
|
|
}
|
|
return '$proposedName$i';
|
|
}
|
|
|
|
if (key1 != null) {
|
|
String? result = namesT[key1];
|
|
if (result != null) return result;
|
|
return namesT[key1] = getNewName();
|
|
}
|
|
if (key2 != null) {
|
|
String? result = namesU[key2];
|
|
if (result != null) return result;
|
|
return namesU[key2] = getNewName();
|
|
}
|
|
throw "Cannot disambiguate";
|
|
}
|
|
}
|
|
|
|
NameSystem globalDebuggingNames = new NameSystem();
|
|
|
|
String debugNodeToString(Node node) {
|
|
StringBuffer buffer = new StringBuffer();
|
|
new Printer(
|
|
buffer,
|
|
showOffsets: true,
|
|
syntheticNames: globalDebuggingNames,
|
|
).writeNode(node);
|
|
return '$buffer';
|
|
}
|
|
|
|
String debugLibraryToString(Library library) {
|
|
StringBuffer buffer = new StringBuffer();
|
|
new Printer(
|
|
buffer,
|
|
syntheticNames: globalDebuggingNames,
|
|
).writeLibraryFile(library);
|
|
return '$buffer';
|
|
}
|
|
|
|
String debugComponentToString(Component component) {
|
|
StringBuffer buffer = new StringBuffer();
|
|
new Printer(
|
|
buffer,
|
|
syntheticNames: new NameSystem(),
|
|
).writeComponentFile(component);
|
|
return '$buffer';
|
|
}
|
|
|
|
String componentToString(Component node) {
|
|
StringBuffer buffer = new StringBuffer();
|
|
new Printer(
|
|
buffer,
|
|
syntheticNames: new NameSystem(),
|
|
).writeComponentFile(node);
|
|
return '$buffer';
|
|
}
|
|
|
|
class NameSystem {
|
|
final Namer<VariableDeclaration> variables =
|
|
new NormalNamer<VariableDeclaration>('#t');
|
|
final Namer<Reference> libraries = new NormalNamer<Reference>('#lib');
|
|
final Namer<TypeParameter> typeParameters = new NormalNamer<TypeParameter>(
|
|
'#T',
|
|
);
|
|
final Namer<StructuralParameter> structuralParameters =
|
|
new NormalNamer<StructuralParameter>('#T');
|
|
final Namer<TreeNode> labels = new NormalNamer<TreeNode>('#L');
|
|
final Namer<Constant> constants = new ConstantNamer('#C');
|
|
final Namer<VariableContext> contexts = new NormalNamer<VariableContext>(
|
|
'#ctx',
|
|
);
|
|
final Disambiguator<Reference, CanonicalName> prefixes =
|
|
new Disambiguator<Reference, CanonicalName>();
|
|
|
|
String nameVariable(VariableDeclaration node) => variables.getName(node);
|
|
String nameLibrary(Reference node) => libraries.getName(node);
|
|
String nameTypeParameter(TypeParameter node) => typeParameters.getName(node);
|
|
String nameStructuralParameter(StructuralParameter node) =>
|
|
structuralParameters.getName(node);
|
|
String nameSwitchCase(SwitchCase node) => labels.getName(node);
|
|
String nameLabeledStatement(LabeledStatement node) => labels.getName(node);
|
|
String nameConstant(Constant node) => constants.getName(node);
|
|
String nameVariableContext(VariableContext node) => contexts.getName(node);
|
|
|
|
final RegExp pathSeparator = new RegExp('[\\/]');
|
|
|
|
String nameLibraryPrefix(Reference reference, {String? proposedName}) {
|
|
return prefixes.disambiguate(reference, reference.canonicalName, () {
|
|
if (proposedName != null) {
|
|
return proposedName;
|
|
}
|
|
NamedNode? node = reference.node;
|
|
if (node is Library) {
|
|
String? name = node.name;
|
|
if (name != null) {
|
|
return abbreviateName(name);
|
|
}
|
|
String path = node.importUri.hasEmptyPath
|
|
? '${node.importUri}'
|
|
: node.importUri.pathSegments.last;
|
|
if (path.endsWith('.dart')) {
|
|
path = path.substring(0, path.length - '.dart'.length);
|
|
}
|
|
return abbreviateName(path);
|
|
} else {
|
|
return _nameFromLibraryCanonicalName(
|
|
reference,
|
|
reference.canonicalName,
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
String _nameFromLibraryCanonicalName(Reference? node, CanonicalName? name) {
|
|
CanonicalName? canonicalName = name ?? node?.canonicalName;
|
|
if (canonicalName?.name != null) {
|
|
String path = canonicalName!.name;
|
|
int slash = path.lastIndexOf(pathSeparator);
|
|
if (slash >= 0) {
|
|
path = path.substring(slash + 1);
|
|
}
|
|
if (path.endsWith('.dart')) {
|
|
path = path.substring(0, path.length - '.dart'.length);
|
|
}
|
|
return abbreviateName(path);
|
|
}
|
|
return 'L';
|
|
}
|
|
|
|
String nameCanonicalNameAsLibraryPrefix(
|
|
Reference? node,
|
|
CanonicalName? name, {
|
|
String? proposedName,
|
|
}) {
|
|
return prefixes.disambiguate(node, name, () {
|
|
if (proposedName != null) return proposedName;
|
|
return _nameFromLibraryCanonicalName(node, name);
|
|
});
|
|
}
|
|
|
|
final RegExp punctuation = new RegExp('[.:]');
|
|
|
|
String abbreviateName(String name) {
|
|
int dot = name.lastIndexOf(punctuation);
|
|
if (dot != -1) {
|
|
name = name.substring(dot + 1);
|
|
}
|
|
if (name.length > 4) {
|
|
return name.substring(0, 3);
|
|
}
|
|
return name;
|
|
}
|
|
}
|
|
|
|
abstract class Annotator {
|
|
String annotateVariable(Printer printer, VariableDeclaration node);
|
|
String annotateReturn(Printer printer, FunctionNode node);
|
|
String annotateField(Printer printer, Field node);
|
|
}
|
|
|
|
/// A quick and dirty ambiguous text printer.
|
|
class Printer extends VisitorDefault<void> with VisitorVoidMixin {
|
|
final NameSystem syntheticNames;
|
|
final StringSink sink;
|
|
final Annotator? annotator;
|
|
final Map<String, MetadataRepository<dynamic>>? metadata;
|
|
ImportTable? importTable;
|
|
int indentation = 0;
|
|
int column = 0;
|
|
bool showOffsets;
|
|
bool showMetadata;
|
|
bool showLibraryForNames;
|
|
Library? _currentLibrary;
|
|
|
|
static final int SPACE = 0;
|
|
static final int WORD = 1;
|
|
static final int SYMBOL = 2;
|
|
int state = SPACE;
|
|
|
|
Printer(
|
|
this.sink, {
|
|
NameSystem? syntheticNames,
|
|
this.showOffsets = false,
|
|
this.showMetadata = false,
|
|
this.showLibraryForNames = false,
|
|
this.importTable,
|
|
this.annotator,
|
|
this.metadata,
|
|
}) : this.syntheticNames = syntheticNames ?? new NameSystem();
|
|
|
|
Printer createInner(
|
|
ImportTable importTable,
|
|
Map<String, MetadataRepository<dynamic>>? metadata,
|
|
) {
|
|
return new Printer(
|
|
sink,
|
|
importTable: importTable,
|
|
metadata: metadata,
|
|
syntheticNames: syntheticNames,
|
|
annotator: annotator,
|
|
showOffsets: showOffsets,
|
|
showMetadata: showMetadata,
|
|
);
|
|
}
|
|
|
|
bool shouldHighlight(Node node) {
|
|
return false;
|
|
}
|
|
|
|
void startHighlight(Node node) {}
|
|
void endHighlight(Node node) {}
|
|
|
|
String getLibraryName(Reference reference) {
|
|
Node? node = reference.node;
|
|
return (node is Library ? node.name : null) ??
|
|
syntheticNames.nameLibrary(reference);
|
|
}
|
|
|
|
String getLibraryReference(Reference reference) {
|
|
Node? node = reference.node;
|
|
if (importTable != null &&
|
|
(node is! Library || importTable?.getImportIndex(node) != -1)) {
|
|
return syntheticNames.nameLibraryPrefix(reference);
|
|
}
|
|
return getLibraryName(reference);
|
|
}
|
|
|
|
String getClassName(Class node) {
|
|
return node.name;
|
|
}
|
|
|
|
String getExtensionName(Extension node) {
|
|
return node.name;
|
|
}
|
|
|
|
String getExtensionTypeDeclarationName(ExtensionTypeDeclaration node) {
|
|
return node.name;
|
|
}
|
|
|
|
String getClassReference(Class node) {
|
|
String name = getClassName(node);
|
|
String library = getLibraryReference(node.enclosingLibrary.reference);
|
|
return '$library::$name';
|
|
}
|
|
|
|
String getExtensionTypeDeclarationReference(ExtensionTypeDeclaration node) {
|
|
String name = getExtensionTypeDeclarationName(node);
|
|
String library = getLibraryReference(node.enclosingLibrary.reference);
|
|
return '$library::$name';
|
|
}
|
|
|
|
String getTypedefReference(Typedef node) {
|
|
String library = getLibraryReference(node.enclosingLibrary.reference);
|
|
return '$library::${node.name}';
|
|
}
|
|
|
|
static final String emptyNameString = '•';
|
|
static final Name emptyName = new Name(emptyNameString);
|
|
|
|
Name getMemberName(Member node) {
|
|
if (node.name.text == '') return emptyName;
|
|
return node.name;
|
|
}
|
|
|
|
String getMemberReference(Member node) {
|
|
String name = getMemberName(node).text;
|
|
GenericDeclaration? enclosingDeclaration = node.enclosingTypeDeclaration;
|
|
if (enclosingDeclaration is Class) {
|
|
String declarationName = getClassReference(enclosingDeclaration);
|
|
return '$declarationName::$name';
|
|
} else if (enclosingDeclaration is ExtensionTypeDeclaration) {
|
|
String declarationName = getExtensionTypeDeclarationReference(
|
|
enclosingDeclaration,
|
|
);
|
|
return '$declarationName::$name';
|
|
} else {
|
|
String library = getLibraryReference(node.enclosingLibrary.reference);
|
|
return '$library::$name';
|
|
}
|
|
}
|
|
|
|
String getVariableName(VariableDeclaration node) {
|
|
return node.cosmeticName ?? syntheticNames.nameVariable(node);
|
|
}
|
|
|
|
String getVariableReference(VariableDeclaration node) {
|
|
return getVariableName(node);
|
|
}
|
|
|
|
String getTypeParameterName(TypeParameter node) {
|
|
return node.name ?? syntheticNames.nameTypeParameter(node);
|
|
}
|
|
|
|
String getStructuralParameterName(StructuralParameter node) {
|
|
return node.name ?? syntheticNames.nameStructuralParameter(node);
|
|
}
|
|
|
|
String getTypeParameterReference(TypeParameter node) {
|
|
String name = getTypeParameterName(node);
|
|
GenericDeclaration? declaration = node.declaration;
|
|
switch (declaration) {
|
|
case Class():
|
|
String className = getClassReference(declaration);
|
|
return '$className::$name';
|
|
case Procedure():
|
|
String member = getMemberReference(declaration);
|
|
return '$member::$name';
|
|
case Typedef():
|
|
case Extension():
|
|
case ExtensionTypeDeclaration():
|
|
case LocalFunction():
|
|
// TODO(johnniwinther): Support these cases correctly.
|
|
case null:
|
|
return name; // Bound inside a function type.
|
|
}
|
|
}
|
|
|
|
String getStructuralParameterReference(StructuralParameter node) {
|
|
return getStructuralParameterName(node);
|
|
}
|
|
|
|
void writeComponentProblems(Component component) {
|
|
writeProblemsAsJson("Problems in component", component.problemsAsJson);
|
|
}
|
|
|
|
void writeProblemsAsJson(String header, List<String>? problemsAsJson) {
|
|
if (problemsAsJson != null && problemsAsJson.isNotEmpty) {
|
|
endLine("//");
|
|
write("// ");
|
|
write(header);
|
|
endLine(":");
|
|
endLine("//");
|
|
for (String s in problemsAsJson) {
|
|
Map<String, dynamic> decoded = json.decode(s);
|
|
List<dynamic> plainTextFormatted =
|
|
decoded["plainTextFormatted"] as List<dynamic>;
|
|
List<String> lines = plainTextFormatted.join("\n").split("\n");
|
|
for (int i = 0; i < lines.length; i++) {
|
|
write("//");
|
|
String trimmed = lines[i].trimRight();
|
|
if (trimmed.isNotEmpty) write(" ");
|
|
endLine(trimmed);
|
|
}
|
|
if (lines.isNotEmpty) endLine("//");
|
|
}
|
|
}
|
|
}
|
|
|
|
void writeLibraryFile(Library library) {
|
|
writeAnnotationList(library.annotations);
|
|
writeWord('library');
|
|
String? name = library.name;
|
|
if (name != null) {
|
|
writeWord(name);
|
|
}
|
|
List<String> flags = [];
|
|
if (!library.conditionalImportSupported &&
|
|
library.importUri.isScheme('dart')) {
|
|
flags.add('!conditionalImportSupported');
|
|
}
|
|
if (flags.isNotEmpty) {
|
|
writeWord('/*${flags.join(',')}*/');
|
|
}
|
|
endLine(';');
|
|
|
|
LibraryImportTable imports = new LibraryImportTable(library);
|
|
Printer inner = createInner(imports, library.enclosingComponent?.metadata);
|
|
inner.writeStandardLibraryContent(
|
|
library,
|
|
outerPrinter: this,
|
|
importsToPrint: imports,
|
|
);
|
|
}
|
|
|
|
void writeProcedureInLibrary(Procedure procedure, Library library) {
|
|
_currentLibrary = library;
|
|
visitProcedure(procedure);
|
|
_currentLibrary = null;
|
|
}
|
|
|
|
void printLibraryImportTable(LibraryImportTable imports) {
|
|
for (Library library in imports.importedLibraries) {
|
|
String importPath = imports.getImportPath(library);
|
|
if (importPath == "") {
|
|
String prefix = syntheticNames.nameLibraryPrefix(
|
|
library.reference,
|
|
proposedName: 'self',
|
|
);
|
|
endLine('import self as $prefix;');
|
|
} else {
|
|
String prefix = syntheticNames.nameLibraryPrefix(library.reference);
|
|
endLine('import "$importPath" as $prefix;');
|
|
}
|
|
}
|
|
}
|
|
|
|
void writeStandardLibraryContent(
|
|
Library library, {
|
|
Printer? outerPrinter,
|
|
LibraryImportTable? importsToPrint,
|
|
}) {
|
|
_currentLibrary = library;
|
|
outerPrinter ??= this;
|
|
outerPrinter.writeProblemsAsJson(
|
|
"Problems in library",
|
|
library.problemsAsJson,
|
|
);
|
|
|
|
if (importsToPrint != null) {
|
|
outerPrinter.printLibraryImportTable(importsToPrint);
|
|
}
|
|
|
|
writeAdditionalExports(library.additionalExports);
|
|
endLine();
|
|
library.dependencies.forEach(writeNode);
|
|
if (library.dependencies.isNotEmpty) endLine();
|
|
library.parts.forEach(writeNode);
|
|
library.typedefs.forEach(writeNode);
|
|
library.classes.forEach(writeNode);
|
|
library.extensions.forEach(writeNode);
|
|
library.extensionTypeDeclarations.forEach(writeNode);
|
|
library.fields.forEach(writeNode);
|
|
library.procedures.forEach(writeNode);
|
|
_currentLibrary = null;
|
|
}
|
|
|
|
void writeAdditionalExports(List<Reference> additionalExports) {
|
|
if (additionalExports.isEmpty) return;
|
|
write('additionalExports = (');
|
|
for (int i = 0; i < additionalExports.length; i++) {
|
|
Reference reference = additionalExports[i];
|
|
NamedNode? node = reference.node;
|
|
if (node is Class) {
|
|
Library nodeLibrary = node.enclosingLibrary;
|
|
String prefix = syntheticNames.nameLibraryPrefix(nodeLibrary.reference);
|
|
write(prefix + '::' + node.name);
|
|
} else if (node is Extension) {
|
|
Library nodeLibrary = node.enclosingLibrary;
|
|
String prefix = syntheticNames.nameLibraryPrefix(nodeLibrary.reference);
|
|
write(prefix + '::' + node.name);
|
|
} else if (node is Field) {
|
|
Library nodeLibrary = node.enclosingLibrary;
|
|
String prefix = syntheticNames.nameLibraryPrefix(nodeLibrary.reference);
|
|
write(prefix + '::' + node.name.text);
|
|
} else if (node is ExtensionTypeDeclaration) {
|
|
Library nodeLibrary = node.enclosingLibrary;
|
|
String prefix = syntheticNames.nameLibraryPrefix(nodeLibrary.reference);
|
|
write(prefix + '::' + node.name);
|
|
} else if (node is Procedure) {
|
|
Library nodeLibrary = node.enclosingLibrary;
|
|
String prefix = syntheticNames.nameLibraryPrefix(nodeLibrary.reference);
|
|
write(prefix + '::' + node.name.text);
|
|
} else if (node is Typedef) {
|
|
Library nodeLibrary = node.enclosingLibrary;
|
|
String prefix = syntheticNames.nameLibraryPrefix(nodeLibrary.reference);
|
|
write(prefix + '::' + node.name);
|
|
} else if (reference.canonicalName != null) {
|
|
write(reference.canonicalName.toString());
|
|
} else {
|
|
throw new UnimplementedError('${node.runtimeType}');
|
|
}
|
|
|
|
if (i + 1 == additionalExports.length) {
|
|
endLine(")");
|
|
} else {
|
|
endLine(",");
|
|
write(" ");
|
|
}
|
|
}
|
|
}
|
|
|
|
void writeComponentFile(Component component) {
|
|
ImportTable imports = new ComponentImportTable(component);
|
|
Printer inner = createInner(imports, component.metadata);
|
|
writeWord('main');
|
|
writeSpaced('=');
|
|
inner.writeMemberReferenceFromReference(component.mainMethodName);
|
|
endLine(';');
|
|
if (showMetadata) {
|
|
inner.writeMetadata(component);
|
|
}
|
|
writeComponentProblems(component);
|
|
for (Library library in component.libraries) {
|
|
if (showMetadata) {
|
|
inner.writeMetadata(library, separateLines: true);
|
|
}
|
|
writeAnnotationList(library.annotations);
|
|
writeWord('library');
|
|
String? name = library.name;
|
|
if (name != null) {
|
|
writeWord(name);
|
|
}
|
|
writeSpaced('from');
|
|
writeWord('"${library.importUri}"');
|
|
String prefix = syntheticNames.nameLibraryPrefix(library.reference);
|
|
writeSpaced('as');
|
|
writeWord(prefix);
|
|
endLine(' {');
|
|
++inner.indentation;
|
|
|
|
inner.writeStandardLibraryContent(library);
|
|
--inner.indentation;
|
|
endLine('}');
|
|
}
|
|
writeConstantTable(component);
|
|
}
|
|
|
|
void writeConstantTable(Component component) {
|
|
if (syntheticNames.constants.map.isEmpty) return;
|
|
ImportTable imports = new ComponentImportTable(component);
|
|
Printer inner = createInner(imports, component.metadata);
|
|
writeWord('constants ');
|
|
endLine(' {');
|
|
++inner.indentation;
|
|
for (final Constant constant
|
|
in syntheticNames.constants.map.keys.toList()) {
|
|
inner.writeNode(constant);
|
|
}
|
|
--inner.indentation;
|
|
endLine('}');
|
|
}
|
|
|
|
int getPrecedence(Expression node) {
|
|
return Precedence.of(node);
|
|
}
|
|
|
|
void write(String string) {
|
|
sink.write(string);
|
|
column += string.length;
|
|
}
|
|
|
|
void writeSpace([String string = ' ']) {
|
|
write(string);
|
|
state = SPACE;
|
|
}
|
|
|
|
void ensureSpace() {
|
|
if (state != SPACE) writeSpace();
|
|
}
|
|
|
|
void writeSymbol(String string) {
|
|
write(string);
|
|
state = SYMBOL;
|
|
}
|
|
|
|
void writeSpaced(String string) {
|
|
ensureSpace();
|
|
write(string);
|
|
writeSpace();
|
|
}
|
|
|
|
void writeComma([String string = ',']) {
|
|
write(string);
|
|
writeSpace();
|
|
}
|
|
|
|
void writeWord(String string) {
|
|
if (string.isEmpty) return;
|
|
ensureWordBoundary();
|
|
write(string);
|
|
state = WORD;
|
|
}
|
|
|
|
void ensureWordBoundary() {
|
|
if (state == WORD) {
|
|
writeSpace();
|
|
}
|
|
}
|
|
|
|
void writeIndentation() {
|
|
writeSpace(' ' * indentation);
|
|
}
|
|
|
|
void writeNode(Node? node) {
|
|
if (node == null) {
|
|
writeSymbol("<Null>");
|
|
} else {
|
|
final bool highlight = shouldHighlight(node);
|
|
if (highlight) {
|
|
startHighlight(node);
|
|
}
|
|
|
|
if (showOffsets && node is TreeNode) {
|
|
writeWord("[${node.fileOffset}]");
|
|
}
|
|
if (showMetadata && node is TreeNode && node is! VariableDeclaration) {
|
|
writeMetadata(
|
|
node,
|
|
separateLines: node is Member || node is FunctionDeclaration,
|
|
);
|
|
}
|
|
|
|
node.accept(this);
|
|
|
|
if (highlight) {
|
|
endHighlight(node);
|
|
}
|
|
}
|
|
}
|
|
|
|
void writeMetadata(TreeNode node, {bool separateLines = false}) {
|
|
if (metadata != null) {
|
|
bool needsLeadingNewLine = separateLines;
|
|
for (MetadataRepository<dynamic> md in metadata!.values) {
|
|
final dynamic nodeMetadata = md.mapping[node];
|
|
if (nodeMetadata != null) {
|
|
if (needsLeadingNewLine) {
|
|
endLine();
|
|
needsLeadingNewLine = false;
|
|
}
|
|
if (separateLines) {
|
|
writeIndentation();
|
|
}
|
|
writeWord("[@${md.tag}=${nodeMetadata}]");
|
|
if (separateLines) {
|
|
endLine();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void writeAnnotatedType(DartType type, String? annotation) {
|
|
writeType(type);
|
|
if (annotation != null) {
|
|
write('/');
|
|
write(annotation);
|
|
state = WORD;
|
|
}
|
|
}
|
|
|
|
void writeType(DartType type) {
|
|
type.accept(this);
|
|
}
|
|
|
|
@override
|
|
void visitSupertype(Supertype type) {
|
|
writeClassReferenceFromReference(type.className);
|
|
if (type.typeArguments.isNotEmpty) {
|
|
writeSymbol('<');
|
|
writeList(type.typeArguments, writeType);
|
|
writeSymbol('>');
|
|
}
|
|
}
|
|
|
|
@override
|
|
void visitTypedefType(TypedefType type) {
|
|
writeTypedefReference(type.typedefNode);
|
|
if (type.typeArguments.isNotEmpty) {
|
|
writeSymbol('<');
|
|
writeList(type.typeArguments, writeType);
|
|
writeSymbol('>');
|
|
}
|
|
}
|
|
|
|
void writeModifier(bool isThere, String name) {
|
|
if (isThere) {
|
|
writeWord(name);
|
|
}
|
|
}
|
|
|
|
void writeName(Name name, {bool showLibrary = false}) {
|
|
if ((showLibrary || showLibraryForNames) &&
|
|
name.isPrivate &&
|
|
name.libraryReference != _currentLibrary?.reference) {
|
|
writeWord('${getLibraryReference(name.libraryReference!)}::${name.text}');
|
|
} else if (name.text == '') {
|
|
writeWord(emptyNameString);
|
|
} else {
|
|
writeWord(name.text);
|
|
}
|
|
}
|
|
|
|
void endLine([String? string]) {
|
|
if (string != null) {
|
|
write(string);
|
|
}
|
|
write('\n');
|
|
state = SPACE;
|
|
column = 0;
|
|
}
|
|
|
|
void _writeContexts(ContextConsumer consumer) {
|
|
if (consumer.capturedContexts case List<VariableContext> contexts
|
|
when contexts.isNotEmpty) {
|
|
ensureSpace();
|
|
writeWord('/*');
|
|
for (VariableContext context in contexts) {
|
|
writeWord(syntheticNames.nameVariableContext(context));
|
|
}
|
|
writeWord('*/');
|
|
}
|
|
}
|
|
|
|
void writeFunction(
|
|
FunctionNode function, {
|
|
name,
|
|
List<Initializer>? initializers,
|
|
bool terminateLine = true,
|
|
}) {
|
|
if (showOffsets) {
|
|
writeWord("[${function.fileOffset}]");
|
|
writeWord("[${function.fileEndOffset}]");
|
|
}
|
|
if (name is String) {
|
|
writeWord(name);
|
|
} else if (name is Name) {
|
|
writeName(name, showLibrary: true);
|
|
} else {
|
|
assert(name == null);
|
|
}
|
|
writeTypeParameterList(function.typeParameters);
|
|
writeParameterList(
|
|
function.positionalParameters,
|
|
function.namedParameters,
|
|
function.requiredParameterCount,
|
|
);
|
|
_writeContexts(function);
|
|
writeReturnType(
|
|
function.returnType,
|
|
annotator?.annotateReturn(this, function),
|
|
);
|
|
if (initializers != null && initializers.isNotEmpty) {
|
|
endLine();
|
|
++indentation;
|
|
writeIndentation();
|
|
writeComma(':');
|
|
writeList(initializers, writeNode);
|
|
--indentation;
|
|
}
|
|
if (function.asyncMarker != AsyncMarker.Sync) {
|
|
writeSpaced(getAsyncMarkerKeyword(function.asyncMarker));
|
|
}
|
|
if (function.emittedValueType != null) {
|
|
writeSpaced("/* emittedValueType=");
|
|
writeNode(function.emittedValueType);
|
|
writeSpaced("*/");
|
|
}
|
|
if (function.dartAsyncMarker != AsyncMarker.Sync &&
|
|
function.dartAsyncMarker != function.asyncMarker) {
|
|
writeSpaced("/* originally");
|
|
writeSpaced(getAsyncMarkerKeyword(function.dartAsyncMarker));
|
|
writeSpaced("*/");
|
|
}
|
|
RedirectingFactoryTarget? redirectingFactoryTarget =
|
|
function.redirectingFactoryTarget;
|
|
if (redirectingFactoryTarget != null &&
|
|
redirectingFactoryTarget.target != null) {
|
|
writeWord('/* redirection-target:');
|
|
writeMemberReferenceFromReference(
|
|
redirectingFactoryTarget.targetReference,
|
|
);
|
|
if (redirectingFactoryTarget.typeArguments!.isNotEmpty) {
|
|
writeSymbol('<');
|
|
writeList(redirectingFactoryTarget.typeArguments!, writeType);
|
|
writeSymbol('>');
|
|
}
|
|
writeWord('*/');
|
|
}
|
|
if (function.scope case Scope scope?) {
|
|
writeScope(scope);
|
|
}
|
|
Statement? body = function.body;
|
|
if (body != null) {
|
|
writeFunctionBody(body, terminateLine: terminateLine);
|
|
} else if (terminateLine) {
|
|
endLine(';');
|
|
} else {
|
|
writeSymbol(';');
|
|
}
|
|
}
|
|
|
|
String getAsyncMarkerKeyword(AsyncMarker marker) {
|
|
switch (marker) {
|
|
case AsyncMarker.Sync:
|
|
return 'sync';
|
|
case AsyncMarker.SyncStar:
|
|
return 'sync*';
|
|
case AsyncMarker.Async:
|
|
return 'async';
|
|
case AsyncMarker.AsyncStar:
|
|
return 'async*';
|
|
}
|
|
}
|
|
|
|
void writeFunctionBody(Statement body, {bool terminateLine = true}) {
|
|
if (body is Block && body.statements.isEmpty) {
|
|
ensureSpace();
|
|
writeSymbol('{}');
|
|
state = WORD;
|
|
if (terminateLine) {
|
|
endLine();
|
|
}
|
|
} else if (body is Block) {
|
|
ensureSpace();
|
|
if (body.scope case Scope scope?) {
|
|
writeScope(scope);
|
|
}
|
|
ensureSpace();
|
|
endLine('{');
|
|
++indentation;
|
|
body.statements.forEach(writeNode);
|
|
--indentation;
|
|
writeIndentation();
|
|
writeSymbol('}');
|
|
state = WORD;
|
|
if (terminateLine) {
|
|
endLine();
|
|
}
|
|
} else if (body is ReturnStatement && !terminateLine) {
|
|
writeSpaced('=>');
|
|
writeExpression(body.expression!);
|
|
} else {
|
|
writeBody(body);
|
|
}
|
|
}
|
|
|
|
void writeFunctionType(FunctionType node) {
|
|
if (state == WORD) {
|
|
ensureSpace();
|
|
}
|
|
writeStructuralParameterList(node.typeParameters);
|
|
writeSymbol('(');
|
|
List<DartType> positional = node.positionalParameters;
|
|
|
|
writeList(positional.take(node.requiredParameterCount), writeType);
|
|
|
|
if (node.requiredParameterCount < positional.length) {
|
|
if (node.requiredParameterCount > 0) {
|
|
writeComma();
|
|
}
|
|
writeSymbol('[');
|
|
writeList(positional.skip(node.requiredParameterCount), writeType);
|
|
writeSymbol(']');
|
|
}
|
|
if (node.namedParameters.isNotEmpty) {
|
|
if (node.positionalParameters.isNotEmpty) {
|
|
writeComma();
|
|
}
|
|
writeSymbol('{');
|
|
writeList(node.namedParameters, visitNamedType);
|
|
writeSymbol('}');
|
|
}
|
|
writeSymbol(')');
|
|
ensureSpace();
|
|
write('→');
|
|
writeNullability(node.nullability);
|
|
writeSpace();
|
|
writeType(node.returnType);
|
|
}
|
|
|
|
void writeBody(Statement body) {
|
|
if (body is Block) {
|
|
if (body.scope case Scope scope?) {
|
|
writeScope(scope);
|
|
}
|
|
endLine(' {');
|
|
++indentation;
|
|
body.statements.forEach(writeNode);
|
|
--indentation;
|
|
writeIndentation();
|
|
endLine('}');
|
|
} else {
|
|
endLine();
|
|
++indentation;
|
|
writeNode(body);
|
|
--indentation;
|
|
}
|
|
}
|
|
|
|
void writeReturnType(DartType type, String? annotation) {
|
|
writeSpaced('→');
|
|
writeAnnotatedType(type, annotation);
|
|
}
|
|
|
|
void writeTypeParameterList(List<TypeParameter> typeParameters) {
|
|
if (typeParameters.isEmpty) return;
|
|
writeSymbol('<');
|
|
writeList(typeParameters, writeNode);
|
|
writeSymbol('>');
|
|
state = WORD; // Ensure space if not followed by another symbol.
|
|
}
|
|
|
|
void writeStructuralParameterList(List<StructuralParameter> typeParameters) {
|
|
if (typeParameters.isEmpty) return;
|
|
writeSymbol('<');
|
|
writeList(typeParameters, writeNode);
|
|
writeSymbol('>');
|
|
state = WORD; // Ensure space if not followed by another symbol.
|
|
}
|
|
|
|
void writeParameterList(
|
|
List<VariableDeclaration> positional,
|
|
List<VariableDeclaration> named,
|
|
int requiredParameterCount,
|
|
) {
|
|
writeSymbol('(');
|
|
writeList(
|
|
positional.take(requiredParameterCount),
|
|
writeVariableDeclaration,
|
|
);
|
|
if (requiredParameterCount < positional.length) {
|
|
if (requiredParameterCount > 0) {
|
|
writeComma();
|
|
}
|
|
writeSymbol('[');
|
|
writeList(
|
|
positional.skip(requiredParameterCount),
|
|
writeVariableDeclaration,
|
|
);
|
|
writeSymbol(']');
|
|
}
|
|
if (named.isNotEmpty) {
|
|
if (positional.isNotEmpty) {
|
|
writeComma();
|
|
}
|
|
writeSymbol('{');
|
|
writeList(named, writeVariableDeclaration);
|
|
writeSymbol('}');
|
|
}
|
|
writeSymbol(')');
|
|
}
|
|
|
|
void writeList<T>(
|
|
Iterable<T> nodes,
|
|
void callback(T x), {
|
|
String separator = ',',
|
|
}) {
|
|
bool first = true;
|
|
for (T node in nodes) {
|
|
if (first) {
|
|
first = false;
|
|
} else {
|
|
writeComma(separator);
|
|
}
|
|
callback(node);
|
|
}
|
|
}
|
|
|
|
void writeClassReferenceFromReference(Reference reference) {
|
|
writeWord(getClassReferenceFromReference(reference));
|
|
}
|
|
|
|
String getClassReferenceFromReference(Reference reference) {
|
|
if (reference.node != null) return getClassReference(reference.asClass);
|
|
if (reference.canonicalName != null) {
|
|
return getCanonicalNameString(reference.canonicalName!);
|
|
}
|
|
throw "Neither node nor canonical name found";
|
|
}
|
|
|
|
void writeExtensionTypeDeclarationReferenceFromReference(
|
|
Reference reference,
|
|
) {
|
|
writeWord(getExtensionTypeDeclarationReferenceFromReference(reference));
|
|
}
|
|
|
|
String getExtensionTypeDeclarationReferenceFromReference(
|
|
Reference reference,
|
|
) {
|
|
if (reference.node != null) {
|
|
return getExtensionTypeDeclarationReference(
|
|
reference.asExtensionTypeDeclaration,
|
|
);
|
|
}
|
|
if (reference.canonicalName != null) {
|
|
return getCanonicalNameString(reference.canonicalName!);
|
|
}
|
|
throw "Neither node nor canonical name found";
|
|
}
|
|
|
|
void writeMemberReferenceFromReference(Reference? reference) {
|
|
writeWord(getMemberReferenceFromReference(reference));
|
|
}
|
|
|
|
String getMemberReferenceFromReference(Reference? reference) {
|
|
if (reference == null) return '<No Member>';
|
|
if (reference.node != null) return getMemberReference(reference.asMember);
|
|
if (reference.canonicalName != null) {
|
|
return getCanonicalNameString(reference.canonicalName!);
|
|
}
|
|
throw "Neither node nor canonical name found";
|
|
}
|
|
|
|
String getCanonicalNameString(CanonicalName name) {
|
|
if (name.isRoot) throw 'unexpected root';
|
|
if (name.name.startsWith('@')) throw 'unexpected @';
|
|
|
|
String libraryString(CanonicalName lib) {
|
|
if (lib.reference.node != null) {
|
|
return getLibraryReference(lib.reference);
|
|
}
|
|
return syntheticNames.nameCanonicalNameAsLibraryPrefix(
|
|
lib.reference,
|
|
lib,
|
|
);
|
|
}
|
|
|
|
String classString(CanonicalName cls) =>
|
|
libraryString(cls.parent!) + '::' + cls.name;
|
|
|
|
if (name.parent!.isRoot) return libraryString(name);
|
|
if (name.parent!.parent!.isRoot) return classString(name);
|
|
|
|
CanonicalName atNode = name.parent!;
|
|
while (!atNode.name.startsWith('@')) {
|
|
atNode = atNode.parent!;
|
|
}
|
|
|
|
String parent = "";
|
|
if (atNode.parent!.parent!.isRoot) {
|
|
parent = libraryString(atNode.parent!);
|
|
} else {
|
|
parent = classString(atNode.parent!);
|
|
}
|
|
|
|
if (name.name == '') return "$parent::$emptyNameString";
|
|
return "$parent::${name.name}";
|
|
}
|
|
|
|
void writeTypedefReference(Typedef typedefNode) {
|
|
writeWord(getTypedefReference(typedefNode));
|
|
}
|
|
|
|
void writeVariableReference(VariableDeclaration variable) {
|
|
final bool highlight = shouldHighlight(variable);
|
|
if (highlight) {
|
|
startHighlight(variable);
|
|
}
|
|
writeWord(getVariableReference(variable));
|
|
if (highlight) {
|
|
endHighlight(variable);
|
|
}
|
|
}
|
|
|
|
void writeTypeParameterReference(TypeParameter node) {
|
|
writeWord(getTypeParameterReference(node));
|
|
}
|
|
|
|
void writeStructuralParameterReference(StructuralParameter node) {
|
|
writeWord(getStructuralParameterReference(node));
|
|
}
|
|
|
|
void writeExpression(Expression node, [int? minimumPrecedence]) {
|
|
final bool highlight = shouldHighlight(node);
|
|
if (highlight) {
|
|
startHighlight(node);
|
|
}
|
|
if (showOffsets) writeWord("[${node.fileOffset}]");
|
|
bool needsParentheses = false;
|
|
if (minimumPrecedence != null && getPrecedence(node) < minimumPrecedence) {
|
|
needsParentheses = true;
|
|
writeSymbol('(');
|
|
}
|
|
writeNode(node);
|
|
if (needsParentheses) {
|
|
writeSymbol(')');
|
|
}
|
|
if (highlight) {
|
|
endHighlight(node);
|
|
}
|
|
}
|
|
|
|
void writeAnnotation(Expression node) {
|
|
writeSymbol('@');
|
|
if (node is ConstructorInvocation) {
|
|
writeMemberReferenceFromReference(node.targetReference);
|
|
visitArguments(node.arguments);
|
|
} else {
|
|
writeExpression(node);
|
|
}
|
|
}
|
|
|
|
void writeAnnotationList(
|
|
List<Expression> nodes, {
|
|
bool separateLines = true,
|
|
}) {
|
|
for (Expression node in nodes) {
|
|
if (separateLines) {
|
|
writeIndentation();
|
|
}
|
|
writeAnnotation(node);
|
|
if (separateLines) {
|
|
endLine();
|
|
} else {
|
|
writeSpace();
|
|
}
|
|
}
|
|
}
|
|
|
|
void writeScope(Scope scope, {String separator = ','}) {
|
|
endLine('/* scope=[');
|
|
++indentation;
|
|
for (VariableContext context in scope.contexts) {
|
|
writeVariableContext(context, separator: separator);
|
|
}
|
|
--indentation;
|
|
writeIndentation();
|
|
writeWord('] */');
|
|
}
|
|
|
|
void writeVariableContext(VariableContext context, {String separator = ','}) {
|
|
writeIndentation();
|
|
writeWord('${syntheticNames.nameVariableContext(context)}:');
|
|
switch (context.captureKind) {
|
|
case CaptureKind.directCaptured:
|
|
writeWord('direct-captured');
|
|
case CaptureKind.notCaptured:
|
|
writeWord('not-captured');
|
|
case CaptureKind.assertCaptured:
|
|
writeWord('assert-captured');
|
|
}
|
|
ensureSpace();
|
|
if (context.variables.isEmpty) {
|
|
writeSymbol('VariableContext([])');
|
|
return;
|
|
}
|
|
endLine('VariableContext([');
|
|
++indentation;
|
|
context.variables.forEach(writeNode);
|
|
--indentation;
|
|
writeIndentation();
|
|
endLine('])${separator}');
|
|
}
|
|
|
|
@override
|
|
void visitLocalVariable(LocalVariable node) {
|
|
writeIndentation();
|
|
writeExpressionVariable(node);
|
|
endLine(';');
|
|
}
|
|
|
|
@override
|
|
void visitPositionalParameter(PositionalParameter node) {
|
|
writeIndentation();
|
|
writeExpressionVariable(node);
|
|
endLine(';');
|
|
}
|
|
|
|
@override
|
|
void visitNamedParameter(NamedParameter node) {
|
|
writeIndentation();
|
|
writeExpressionVariable(node);
|
|
endLine(';');
|
|
}
|
|
|
|
@override
|
|
void visitThisVariable(ThisVariable node) {
|
|
writeIndentation();
|
|
writeExpressionVariable(node);
|
|
endLine(';');
|
|
}
|
|
|
|
@override
|
|
void visitCatchVariable(CatchVariable node) {
|
|
writeIndentation();
|
|
writeExpressionVariable(node);
|
|
endLine(';');
|
|
}
|
|
|
|
@override
|
|
void visitSyntheticVariable(SyntheticVariable node) {
|
|
writeIndentation();
|
|
writeExpressionVariable(node);
|
|
endLine(';');
|
|
}
|
|
|
|
void writeExpressionVariable(VariableDeclaration node) {
|
|
// TODO(cstefantsova): Printer of the new variables is broken.
|
|
if (node is LegacyVariable && node is! FunctionParameter) {
|
|
writeVariableDeclaration(node);
|
|
} else {
|
|
if (showOffsets) writeWord("[${node.fileOffset}]");
|
|
if (showMetadata) writeMetadata(node);
|
|
|
|
switch (node) {
|
|
case LocalVariable():
|
|
writeWord('local-variable');
|
|
case PositionalParameter():
|
|
writeWord('positional-parameter');
|
|
case NamedParameter():
|
|
writeWord('named-parameter');
|
|
case ThisVariable():
|
|
writeWord('this-variable');
|
|
case SyntheticVariable():
|
|
writeWord('synthetic-variable');
|
|
case LegacyVariable():
|
|
writeWord('variable-declaration');
|
|
case CatchVariable():
|
|
writeWord('catch-variable');
|
|
case VariableInitialization():
|
|
writeWord('variable-initialization');
|
|
}
|
|
|
|
// TODO(cstefantsova): Should [Variable]s have annotations?
|
|
// writeAnnotationList(node.annotations, separateLines: false);
|
|
if (node.hasIsLowered) {
|
|
writeModifier(node.isLowered, 'lowered');
|
|
}
|
|
if (node.hasIsLate) {
|
|
writeModifier(node.isLate, 'late');
|
|
}
|
|
if (node.hasIsRequired) {
|
|
writeModifier(node.isRequired, 'required');
|
|
}
|
|
if (node.hasIsCovariantByDeclaration) {
|
|
writeModifier(
|
|
node.isCovariantByDeclaration,
|
|
'covariant-by-declaration',
|
|
);
|
|
}
|
|
if (node.hasIsCovariantByClass) {
|
|
writeModifier(node.isCovariantByClass, 'covariant-by-class');
|
|
}
|
|
if (node.hasIsFinal) {
|
|
writeModifier(node.isFinal, 'final');
|
|
}
|
|
if (node.hasIsConst) {
|
|
writeModifier(node.isConst, 'const');
|
|
}
|
|
if (node.hasIsSynthesized) {
|
|
writeModifier(
|
|
node.isSynthesized && node.cosmeticName != null,
|
|
'synthesized',
|
|
);
|
|
}
|
|
if (node.hasIsHoisted) {
|
|
writeModifier(node.isHoisted, 'hoisted');
|
|
}
|
|
if (node.hasIsWildcard) {
|
|
writeModifier(node.isWildcard, 'wildcard');
|
|
}
|
|
if (node.hasIsErroneouslyInitialized) {
|
|
writeModifier(node.isErroneouslyInitialized, 'erroneously-initialized');
|
|
}
|
|
// TODO(cstefantsova): Adapt [Annotator] for [Variable]s.
|
|
// writeAnnotatedType(node.type, annotator?.annotateVariable(this, node));
|
|
writeWord(getVariableName(node));
|
|
}
|
|
}
|
|
|
|
@override
|
|
void visitLibrary(Library node) {}
|
|
|
|
@override
|
|
void visitField(Field node) {
|
|
writeAnnotationList(node.annotations);
|
|
writeIndentation();
|
|
writeModifier(node.isEnumElement, 'enum-element');
|
|
writeModifier(node.isLate, 'late');
|
|
writeModifier(node.isStatic, 'static');
|
|
writeModifier(node.isCovariantByDeclaration, 'covariant-by-declaration');
|
|
writeModifier(node.isCovariantByClass, 'covariant-by-class');
|
|
writeModifier(node.isFinal, 'final');
|
|
writeModifier(node.isConst, 'const');
|
|
writeModifier(node.isErroneous, 'erroneous');
|
|
// Only show implicit getter/setter modifiers in cases where they are
|
|
// out of the ordinary.
|
|
if (node.isFinal) {
|
|
writeModifier(node.hasSetter, '[setter]');
|
|
}
|
|
writeWord('field');
|
|
writeSpace();
|
|
writeAnnotatedType(node.type, annotator?.annotateField(this, node));
|
|
writeName(getMemberName(node), showLibrary: true);
|
|
if (node.scope case Scope scope?) {
|
|
writeScope(scope);
|
|
}
|
|
Expression? initializer = node.initializer;
|
|
if (initializer != null) {
|
|
writeSpaced('=');
|
|
writeExpression(initializer);
|
|
}
|
|
List<String> features = <String>[];
|
|
Class? enclosingClass = node.enclosingClass;
|
|
if ((enclosingClass == null &&
|
|
node.enclosingLibrary.fileUri != node.fileUri) ||
|
|
(enclosingClass != null && enclosingClass.fileUri != node.fileUri)) {
|
|
features.add(" from ${node.fileUri} ");
|
|
}
|
|
if (features.isNotEmpty) {
|
|
writeWord("/*${features.join(',')}*/");
|
|
}
|
|
endLine(';');
|
|
}
|
|
|
|
@override
|
|
void visitProcedure(Procedure node) {
|
|
writeAnnotationList(node.annotations);
|
|
writeIndentation();
|
|
writeModifier(node.isExternal, 'external');
|
|
writeModifier(node.isStatic, 'static');
|
|
writeModifier(node.isAbstract, 'abstract');
|
|
writeModifier(node.isForwardingStub, 'forwarding-stub');
|
|
writeModifier(node.isForwardingSemiStub, 'forwarding-semi-stub');
|
|
writeModifier(node.isExtensionMember, 'extension-member');
|
|
writeModifier(node.isExtensionTypeMember, 'extension-type-member');
|
|
writeModifier(node.isSynthetic, 'synthetic');
|
|
writeModifier(node.isConst, 'const');
|
|
writeModifier(node.isErroneous, 'erroneous');
|
|
writeModifier(node.hasExternalEffectPragma, 'external-effect');
|
|
switch (node.stubKind) {
|
|
case ProcedureStubKind.Regular:
|
|
case ProcedureStubKind.AbstractForwardingStub:
|
|
case ProcedureStubKind.ConcreteForwardingStub:
|
|
break;
|
|
case ProcedureStubKind.NoSuchMethodForwarder:
|
|
writeWord('no-such-method-forwarder');
|
|
break;
|
|
case ProcedureStubKind.MemberSignature:
|
|
writeWord('member-signature');
|
|
break;
|
|
case ProcedureStubKind.AbstractMixinStub:
|
|
writeWord('mixin-stub');
|
|
break;
|
|
case ProcedureStubKind.ConcreteMixinStub:
|
|
writeWord('mixin-super-stub');
|
|
break;
|
|
case ProcedureStubKind.RepresentationField:
|
|
writeWord('representation-field');
|
|
break;
|
|
}
|
|
writeWord(procedureKindToString(node.kind));
|
|
List<String> features = <String>[];
|
|
Class? enclosingClass = node.enclosingClass;
|
|
if ((enclosingClass == null &&
|
|
node.enclosingLibrary.fileUri != node.fileUri) ||
|
|
(enclosingClass != null && enclosingClass.fileUri != node.fileUri)) {
|
|
features.add(" from ${node.fileUri} ");
|
|
}
|
|
if (features.isNotEmpty) {
|
|
writeWord("/*${features.join(',')}*/");
|
|
}
|
|
if (node.signatureType != null) {
|
|
writeWord('/* signature-type:');
|
|
writeType(node.signatureType!);
|
|
writeWord('*/');
|
|
}
|
|
switch (node.stubKind) {
|
|
case ProcedureStubKind.Regular:
|
|
case ProcedureStubKind.AbstractForwardingStub:
|
|
case ProcedureStubKind.ConcreteForwardingStub:
|
|
case ProcedureStubKind.NoSuchMethodForwarder:
|
|
case ProcedureStubKind.ConcreteMixinStub:
|
|
case ProcedureStubKind.RepresentationField:
|
|
writeFunction(node.function, name: getMemberName(node));
|
|
break;
|
|
case ProcedureStubKind.MemberSignature:
|
|
case ProcedureStubKind.AbstractMixinStub:
|
|
writeFunction(
|
|
node.function,
|
|
name: getMemberName(node),
|
|
terminateLine: false,
|
|
);
|
|
if (node.function.body is ReturnStatement) {
|
|
writeSymbol(';');
|
|
}
|
|
writeSymbol(' -> ');
|
|
writeMemberReferenceFromReference(node.stubTargetReference!);
|
|
endLine();
|
|
break;
|
|
}
|
|
}
|
|
|
|
@override
|
|
void visitConstructor(Constructor node) {
|
|
writeAnnotationList(node.annotations);
|
|
writeIndentation();
|
|
writeModifier(node.isExternal, 'external');
|
|
writeModifier(node.isConst, 'const');
|
|
writeModifier(node.isSynthetic, 'synthetic');
|
|
writeModifier(node.isErroneous, 'erroneous');
|
|
writeWord('constructor');
|
|
List<String> features = <String>[];
|
|
if (node.enclosingClass.fileUri != node.fileUri) {
|
|
features.add(" from ${node.fileUri} ");
|
|
}
|
|
if (features.isNotEmpty) {
|
|
writeWord("/*${features.join(',')}*/");
|
|
}
|
|
writeFunction(
|
|
node.function,
|
|
name: node.name,
|
|
initializers: node.initializers,
|
|
);
|
|
}
|
|
|
|
@override
|
|
void visitClass(Class node) {
|
|
writeAnnotationList(node.annotations);
|
|
writeIndentation();
|
|
writeModifier(node.isAbstract, 'abstract');
|
|
writeModifier(node.isMacro, 'macro');
|
|
writeModifier(node.isSealed, 'sealed');
|
|
writeModifier(node.isBase, 'base');
|
|
writeModifier(node.isInterface, 'interface');
|
|
writeModifier(node.isFinal, 'final');
|
|
writeModifier(node.isMixinClass, 'mixin');
|
|
writeWord('class');
|
|
writeWord(getClassName(node));
|
|
writeTypeParameterList(node.typeParameters);
|
|
if (node.isMixinApplication) {
|
|
writeSpaced('=');
|
|
visitSupertype(node.supertype!);
|
|
writeSpaced('with');
|
|
visitSupertype(node.mixedInType!);
|
|
} else if (node.supertype != null) {
|
|
writeSpaced('extends');
|
|
visitSupertype(node.supertype!);
|
|
}
|
|
if (node.implementedTypes.isNotEmpty) {
|
|
writeSpaced('implements');
|
|
writeList(node.implementedTypes, visitSupertype);
|
|
}
|
|
List<String> features = <String>[];
|
|
if (node.isEnum) {
|
|
features.add('isEnum');
|
|
}
|
|
if (node.isAnonymousMixin) {
|
|
features.add('isAnonymousMixin');
|
|
}
|
|
if (node.isEliminatedMixin) {
|
|
features.add('isEliminatedMixin');
|
|
}
|
|
if (node.isMixinDeclaration) {
|
|
features.add('isMixinDeclaration');
|
|
}
|
|
if (node.hasConstConstructor) {
|
|
features.add('hasConstConstructor');
|
|
}
|
|
if (features.isNotEmpty) {
|
|
writeSpaced('/*${features.join(',')}*/');
|
|
}
|
|
String endLineString = ' {';
|
|
if (node.enclosingLibrary.fileUri != node.fileUri) {
|
|
endLineString += ' // from ${node.fileUri}';
|
|
}
|
|
endLine(endLineString);
|
|
++indentation;
|
|
node.fields.forEach(writeNode);
|
|
node.constructors.forEach(writeNode);
|
|
node.procedures.forEach(writeNode);
|
|
--indentation;
|
|
writeIndentation();
|
|
endLine('}');
|
|
}
|
|
|
|
@override
|
|
void visitExtension(Extension node) {
|
|
writeAnnotationList(node.annotations);
|
|
writeIndentation();
|
|
writeWord('extension');
|
|
if (node.isUnnamedExtension) {
|
|
writeWord('/* unnamed */');
|
|
}
|
|
writeWord(getExtensionName(node));
|
|
writeTypeParameterList(node.typeParameters);
|
|
writeSpaced('on');
|
|
writeType(node.onType);
|
|
|
|
String endLineString = ' {';
|
|
if (node.enclosingLibrary.fileUri != node.fileUri) {
|
|
endLineString += ' // from ${node.fileUri}';
|
|
}
|
|
|
|
endLine(endLineString);
|
|
++indentation;
|
|
node.memberDescriptors.forEach((ExtensionMemberDescriptor descriptor) {
|
|
void writeReference(Reference reference, {required bool isTearOff}) {
|
|
writeIndentation();
|
|
writeModifier(descriptor.isStatic, 'static');
|
|
writeModifier(descriptor.isInternalImplementation, 'impl');
|
|
switch (descriptor.kind) {
|
|
case ExtensionMemberKind.Method:
|
|
writeWord('method');
|
|
break;
|
|
case ExtensionMemberKind.Getter:
|
|
writeWord('get');
|
|
break;
|
|
case ExtensionMemberKind.Setter:
|
|
writeWord('set');
|
|
break;
|
|
case ExtensionMemberKind.Operator:
|
|
writeWord('operator');
|
|
break;
|
|
case ExtensionMemberKind.Field:
|
|
writeWord('field');
|
|
break;
|
|
}
|
|
if (isTearOff) {
|
|
writeWord('tearoff');
|
|
}
|
|
writeName(descriptor.name);
|
|
writeSpaced('=');
|
|
Member member = reference.asMember;
|
|
if (member is Procedure) {
|
|
if (member.isGetter) {
|
|
writeWord('get');
|
|
} else if (member.isSetter) {
|
|
writeWord('set');
|
|
}
|
|
}
|
|
writeMemberReferenceFromReference(reference);
|
|
endLine(';');
|
|
}
|
|
|
|
final Reference? memberReference = descriptor.memberReference;
|
|
if (memberReference != null) {
|
|
writeReference(memberReference, isTearOff: false);
|
|
}
|
|
final Reference? tearOffReference = descriptor.tearOffReference;
|
|
if (tearOffReference != null) {
|
|
writeReference(tearOffReference, isTearOff: true);
|
|
}
|
|
});
|
|
--indentation;
|
|
writeIndentation();
|
|
endLine('}');
|
|
}
|
|
|
|
@override
|
|
void visitExtensionTypeDeclaration(ExtensionTypeDeclaration node) {
|
|
writeAnnotationList(node.annotations);
|
|
writeIndentation();
|
|
writeWord('extension type');
|
|
writeWord(getExtensionTypeDeclarationName(node));
|
|
writeTypeParameterList(node.typeParameters);
|
|
writeSymbol('(');
|
|
writeType(node.declaredRepresentationType);
|
|
writeWord(node.representationName);
|
|
writeSymbol(')');
|
|
if (node.implements.isNotEmpty) {
|
|
writeSpaced('implements');
|
|
writeList(node.implements, writeType);
|
|
}
|
|
String endLineString = ' {';
|
|
if (node.enclosingLibrary.fileUri != node.fileUri) {
|
|
endLineString += ' // from ${node.fileUri}';
|
|
}
|
|
|
|
endLine(endLineString);
|
|
++indentation;
|
|
node.procedures.forEach(writeNode);
|
|
node.memberDescriptors.forEach((ExtensionTypeMemberDescriptor descriptor) {
|
|
void writeReference(Reference reference, {required bool isTearOff}) {
|
|
writeIndentation();
|
|
writeModifier(descriptor.isStatic, 'static');
|
|
writeModifier(descriptor.isInternalImplementation, 'impl');
|
|
switch (descriptor.kind) {
|
|
case ExtensionTypeMemberKind.Constructor:
|
|
writeWord('constructor');
|
|
break;
|
|
case ExtensionTypeMemberKind.Factory:
|
|
writeWord('factory');
|
|
break;
|
|
case ExtensionTypeMemberKind.RedirectingFactory:
|
|
writeWord('redirecting-factory');
|
|
break;
|
|
case ExtensionTypeMemberKind.Method:
|
|
writeWord('method');
|
|
break;
|
|
case ExtensionTypeMemberKind.Getter:
|
|
writeWord('get');
|
|
break;
|
|
case ExtensionTypeMemberKind.Setter:
|
|
writeWord('set');
|
|
break;
|
|
case ExtensionTypeMemberKind.Operator:
|
|
writeWord('operator');
|
|
break;
|
|
case ExtensionTypeMemberKind.Field:
|
|
writeWord('field');
|
|
break;
|
|
}
|
|
if (isTearOff) {
|
|
writeWord('tearoff');
|
|
}
|
|
writeName(descriptor.name);
|
|
writeSpaced('=');
|
|
Member member = reference.asMember;
|
|
if (member is Procedure) {
|
|
if (member.isGetter) {
|
|
writeWord('get');
|
|
} else if (member.isSetter) {
|
|
writeWord('set');
|
|
}
|
|
}
|
|
writeMemberReferenceFromReference(reference);
|
|
endLine(';');
|
|
}
|
|
|
|
final Reference? memberReference = descriptor.memberReference;
|
|
if (memberReference != null) {
|
|
writeReference(memberReference, isTearOff: false);
|
|
}
|
|
final Reference? tearOffReference = descriptor.tearOffReference;
|
|
if (tearOffReference != null) {
|
|
writeReference(tearOffReference, isTearOff: true);
|
|
}
|
|
});
|
|
--indentation;
|
|
writeIndentation();
|
|
endLine('}');
|
|
}
|
|
|
|
@override
|
|
void visitTypedef(Typedef node) {
|
|
writeAnnotationList(node.annotations);
|
|
writeIndentation();
|
|
writeWord('typedef');
|
|
if (node.enclosingLibrary.fileUri != node.fileUri) {
|
|
writeWord("/* from ${node.fileUri} */");
|
|
}
|
|
writeWord(node.name);
|
|
writeTypeParameterList(node.typeParameters);
|
|
writeSpaced('=');
|
|
DartType? type = node.type;
|
|
if (type is FunctionType) {
|
|
writeFunctionType(type);
|
|
} else {
|
|
writeNode(type);
|
|
}
|
|
endLine(';');
|
|
}
|
|
|
|
@override
|
|
void visitInvalidExpression(InvalidExpression node) {
|
|
writeWord('invalid-expression');
|
|
writeWord('"${escapeString(node.message)}"');
|
|
if (node.expression != null) {
|
|
writeSpaced('in');
|
|
writeNode(node.expression!);
|
|
}
|
|
}
|
|
|
|
void _writeDynamicAccessKind(DynamicAccessKind kind) {
|
|
switch (kind) {
|
|
case DynamicAccessKind.Dynamic:
|
|
writeSymbol('{dynamic}');
|
|
break;
|
|
case DynamicAccessKind.Never:
|
|
writeSymbol('{Never}');
|
|
break;
|
|
case DynamicAccessKind.Invalid:
|
|
writeSymbol('{<invalid>}');
|
|
break;
|
|
case DynamicAccessKind.Unresolved:
|
|
writeSymbol('{<unresolved>}');
|
|
break;
|
|
}
|
|
}
|
|
|
|
@override
|
|
void visitDynamicInvocation(DynamicInvocation node) {
|
|
writeExpression(node.receiver, Precedence.PRIMARY);
|
|
_writeDynamicAccessKind(node.kind);
|
|
if (!node.isImplicitCall) {
|
|
writeSymbol('.');
|
|
writeName(node.name);
|
|
}
|
|
writeNode(node.arguments);
|
|
}
|
|
|
|
void _writeFunctionAccessKind(FunctionAccessKind kind) {
|
|
switch (kind) {
|
|
case FunctionAccessKind.Function:
|
|
case FunctionAccessKind.FunctionType:
|
|
break;
|
|
case FunctionAccessKind.Inapplicable:
|
|
writeSymbol('{<inapplicable>}.');
|
|
break;
|
|
case FunctionAccessKind.Nullable:
|
|
writeSymbol('{<nullable>}.');
|
|
break;
|
|
}
|
|
}
|
|
|
|
@override
|
|
void visitFunctionInvocation(FunctionInvocation node) {
|
|
writeExpression(node.receiver, Precedence.PRIMARY);
|
|
_writeFunctionAccessKind(node.kind);
|
|
writeNode(node.arguments);
|
|
if (node.functionType != null) {
|
|
writeSymbol('{');
|
|
writeType(node.functionType!);
|
|
writeSymbol('}');
|
|
}
|
|
}
|
|
|
|
@override
|
|
void visitLocalFunctionInvocation(LocalFunctionInvocation node) {
|
|
writeVariableReference(node.variable);
|
|
writeNode(node.arguments);
|
|
writeSymbol('{');
|
|
writeType(node.functionType);
|
|
writeSymbol('}');
|
|
}
|
|
|
|
void _writeInstanceAccessKind(InstanceAccessKind kind) {
|
|
switch (kind) {
|
|
case InstanceAccessKind.Instance:
|
|
break;
|
|
case InstanceAccessKind.Object:
|
|
writeSymbol('{<object>}.');
|
|
break;
|
|
case InstanceAccessKind.Inapplicable:
|
|
writeSymbol('{<inapplicable>}.');
|
|
break;
|
|
case InstanceAccessKind.Nullable:
|
|
writeSymbol('{<nullable>}.');
|
|
break;
|
|
}
|
|
}
|
|
|
|
@override
|
|
void visitInstanceInvocation(InstanceInvocation node) {
|
|
writeExpression(node.receiver, Precedence.PRIMARY);
|
|
writeSymbol('.');
|
|
writeInterfaceTarget(node.name, node.interfaceTargetReference);
|
|
_writeInstanceAccessKind(node.kind);
|
|
List<String> flags = <String>[];
|
|
if (node.isInvariant) {
|
|
flags.add('Invariant');
|
|
}
|
|
if (node.isBoundsSafe) {
|
|
flags.add('BoundsSafe');
|
|
}
|
|
if (flags.isNotEmpty) {
|
|
write('{${flags.join(',')}}');
|
|
}
|
|
writeNode(node.arguments);
|
|
writeSymbol('{');
|
|
writeType(node.functionType);
|
|
writeSymbol('}');
|
|
}
|
|
|
|
@override
|
|
void visitInstanceGetterInvocation(InstanceGetterInvocation node) {
|
|
writeExpression(node.receiver, Precedence.PRIMARY);
|
|
writeSymbol('.');
|
|
writeInterfaceTarget(node.name, node.interfaceTargetReference);
|
|
_writeInstanceAccessKind(node.kind);
|
|
writeNode(node.arguments);
|
|
if (node.functionType != null) {
|
|
writeSymbol('{');
|
|
writeType(node.functionType!);
|
|
writeSymbol('}');
|
|
}
|
|
}
|
|
|
|
@override
|
|
void visitEqualsCall(EqualsCall node) {
|
|
int precedence = Precedence.EQUALITY;
|
|
writeExpression(node.left, precedence);
|
|
writeSpace();
|
|
writeSymbol('==');
|
|
writeInterfaceTarget(Name.equalsName, node.interfaceTargetReference);
|
|
writeSymbol('{');
|
|
writeType(node.functionType);
|
|
writeSymbol('}');
|
|
writeSpace();
|
|
writeExpression(node.right, precedence + 1);
|
|
}
|
|
|
|
@override
|
|
void visitEqualsNull(EqualsNull node) {
|
|
writeExpression(node.expression, Precedence.EQUALITY);
|
|
writeSpace();
|
|
writeSymbol('==');
|
|
writeSpace();
|
|
writeSymbol('null');
|
|
}
|
|
|
|
@override
|
|
void visitSuperMethodInvocation(SuperMethodInvocation node) {
|
|
writeWord('super');
|
|
if (node.receiver is! ThisExpression) {
|
|
writeSymbol('{');
|
|
writeNode(node.receiver);
|
|
writeSymbol('}');
|
|
}
|
|
writeSymbol('.');
|
|
writeInterfaceTarget(node.name, node.interfaceTargetReference);
|
|
writeNode(node.arguments);
|
|
}
|
|
|
|
@override
|
|
void visitStaticInvocation(StaticInvocation node) {
|
|
writeModifier(node.isConst, 'const');
|
|
writeMemberReferenceFromReference(node.targetReference);
|
|
writeNode(node.arguments);
|
|
}
|
|
|
|
@override
|
|
void visitConstructorInvocation(ConstructorInvocation node) {
|
|
writeWord(node.isConst ? 'const' : 'new');
|
|
writeMemberReferenceFromReference(node.targetReference);
|
|
writeNode(node.arguments);
|
|
}
|
|
|
|
@override
|
|
void visitRedirectingFactoryInvocation(RedirectingFactoryInvocation node) {
|
|
write('/*original=');
|
|
writeMemberReferenceFromReference(node.redirectingFactoryTargetReference);
|
|
write('*/');
|
|
writeNode(node.expression);
|
|
}
|
|
|
|
@override
|
|
void visitNot(Not node) {
|
|
writeSymbol('!');
|
|
writeExpression(node.operand, Precedence.PREFIX);
|
|
}
|
|
|
|
@override
|
|
void visitNullCheck(NullCheck node) {
|
|
writeExpression(node.operand, Precedence.POSTFIX);
|
|
writeSymbol('!');
|
|
}
|
|
|
|
@override
|
|
void visitLogicalExpression(LogicalExpression node) {
|
|
int precedence =
|
|
Precedence.binaryPrecedence[logicalExpressionOperatorToString(
|
|
node.operatorEnum,
|
|
)]!;
|
|
writeExpression(node.left, precedence);
|
|
writeSpaced(logicalExpressionOperatorToString(node.operatorEnum));
|
|
writeExpression(node.right, precedence + 1);
|
|
}
|
|
|
|
@override
|
|
void visitConditionalExpression(ConditionalExpression node) {
|
|
writeExpression(node.condition, Precedence.LOGICAL_OR);
|
|
ensureSpace();
|
|
write('?');
|
|
writeStaticType(node.staticType);
|
|
writeSpace();
|
|
writeExpression(node.then);
|
|
writeSpaced(':');
|
|
writeExpression(node.otherwise);
|
|
}
|
|
|
|
@override
|
|
void visitStringConcatenation(StringConcatenation node) {
|
|
if (state == WORD) {
|
|
writeSpace();
|
|
}
|
|
write('"');
|
|
for (Expression part in node.expressions) {
|
|
if (part is StringLiteral) {
|
|
writeSymbol(escapeString(part.value));
|
|
} else {
|
|
writeSymbol(r'${');
|
|
writeExpression(part);
|
|
writeSymbol('}');
|
|
}
|
|
}
|
|
write('"');
|
|
state = WORD;
|
|
}
|
|
|
|
@override
|
|
void visitListConcatenation(ListConcatenation node) {
|
|
bool first = true;
|
|
for (Expression part in node.lists) {
|
|
if (!first) writeSpaced('+');
|
|
writeExpression(part);
|
|
first = false;
|
|
}
|
|
}
|
|
|
|
@override
|
|
void visitSetConcatenation(SetConcatenation node) {
|
|
bool first = true;
|
|
for (Expression part in node.sets) {
|
|
if (!first) writeSpaced('+');
|
|
writeExpression(part);
|
|
first = false;
|
|
}
|
|
}
|
|
|
|
@override
|
|
void visitMapConcatenation(MapConcatenation node) {
|
|
bool first = true;
|
|
for (Expression part in node.maps) {
|
|
if (!first) writeSpaced('+');
|
|
writeExpression(part);
|
|
first = false;
|
|
}
|
|
}
|
|
|
|
@override
|
|
void visitInstanceCreation(InstanceCreation node) {
|
|
writeClassReferenceFromReference(node.classReference);
|
|
if (node.typeArguments.isNotEmpty) {
|
|
writeSymbol('<');
|
|
writeList(node.typeArguments, writeType);
|
|
writeSymbol('>');
|
|
}
|
|
writeSymbol('{');
|
|
bool first = true;
|
|
node.fieldValues.forEach((Reference fieldRef, Expression value) {
|
|
if (!first) {
|
|
writeComma();
|
|
}
|
|
writeWord('${fieldRef.asField.name.text}');
|
|
writeSymbol(':');
|
|
writeExpression(value);
|
|
first = false;
|
|
});
|
|
for (AssertStatement assert_ in node.asserts) {
|
|
if (!first) {
|
|
writeComma();
|
|
}
|
|
write('assert(');
|
|
writeExpression(assert_.condition);
|
|
Expression? message = assert_.message;
|
|
if (message != null) {
|
|
writeComma();
|
|
writeExpression(message);
|
|
}
|
|
write(')');
|
|
first = false;
|
|
}
|
|
for (Expression unusedArgument in node.unusedArguments) {
|
|
if (!first) {
|
|
writeComma();
|
|
}
|
|
writeExpression(unusedArgument);
|
|
first = false;
|
|
}
|
|
writeSymbol('}');
|
|
}
|
|
|
|
@override
|
|
void visitFileUriExpression(FileUriExpression node) {
|
|
writeWord('/* from ${node.fileUri} */');
|
|
writeExpression(node.expression);
|
|
}
|
|
|
|
@override
|
|
void visitIsExpression(IsExpression node) {
|
|
writeExpression(node.operand, Precedence.BITWISE_OR);
|
|
writeSpaced('is');
|
|
writeType(node.type);
|
|
}
|
|
|
|
@override
|
|
void visitAsExpression(AsExpression node) {
|
|
writeExpression(node.operand, Precedence.BITWISE_OR);
|
|
List<String> flags = <String>[];
|
|
if (node.isTypeError) {
|
|
flags.add('TypeError');
|
|
}
|
|
if (node.isCovarianceCheck) {
|
|
flags.add('CovarianceCheck');
|
|
}
|
|
if (node.isForDynamic) {
|
|
flags.add('ForDynamic');
|
|
}
|
|
if (node.isUnchecked) {
|
|
flags.add('Unchecked');
|
|
}
|
|
writeSpaced(flags.isNotEmpty ? 'as{${flags.join(',')}}' : 'as');
|
|
writeType(node.type);
|
|
}
|
|
|
|
@override
|
|
void visitSymbolLiteral(SymbolLiteral node) {
|
|
writeSymbol('#');
|
|
writeWord(node.value);
|
|
}
|
|
|
|
@override
|
|
void visitTypeLiteral(TypeLiteral node) {
|
|
writeType(node.type);
|
|
}
|
|
|
|
@override
|
|
void visitThisExpression(ThisExpression node) {
|
|
writeWord('this');
|
|
}
|
|
|
|
@override
|
|
void visitRethrow(Rethrow node) {
|
|
writeWord('rethrow');
|
|
}
|
|
|
|
@override
|
|
void visitThrow(Throw node) {
|
|
if (node.forErrorHandling) {
|
|
writeWord('throw{for-error-handling}');
|
|
} else {
|
|
writeWord('throw');
|
|
}
|
|
writeSpace();
|
|
writeExpression(node.expression);
|
|
}
|
|
|
|
@override
|
|
void visitListLiteral(ListLiteral node) {
|
|
if (node.isConst) {
|
|
writeWord('const');
|
|
writeSpace();
|
|
}
|
|
writeSymbol('<');
|
|
writeType(node.typeArgument);
|
|
writeSymbol('>');
|
|
writeSymbol('[');
|
|
writeList(node.expressions, writeNode);
|
|
writeSymbol(']');
|
|
}
|
|
|
|
@override
|
|
void visitSetLiteral(SetLiteral node) {
|
|
if (node.isConst) {
|
|
writeWord('const');
|
|
writeSpace();
|
|
}
|
|
writeSymbol('<');
|
|
writeType(node.typeArgument);
|
|
writeSymbol('>');
|
|
writeSymbol('{');
|
|
writeList(node.expressions, writeNode);
|
|
writeSymbol('}');
|
|
}
|
|
|
|
@override
|
|
void visitMapLiteral(MapLiteral node) {
|
|
if (node.isConst) {
|
|
writeWord('const');
|
|
writeSpace();
|
|
}
|
|
writeSymbol('<');
|
|
writeList([node.keyType, node.valueType], writeType);
|
|
writeSymbol('>');
|
|
writeSymbol('{');
|
|
writeList(node.entries, writeNode);
|
|
writeSymbol('}');
|
|
}
|
|
|
|
@override
|
|
void visitMapLiteralEntry(MapLiteralEntry node) {
|
|
writeExpression(node.key);
|
|
writeComma(':');
|
|
writeExpression(node.value);
|
|
}
|
|
|
|
@override
|
|
void visitRecordLiteral(RecordLiteral node) {
|
|
if (node.isConst) {
|
|
writeWord('const');
|
|
writeSpace();
|
|
}
|
|
writeSymbol('(');
|
|
writeList(node.positional, writeNode);
|
|
if (node.named.isNotEmpty) {
|
|
if (node.positional.isNotEmpty) writeComma();
|
|
writeSymbol('{');
|
|
writeList(node.named, writeNode);
|
|
writeSymbol('}');
|
|
}
|
|
writeSymbol(')');
|
|
}
|
|
|
|
@override
|
|
void visitAwaitExpression(AwaitExpression node) {
|
|
writeWord('await');
|
|
writeExpression(node.operand);
|
|
if (node.runtimeCheckType != null) {
|
|
writeSpaced("/* runtimeCheckType=");
|
|
writeNode(node.runtimeCheckType);
|
|
writeSpaced("*/");
|
|
}
|
|
}
|
|
|
|
@override
|
|
void visitFunctionExpression(FunctionExpression node) {
|
|
writeFunction(node.function, terminateLine: false);
|
|
}
|
|
|
|
@override
|
|
void visitStringLiteral(StringLiteral node) {
|
|
writeWord('"${escapeString(node.value)}"');
|
|
}
|
|
|
|
@override
|
|
void visitIntLiteral(IntLiteral node) {
|
|
writeWord('${node.value}');
|
|
}
|
|
|
|
@override
|
|
void visitDoubleLiteral(DoubleLiteral node) {
|
|
writeWord('${node.value}');
|
|
}
|
|
|
|
@override
|
|
void visitBoolLiteral(BoolLiteral node) {
|
|
writeWord('${node.value}');
|
|
}
|
|
|
|
@override
|
|
void visitNullLiteral(NullLiteral node) {
|
|
writeWord('null');
|
|
}
|
|
|
|
@override
|
|
void visitLet(Let node) {
|
|
writeWord('let');
|
|
writeVariableDeclaration(node.variable);
|
|
writeSpaced('in');
|
|
writeExpression(node.body);
|
|
}
|
|
|
|
@override
|
|
void visitBlockExpression(BlockExpression node) {
|
|
writeSpaced('block');
|
|
writeBlockBody(node.body.statements, asExpression: true);
|
|
writeSymbol(' =>');
|
|
writeExpression(node.value);
|
|
}
|
|
|
|
@override
|
|
void visitInstantiation(Instantiation node) {
|
|
writeExpression(node.expression, Precedence.TYPE_LITERAL);
|
|
writeSymbol('<');
|
|
writeList(node.typeArguments, writeType);
|
|
writeSymbol('>');
|
|
}
|
|
|
|
@override
|
|
void visitLoadLibrary(LoadLibrary node) {
|
|
writeWord('LoadLibrary');
|
|
writeSymbol('(');
|
|
writeWord(node.import.name!);
|
|
writeSymbol(')');
|
|
state = WORD;
|
|
}
|
|
|
|
@override
|
|
void visitCheckLibraryIsLoaded(CheckLibraryIsLoaded node) {
|
|
writeWord('CheckLibraryIsLoaded');
|
|
writeSymbol('(');
|
|
writeWord(node.import.name!);
|
|
writeSymbol(')');
|
|
state = WORD;
|
|
}
|
|
|
|
@override
|
|
void visitLibraryPart(LibraryPart node) {
|
|
writeAnnotationList(node.annotations);
|
|
writeIndentation();
|
|
writeWord('part');
|
|
writeWord(node.partUri);
|
|
endLine(";");
|
|
}
|
|
|
|
@override
|
|
void visitLibraryDependency(LibraryDependency node) {
|
|
writeAnnotationList(node.annotations);
|
|
writeIndentation();
|
|
writeWord(node.isImport ? 'import' : 'export');
|
|
String uriString;
|
|
if (node.importedLibraryReference.node != null) {
|
|
uriString = '${node.targetLibrary.importUri}';
|
|
} else {
|
|
uriString = '${node.importedLibraryReference.canonicalName?.name}';
|
|
}
|
|
writeWord('"$uriString"');
|
|
if (node.isDeferred) {
|
|
writeWord('deferred');
|
|
}
|
|
String? name = node.name;
|
|
if (name != null) {
|
|
writeWord('as');
|
|
writeWord(name);
|
|
}
|
|
String? last;
|
|
final String show = 'show';
|
|
final String hide = 'hide';
|
|
if (node.combinators.isNotEmpty) {
|
|
for (Combinator combinator in node.combinators) {
|
|
if (combinator.isShow && last != show) {
|
|
last = show;
|
|
writeWord(show);
|
|
} else if (combinator.isHide && last != hide) {
|
|
last = hide;
|
|
writeWord(hide);
|
|
}
|
|
|
|
bool first = true;
|
|
for (String name in combinator.names) {
|
|
if (!first) writeComma();
|
|
writeWord(name);
|
|
first = false;
|
|
}
|
|
}
|
|
}
|
|
endLine(';');
|
|
}
|
|
|
|
@override
|
|
void defaultExpression(Expression node) {
|
|
writeWord('${node.runtimeType}');
|
|
}
|
|
|
|
@override
|
|
void visitVariableGet(VariableGet node) {
|
|
writeVariableReference(node.variable);
|
|
DartType? promotedType = node.promotedType;
|
|
if (promotedType != null) {
|
|
writeSymbol('{');
|
|
writeNode(promotedType);
|
|
writeSymbol('}');
|
|
state = WORD;
|
|
}
|
|
}
|
|
|
|
@override
|
|
void visitVariableSet(VariableSet node) {
|
|
writeVariableReference(node.variable);
|
|
writeSpaced('=');
|
|
writeExpression(node.value);
|
|
}
|
|
|
|
void writeInterfaceTarget(Name name, Reference? target) {
|
|
if (target != null) {
|
|
writeSymbol('{');
|
|
writeMemberReferenceFromReference(target);
|
|
writeSymbol('}');
|
|
} else {
|
|
writeName(name);
|
|
}
|
|
}
|
|
|
|
void writeStaticType(DartType type) {
|
|
writeSymbol('{');
|
|
writeType(type);
|
|
writeSymbol('}');
|
|
}
|
|
|
|
@override
|
|
void visitDynamicGet(DynamicGet node) {
|
|
writeExpression(node.receiver, Precedence.PRIMARY);
|
|
_writeDynamicAccessKind(node.kind);
|
|
writeSymbol('.');
|
|
writeName(node.name);
|
|
}
|
|
|
|
@override
|
|
void visitFunctionTearOff(FunctionTearOff node) {
|
|
writeExpression(node.receiver, Precedence.PRIMARY);
|
|
writeSymbol('.');
|
|
writeSymbol('call');
|
|
}
|
|
|
|
@override
|
|
void visitInstanceGet(InstanceGet node) {
|
|
writeExpression(node.receiver, Precedence.PRIMARY);
|
|
writeSymbol('.');
|
|
writeInterfaceTarget(node.name, node.interfaceTargetReference);
|
|
_writeInstanceAccessKind(node.kind);
|
|
writeSymbol('{');
|
|
writeType(node.resultType);
|
|
writeSymbol('}');
|
|
}
|
|
|
|
@override
|
|
void visitInstanceTearOff(InstanceTearOff node) {
|
|
writeExpression(node.receiver, Precedence.PRIMARY);
|
|
writeSymbol('.');
|
|
writeInterfaceTarget(node.name, node.interfaceTargetReference);
|
|
_writeInstanceAccessKind(node.kind);
|
|
writeSymbol('{');
|
|
writeType(node.resultType);
|
|
writeSymbol('}');
|
|
}
|
|
|
|
@override
|
|
void visitDynamicSet(DynamicSet node) {
|
|
writeExpression(node.receiver, Precedence.PRIMARY);
|
|
writeSymbol('.');
|
|
_writeDynamicAccessKind(node.kind);
|
|
writeName(node.name);
|
|
writeSpaced('=');
|
|
writeExpression(node.value);
|
|
}
|
|
|
|
@override
|
|
void visitInstanceSet(InstanceSet node) {
|
|
writeExpression(node.receiver, Precedence.PRIMARY);
|
|
writeSymbol('.');
|
|
writeInterfaceTarget(node.name, node.interfaceTargetReference);
|
|
_writeInstanceAccessKind(node.kind);
|
|
writeSpaced('=');
|
|
writeExpression(node.value);
|
|
}
|
|
|
|
@override
|
|
void visitSuperPropertyGet(SuperPropertyGet node) {
|
|
writeWord('super');
|
|
if (node.receiver is! ThisExpression) {
|
|
writeSymbol('{');
|
|
writeNode(node.receiver);
|
|
writeSymbol('}');
|
|
}
|
|
writeSymbol('.');
|
|
writeInterfaceTarget(node.name, node.interfaceTargetReference);
|
|
}
|
|
|
|
@override
|
|
void visitSuperPropertySet(SuperPropertySet node) {
|
|
writeWord('super');
|
|
if (node.receiver is! ThisExpression) {
|
|
writeSymbol('{');
|
|
writeNode(node.receiver);
|
|
writeSymbol('}');
|
|
}
|
|
writeSymbol('.');
|
|
writeInterfaceTarget(node.name, node.interfaceTargetReference);
|
|
writeSpaced('=');
|
|
writeExpression(node.value);
|
|
}
|
|
|
|
@override
|
|
void visitStaticTearOff(StaticTearOff node) {
|
|
writeMemberReferenceFromReference(node.targetReference);
|
|
}
|
|
|
|
@override
|
|
void visitStaticGet(StaticGet node) {
|
|
writeMemberReferenceFromReference(node.targetReference);
|
|
}
|
|
|
|
@override
|
|
void visitStaticSet(StaticSet node) {
|
|
writeMemberReferenceFromReference(node.targetReference);
|
|
writeSpaced('=');
|
|
writeExpression(node.value);
|
|
}
|
|
|
|
@override
|
|
void visitConstructorTearOff(ConstructorTearOff node) {
|
|
writeMemberReferenceFromReference(node.targetReference);
|
|
}
|
|
|
|
@override
|
|
void visitRedirectingFactoryTearOff(RedirectingFactoryTearOff node) {
|
|
writeMemberReferenceFromReference(node.targetReference);
|
|
}
|
|
|
|
@override
|
|
void visitTypedefTearOff(TypedefTearOff node) {
|
|
writeStructuralParameterList(node.structuralParameters);
|
|
state = SYMBOL;
|
|
writeSymbol('.(');
|
|
writeNode(node.expression);
|
|
if (node.typeArguments.isNotEmpty) {
|
|
writeSymbol('<');
|
|
writeList(node.typeArguments, writeType);
|
|
writeSymbol('>');
|
|
}
|
|
writeSymbol(')');
|
|
state = WORD;
|
|
}
|
|
|
|
@override
|
|
void visitRecordIndexGet(RecordIndexGet node) {
|
|
writeExpression(node.receiver, Precedence.PRIMARY);
|
|
writeSymbol('.\$${node.index + 1}');
|
|
writeSymbol('{');
|
|
writeType(node.receiverType.positional[node.index]);
|
|
writeSymbol('}');
|
|
}
|
|
|
|
@override
|
|
void visitRecordNameGet(RecordNameGet node) {
|
|
writeExpression(node.receiver, Precedence.PRIMARY);
|
|
writeSymbol('.${node.name}');
|
|
writeSymbol('{');
|
|
// TODO(johnniwinther): Should we store the result type in the node?
|
|
writeType(
|
|
node.receiverType.named
|
|
.singleWhere((element) => element.name == node.name)
|
|
.type,
|
|
);
|
|
writeSymbol('}');
|
|
}
|
|
|
|
@override
|
|
void visitExpressionStatement(ExpressionStatement node) {
|
|
writeIndentation();
|
|
writeExpression(node.expression);
|
|
endLine(';');
|
|
}
|
|
|
|
void writeBlockBody(List<Statement> statements, {bool asExpression = false}) {
|
|
if (statements.isEmpty) {
|
|
asExpression ? writeSymbol('{}') : endLine('{}');
|
|
return;
|
|
}
|
|
endLine('{');
|
|
++indentation;
|
|
statements.forEach(writeNode);
|
|
--indentation;
|
|
writeIndentation();
|
|
asExpression ? writeSymbol('}') : endLine('}');
|
|
}
|
|
|
|
@override
|
|
void visitBlock(Block node) {
|
|
writeIndentation();
|
|
if (node.scope case Scope scope?) {
|
|
writeScope(scope);
|
|
}
|
|
writeBlockBody(node.statements);
|
|
}
|
|
|
|
@override
|
|
void visitAssertBlock(AssertBlock node) {
|
|
writeIndentation();
|
|
writeSpaced('assert');
|
|
writeBlockBody(node.statements);
|
|
}
|
|
|
|
@override
|
|
void visitEmptyStatement(EmptyStatement node) {
|
|
writeIndentation();
|
|
endLine(';');
|
|
}
|
|
|
|
@override
|
|
void visitAssertStatement(AssertStatement node, {bool asExpression = false}) {
|
|
if (!asExpression) {
|
|
writeIndentation();
|
|
}
|
|
writeWord('assert');
|
|
writeSymbol('(');
|
|
writeExpression(node.condition);
|
|
Expression? message = node.message;
|
|
if (message != null) {
|
|
writeComma();
|
|
writeExpression(message);
|
|
}
|
|
if (!asExpression) {
|
|
endLine(');');
|
|
} else {
|
|
writeSymbol(')');
|
|
}
|
|
}
|
|
|
|
@override
|
|
void visitLabeledStatement(LabeledStatement node) {
|
|
writeIndentation();
|
|
writeWord(syntheticNames.nameLabeledStatement(node));
|
|
endLine(':');
|
|
writeNode(node.body);
|
|
}
|
|
|
|
@override
|
|
void visitBreakStatement(BreakStatement node) {
|
|
writeIndentation();
|
|
writeWord('break');
|
|
writeWord(syntheticNames.nameLabeledStatement(node.target));
|
|
endLine(';');
|
|
}
|
|
|
|
@override
|
|
void visitWhileStatement(WhileStatement node) {
|
|
writeIndentation();
|
|
writeSpaced('while');
|
|
writeSymbol('(');
|
|
writeExpression(node.condition);
|
|
writeSymbol(')');
|
|
writeBody(node.body);
|
|
}
|
|
|
|
@override
|
|
void visitDoStatement(DoStatement node) {
|
|
writeIndentation();
|
|
writeWord('do');
|
|
writeBody(node.body);
|
|
writeIndentation();
|
|
writeSpaced('while');
|
|
writeSymbol('(');
|
|
writeExpression(node.condition);
|
|
endLine(')');
|
|
}
|
|
|
|
@override
|
|
void visitForStatement(ForStatement node) {
|
|
writeIndentation();
|
|
writeSpaced('for');
|
|
if (node.scope case Scope scope?) {
|
|
writeScope(scope);
|
|
ensureSpace();
|
|
}
|
|
writeSymbol('(');
|
|
writeList(node.variables, writeVariableStatement);
|
|
writeComma(';');
|
|
Expression? condition = node.condition;
|
|
if (condition != null) {
|
|
writeExpression(condition);
|
|
}
|
|
writeComma(';');
|
|
writeList(node.updates, writeExpression);
|
|
writeSymbol(')');
|
|
writeBody(node.body);
|
|
}
|
|
|
|
@override
|
|
void visitForInStatement(ForInStatement node) {
|
|
writeIndentation();
|
|
if (node.isAsync) {
|
|
writeSpaced('await');
|
|
}
|
|
writeSpaced('for');
|
|
if (node.scope case Scope scope?) {
|
|
writeScope(scope);
|
|
ensureSpace();
|
|
}
|
|
writeSymbol('(');
|
|
if (node.variable case LegacyVariable variable) {
|
|
writeVariableDeclaration(variable, useVarKeyword: true);
|
|
} else {
|
|
writeExpressionVariable(node.variable);
|
|
}
|
|
writeSpaced('in');
|
|
writeExpression(node.iterable);
|
|
writeSymbol(')');
|
|
writeBody(node.body);
|
|
}
|
|
|
|
@override
|
|
void visitSwitchStatement(SwitchStatement node) {
|
|
writeIndentation();
|
|
writeWord('switch');
|
|
writeSymbol('(');
|
|
writeExpression(node.expression);
|
|
writeSymbol(')');
|
|
writeWord(" /*");
|
|
if (node.isExplicitlyExhaustive) {
|
|
writeWord("isExplicitlyExhaustive,");
|
|
}
|
|
writeNode(node.expressionTypeInternal);
|
|
writeWord("*/");
|
|
endLine(' {');
|
|
++indentation;
|
|
node.cases.forEach(writeNode);
|
|
--indentation;
|
|
writeIndentation();
|
|
endLine('}');
|
|
}
|
|
|
|
@override
|
|
void visitSwitchCase(SwitchCase node) {
|
|
String label = syntheticNames.nameSwitchCase(node);
|
|
writeIndentation();
|
|
writeWord(label);
|
|
endLine(':');
|
|
for (Expression expression in node.expressions) {
|
|
writeIndentation();
|
|
writeWord('case');
|
|
writeExpression(expression);
|
|
endLine(':');
|
|
}
|
|
if (node.isDefault) {
|
|
writeIndentation();
|
|
writeWord('default');
|
|
endLine(':');
|
|
}
|
|
++indentation;
|
|
writeNode(node.body);
|
|
--indentation;
|
|
}
|
|
|
|
@override
|
|
void visitContinueSwitchStatement(ContinueSwitchStatement node) {
|
|
writeIndentation();
|
|
writeWord('continue');
|
|
writeWord(syntheticNames.nameSwitchCase(node.target));
|
|
endLine(';');
|
|
}
|
|
|
|
@override
|
|
void visitIfStatement(IfStatement node) {
|
|
writeIndentation();
|
|
writeWord('if');
|
|
writeSymbol('(');
|
|
writeExpression(node.condition);
|
|
writeSymbol(')');
|
|
writeBody(node.then);
|
|
Statement? otherwise = node.otherwise;
|
|
if (otherwise != null) {
|
|
writeIndentation();
|
|
writeWord('else');
|
|
writeBody(otherwise);
|
|
}
|
|
}
|
|
|
|
@override
|
|
void visitReturnStatement(ReturnStatement node) {
|
|
writeIndentation();
|
|
writeWord('return');
|
|
Expression? expression = node.expression;
|
|
if (expression != null) {
|
|
writeSpace();
|
|
writeExpression(expression);
|
|
}
|
|
endLine(';');
|
|
}
|
|
|
|
@override
|
|
void visitTryCatch(TryCatch node) {
|
|
writeIndentation();
|
|
writeWord('try');
|
|
writeBody(node.body);
|
|
node.catches.forEach(writeNode);
|
|
}
|
|
|
|
@override
|
|
void visitCatch(Catch node) {
|
|
writeIndentation();
|
|
writeWord('on');
|
|
writeType(node.guard);
|
|
writeSpace();
|
|
writeWord('catch');
|
|
writeSymbol('(');
|
|
VariableDeclaration? exception = node.exception;
|
|
if (exception != null) {
|
|
writeExpressionVariable(exception);
|
|
} else {
|
|
writeWord('no-exception-var');
|
|
}
|
|
VariableDeclaration? stackTrace = node.stackTrace;
|
|
if (stackTrace != null) {
|
|
writeComma();
|
|
writeExpressionVariable(stackTrace);
|
|
}
|
|
writeSymbol(')');
|
|
if (node.scope case Scope scope?) {
|
|
writeScope(scope);
|
|
}
|
|
writeBody(node.body);
|
|
}
|
|
|
|
@override
|
|
void visitTryFinally(TryFinally node) {
|
|
writeIndentation();
|
|
writeWord('try');
|
|
writeBody(node.body);
|
|
writeIndentation();
|
|
writeWord('finally');
|
|
writeBody(node.finalizer);
|
|
}
|
|
|
|
@override
|
|
void visitYieldStatement(YieldStatement node) {
|
|
writeIndentation();
|
|
if (node.isYieldStar) {
|
|
writeWord('yield*');
|
|
} else {
|
|
writeWord('yield');
|
|
}
|
|
writeExpression(node.expression);
|
|
endLine(';');
|
|
}
|
|
|
|
@override
|
|
void visitLegacyVariableStatement(LegacyVariableStatement node) {
|
|
writeIndentation();
|
|
writeVariableStatement(node);
|
|
endLine(';');
|
|
}
|
|
|
|
@override
|
|
void visitVariableInitialization(VariableInitialization node) {
|
|
writeIndentation();
|
|
writeVariableStatement(node);
|
|
_writeContexts(node);
|
|
endLine(';');
|
|
}
|
|
|
|
@override
|
|
void visitFunctionDeclaration(FunctionDeclaration node) {
|
|
writeAnnotationList(node.variable.annotations);
|
|
writeIndentation();
|
|
writeWord('function');
|
|
writeModifier(node.variable.isWildcard, 'wildcard');
|
|
writeFunction(node.function, name: getVariableName(node.variable));
|
|
}
|
|
|
|
void writeVariableDeclaration(
|
|
VariableDeclaration node, {
|
|
bool useVarKeyword = false,
|
|
}) {
|
|
switch (node) {
|
|
case LocalVariable():
|
|
case CatchVariable():
|
|
case ThisVariable():
|
|
case SyntheticVariable():
|
|
case PositionalParameter():
|
|
case NamedParameter():
|
|
writeExpressionVariable(node);
|
|
case LegacyVariable():
|
|
if (showOffsets) writeWord("[${node.fileOffset}]");
|
|
if (showMetadata) writeMetadata(node);
|
|
writeAnnotationList(node.annotations, separateLines: false);
|
|
writeModifier(node.isLowered, 'lowered');
|
|
writeModifier(node.isLate, 'late');
|
|
writeModifier(node.isRequired, 'required');
|
|
writeModifier(
|
|
node.isCovariantByDeclaration,
|
|
'covariant-by-declaration',
|
|
);
|
|
writeModifier(node.isCovariantByClass, 'covariant-by-class');
|
|
writeModifier(node.isFinal, 'final');
|
|
writeModifier(node.isConst, 'const');
|
|
writeModifier(node.isSynthesized && node.name != null, 'synthesized');
|
|
writeModifier(node.isHoisted, 'hoisted');
|
|
writeModifier(node.isWildcard, 'wildcard');
|
|
writeModifier(node.isInitializingFormal, 'initializing-formal');
|
|
writeModifier(
|
|
node.isSuperInitializingFormal,
|
|
'super-initializing-formal',
|
|
);
|
|
writeModifier(node.isErroneouslyInitialized, 'erroneously-initialized');
|
|
bool hasImplicitInitializer =
|
|
node.initializer is NullLiteral ||
|
|
(node.initializer is ConstantExpression &&
|
|
(node.initializer as ConstantExpression).constant
|
|
is NullConstant);
|
|
if ((node.initializer == null || hasImplicitInitializer) &&
|
|
node.hasDeclaredInitializer) {
|
|
writeModifier(
|
|
node.hasDeclaredInitializer,
|
|
'has-declared-initializer',
|
|
);
|
|
} else if (node.initializer != null &&
|
|
!hasImplicitInitializer &&
|
|
!node.hasDeclaredInitializer) {
|
|
writeModifier(
|
|
node.hasDeclaredInitializer,
|
|
'has-no-declared-initializer',
|
|
);
|
|
}
|
|
writeAnnotatedType(node.type, annotator?.annotateVariable(this, node));
|
|
writeWord(getVariableName(node));
|
|
Expression? initializer = node.initializer;
|
|
if (initializer != null) {
|
|
writeSpaced('=');
|
|
writeExpression(initializer);
|
|
}
|
|
}
|
|
}
|
|
|
|
void writeVariableStatement(VariableStatement node) {
|
|
VariableDeclaration variable = node.variable;
|
|
if (node is VariableInitialization) {
|
|
if (showOffsets) writeWord("[${node.fileOffset}]");
|
|
if (showMetadata) writeMetadata(node);
|
|
writeAnnotationList(node.annotations, separateLines: false);
|
|
writeModifier(node.isErroneouslyInitialized, 'erroneously-initialized');
|
|
bool hasImplicitInitializer =
|
|
variable.initializer is NullLiteral ||
|
|
(variable.initializer is ConstantExpression &&
|
|
(variable.initializer as ConstantExpression).constant
|
|
is NullConstant);
|
|
if ((variable.initializer == null || hasImplicitInitializer) &&
|
|
variable.hasDeclaredInitializer) {
|
|
writeModifier(
|
|
variable.hasDeclaredInitializer,
|
|
'has-declared-initializer',
|
|
);
|
|
} else if (variable.initializer != null &&
|
|
!hasImplicitInitializer &&
|
|
!variable.hasDeclaredInitializer) {
|
|
writeModifier(
|
|
variable.hasDeclaredInitializer,
|
|
'has-no-declared-initializer',
|
|
);
|
|
}
|
|
writeWord(getVariableName(variable.variable));
|
|
Expression? initializer = variable.initializer;
|
|
if (initializer != null) {
|
|
writeSpaced(':=');
|
|
writeExpression(initializer);
|
|
}
|
|
} else {
|
|
writeVariableDeclaration(variable);
|
|
}
|
|
}
|
|
|
|
@override
|
|
void visitArguments(Arguments node) {
|
|
if (node.types.isNotEmpty) {
|
|
writeSymbol('<');
|
|
writeList(node.types, writeType);
|
|
writeSymbol('>');
|
|
}
|
|
writeSymbol('(');
|
|
Iterable<TreeNode> allArgs = <List<TreeNode>>[
|
|
node.positional,
|
|
node.named,
|
|
].expand((x) => x);
|
|
writeList(allArgs, writeNode);
|
|
writeSymbol(')');
|
|
}
|
|
|
|
@override
|
|
void visitNamedExpression(NamedExpression node) {
|
|
writeWord(node.name);
|
|
writeComma(':');
|
|
writeExpression(node.value);
|
|
}
|
|
|
|
@override
|
|
void defaultStatement(Statement node) {
|
|
writeIndentation();
|
|
endLine('${node.runtimeType}');
|
|
}
|
|
|
|
@override
|
|
void visitInvalidInitializer(InvalidInitializer node) {
|
|
writeWord('invalid-initializer');
|
|
writeWord('"${escapeString(node.message)}"');
|
|
}
|
|
|
|
@override
|
|
void visitFieldInitializer(FieldInitializer node) {
|
|
writeMemberReferenceFromReference(node.fieldReference);
|
|
writeSpaced('=');
|
|
writeExpression(node.value);
|
|
}
|
|
|
|
@override
|
|
void visitSuperInitializer(SuperInitializer node) {
|
|
writeWord('super');
|
|
writeMemberReferenceFromReference(node.targetReference);
|
|
writeNode(node.arguments);
|
|
}
|
|
|
|
@override
|
|
void visitRedirectingInitializer(RedirectingInitializer node) {
|
|
writeWord('this');
|
|
writeMemberReferenceFromReference(node.targetReference);
|
|
writeNode(node.arguments);
|
|
}
|
|
|
|
@override
|
|
void visitLocalInitializer(LocalInitializer node) {
|
|
writeVariableDeclaration(node.variable);
|
|
}
|
|
|
|
@override
|
|
void visitAssertInitializer(AssertInitializer node) {
|
|
visitAssertStatement(node.statement, asExpression: true);
|
|
}
|
|
|
|
@override
|
|
void defaultInitializer(Initializer node) {
|
|
writeIndentation();
|
|
endLine(': ${node.runtimeType}');
|
|
}
|
|
|
|
void writeNullability(Nullability nullability, {bool inComment = false}) {
|
|
switch (nullability) {
|
|
case Nullability.nullable:
|
|
writeSymbol('?');
|
|
if (!inComment) {
|
|
state = WORD; // Disallow a word immediately after the '?'.
|
|
}
|
|
break;
|
|
case Nullability.undetermined:
|
|
writeSymbol('%');
|
|
if (!inComment) {
|
|
state = WORD; // Disallow a word immediately after the '%'.
|
|
}
|
|
break;
|
|
case Nullability.nonNullable:
|
|
if (inComment) {
|
|
writeSymbol("!");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void writeDartTypeNullability(DartType type, {bool inComment = false}) {
|
|
if (type is InvalidType) {
|
|
writeNullability(Nullability.undetermined);
|
|
} else {
|
|
writeNullability(type.nullability, inComment: inComment);
|
|
}
|
|
}
|
|
|
|
@override
|
|
void visitInvalidType(InvalidType node) {
|
|
writeWord('invalid-type');
|
|
}
|
|
|
|
@override
|
|
void visitDynamicType(DynamicType node) {
|
|
writeWord('dynamic');
|
|
}
|
|
|
|
@override
|
|
void visitVoidType(VoidType node) {
|
|
writeWord('void');
|
|
}
|
|
|
|
@override
|
|
void visitNeverType(NeverType node) {
|
|
writeWord('Never');
|
|
writeNullability(node.nullability);
|
|
}
|
|
|
|
@override
|
|
void visitNullType(NullType node) {
|
|
writeWord('Null');
|
|
}
|
|
|
|
@override
|
|
void visitInterfaceType(InterfaceType node) {
|
|
writeClassReferenceFromReference(node.classReference);
|
|
if (node.typeArguments.isNotEmpty) {
|
|
writeSymbol('<');
|
|
writeList(node.typeArguments, writeType);
|
|
writeSymbol('>');
|
|
state = WORD; // Disallow a word immediately after the '>'.
|
|
}
|
|
writeNullability(node.nullability);
|
|
}
|
|
|
|
@override
|
|
void visitExtensionType(ExtensionType node) {
|
|
writeExtensionTypeDeclarationReferenceFromReference(
|
|
node.extensionTypeDeclarationReference,
|
|
);
|
|
if (node.typeArguments.isNotEmpty) {
|
|
writeSymbol('<');
|
|
writeList(node.typeArguments, writeType);
|
|
writeSymbol('>');
|
|
state = Printer.WORD;
|
|
}
|
|
writeNullability(node.nullability);
|
|
|
|
writeSpace();
|
|
write("/* erasure=");
|
|
writeType(node.extensionTypeErasure);
|
|
|
|
if (node.nullability != node.declaredNullability) {
|
|
// The following line prints the nullability of the extension type derived
|
|
// from its supertypes. If the extension type declaration implements
|
|
// `Object` directly or indirectly, such nullability is
|
|
// `Nullability.nonNullable`, otherwise it's `Nullability.undetermined`.
|
|
//
|
|
// The nullability derived from the supertypes will be combined with the
|
|
// nullability of the type declared by the programmer (by appending or
|
|
// omitting `?`) to compute the overall nullability of the type, which is
|
|
// returned by the `nullability` getter.
|
|
//
|
|
// Since the getter for computing the nullability derived from the
|
|
// supertypes isn't exposed publicly, we compute it as the overall
|
|
// nullability of the current type taken as if the programmer omitted the
|
|
// nullability marker `?` on the type.
|
|
write(", declared=");
|
|
writeNullability(node.declaredNullability, inComment: true);
|
|
writeSpace();
|
|
}
|
|
writeWord("*/");
|
|
}
|
|
|
|
@override
|
|
void visitFutureOrType(FutureOrType node) {
|
|
writeWord('FutureOr');
|
|
writeSymbol('<');
|
|
writeNode(node.typeArgument);
|
|
writeSymbol('>');
|
|
writeNullability(node.declaredNullability);
|
|
}
|
|
|
|
@override
|
|
void visitFunctionType(FunctionType node) {
|
|
writeFunctionType(node);
|
|
}
|
|
|
|
@override
|
|
void visitRecordType(RecordType node) {
|
|
writeSymbol('(');
|
|
writeList(node.positional, writeType);
|
|
if (node.positional.isNotEmpty && node.named.isNotEmpty) {
|
|
writeComma(',');
|
|
}
|
|
if (node.named.isNotEmpty) {
|
|
writeSymbol('{');
|
|
writeList(node.named, writeNode);
|
|
writeSymbol('}');
|
|
}
|
|
writeSymbol(')');
|
|
writeNullability(node.declaredNullability);
|
|
// Disallow a word immediately after the record type.
|
|
state = WORD;
|
|
}
|
|
|
|
@override
|
|
void visitNamedType(NamedType node) {
|
|
writeModifier(node.isRequired, 'required');
|
|
writeWord(node.name);
|
|
writeSymbol(':');
|
|
writeSpace();
|
|
writeType(node.type);
|
|
}
|
|
|
|
@override
|
|
void visitTypeParameterType(TypeParameterType node) {
|
|
writeTypeParameterReference(node.parameter);
|
|
writeNullability(node.declaredNullability);
|
|
}
|
|
|
|
@override
|
|
void visitStructuralParameterType(StructuralParameterType node) {
|
|
writeStructuralParameterReference(node.parameter);
|
|
writeNullability(node.declaredNullability);
|
|
}
|
|
|
|
@override
|
|
void visitIntersectionType(IntersectionType node) {
|
|
writeType(node.left);
|
|
writeSpaced('&');
|
|
writeType(node.right);
|
|
|
|
writeWord("/* '");
|
|
writeDartTypeNullability(node.left, inComment: true);
|
|
writeWord("' & '");
|
|
writeDartTypeNullability(node.right, inComment: true);
|
|
writeWord("' = '");
|
|
writeNullability(node.nullability, inComment: true);
|
|
writeWord("' */");
|
|
}
|
|
|
|
@override
|
|
void visitTypeParameter(TypeParameter node) {
|
|
writeModifier(node.isCovariantByClass, 'covariant-by-class');
|
|
writeAnnotationList(node.annotations, separateLines: false);
|
|
if (node.variance != Variance.covariant) {
|
|
writeWord(node.variance.name);
|
|
}
|
|
writeWord(getTypeParameterName(node));
|
|
writeSpaced('extends');
|
|
writeType(node.bound);
|
|
if (node.defaultType != node.bound) {
|
|
writeSpaced('=');
|
|
writeType(node.defaultType);
|
|
}
|
|
}
|
|
|
|
@override
|
|
void visitStructuralParameter(StructuralParameter node) {
|
|
if (node.variance != Variance.covariant) {
|
|
writeWord(node.variance.name);
|
|
}
|
|
writeWord(getStructuralParameterName(node));
|
|
writeSpaced('extends');
|
|
writeType(node.bound);
|
|
if (node.defaultType != node.bound) {
|
|
writeSpaced('=');
|
|
writeType(node.defaultType);
|
|
}
|
|
}
|
|
|
|
void writeConstantReference(Constant node) {
|
|
writeWord(syntheticNames.nameConstant(node));
|
|
}
|
|
|
|
@override
|
|
void visitConstantExpression(ConstantExpression node) {
|
|
if (node is FileUriConstantExpression) {
|
|
writeWord('/* from ${node.fileUri} */');
|
|
}
|
|
writeConstantReference(node.constant);
|
|
}
|
|
|
|
@override
|
|
void defaultConstant(Constant node) {
|
|
writeIndentation();
|
|
writeConstantReference(node);
|
|
writeSpaced('=');
|
|
endLine('${node.runtimeType}');
|
|
}
|
|
|
|
@override
|
|
void visitNullConstant(NullConstant node) {
|
|
writeIndentation();
|
|
writeConstantReference(node);
|
|
writeSpaced('=');
|
|
endLine('${node.value}');
|
|
}
|
|
|
|
@override
|
|
void visitBoolConstant(BoolConstant node) {
|
|
writeIndentation();
|
|
writeConstantReference(node);
|
|
writeSpaced('=');
|
|
endLine('${node.value}');
|
|
}
|
|
|
|
@override
|
|
void visitIntConstant(IntConstant node) {
|
|
writeIndentation();
|
|
writeConstantReference(node);
|
|
writeSpaced('=');
|
|
endLine('${node.value}');
|
|
}
|
|
|
|
@override
|
|
void visitDoubleConstant(DoubleConstant node) {
|
|
writeIndentation();
|
|
writeConstantReference(node);
|
|
writeSpaced('=');
|
|
endLine('${node.value}');
|
|
}
|
|
|
|
@override
|
|
void visitSymbolConstant(SymbolConstant node) {
|
|
writeIndentation();
|
|
writeConstantReference(node);
|
|
writeSpaced('=');
|
|
Reference? libraryReference = node.libraryReference;
|
|
String text = libraryReference != null
|
|
? '#${libraryReference.asLibrary.importUri}::${node.name}'
|
|
: '#${node.name}';
|
|
endLine('${text}');
|
|
}
|
|
|
|
@override
|
|
void visitListConstant(ListConstant node) {
|
|
writeIndentation();
|
|
writeConstantReference(node);
|
|
writeSpaced('=');
|
|
writeSymbol('<');
|
|
writeType(node.typeArgument);
|
|
writeSymbol('>[');
|
|
writeList(node.entries, writeConstantReference);
|
|
endLine(']');
|
|
}
|
|
|
|
@override
|
|
void visitSetConstant(SetConstant node) {
|
|
writeIndentation();
|
|
writeConstantReference(node);
|
|
writeSpaced('=');
|
|
writeSymbol('<');
|
|
writeType(node.typeArgument);
|
|
writeSymbol('>{');
|
|
writeList(node.entries, writeConstantReference);
|
|
endLine('}');
|
|
}
|
|
|
|
@override
|
|
void visitMapConstant(MapConstant node) {
|
|
writeIndentation();
|
|
writeConstantReference(node);
|
|
writeSpaced('=');
|
|
writeSymbol('<');
|
|
writeList([node.keyType, node.valueType], writeType);
|
|
writeSymbol('>{');
|
|
writeList(node.entries, (ConstantMapEntry entry) {
|
|
writeConstantReference(entry.key);
|
|
writeSymbol(':');
|
|
writeConstantReference(entry.value);
|
|
});
|
|
endLine('}');
|
|
}
|
|
|
|
@override
|
|
void visitTypeLiteralConstant(TypeLiteralConstant node) {
|
|
writeIndentation();
|
|
writeConstantReference(node);
|
|
writeSpaced('=');
|
|
writeWord('${node.runtimeType}');
|
|
writeSymbol('(');
|
|
writeNode(node.type);
|
|
endLine(')');
|
|
}
|
|
|
|
@override
|
|
void visitInstanceConstant(InstanceConstant node) {
|
|
writeIndentation();
|
|
writeConstantReference(node);
|
|
writeSpaced('=');
|
|
writeClassReferenceFromReference(node.classReference);
|
|
if (!node.typeArguments.isEmpty) {
|
|
writeSymbol('<');
|
|
writeList(node.typeArguments, writeType);
|
|
writeSymbol('>');
|
|
}
|
|
|
|
writeSymbol(' {');
|
|
writeList(node.fieldValues.entries, (MapEntry<Reference, Constant> entry) {
|
|
if (entry.key.node != null) {
|
|
writeWord('${entry.key.asField.name.text}');
|
|
} else {
|
|
writeWord('${entry.key.canonicalName!.name}');
|
|
}
|
|
writeSymbol(':');
|
|
writeConstantReference(entry.value);
|
|
});
|
|
endLine('}');
|
|
}
|
|
|
|
@override
|
|
void visitRecordConstant(RecordConstant node) {
|
|
writeIndentation();
|
|
writeConstantReference(node);
|
|
writeSpaced('=');
|
|
writeSymbol('(');
|
|
writeList(node.positional, writeConstantReference);
|
|
if (node.named.isNotEmpty) {
|
|
if (node.positional.isNotEmpty) writeComma();
|
|
writeSymbol('{');
|
|
writeList(node.named.entries, (MapEntry<String, Constant> entry) {
|
|
writeWord(entry.key);
|
|
writeSymbol(':');
|
|
writeConstantReference(entry.value);
|
|
});
|
|
writeSymbol('}');
|
|
}
|
|
writeSymbol(')');
|
|
endLine();
|
|
}
|
|
|
|
@override
|
|
void visitInstantiationConstant(InstantiationConstant node) {
|
|
writeIndentation();
|
|
writeConstantReference(node);
|
|
writeSpaced('=');
|
|
writeWord('instantiation');
|
|
writeSpace();
|
|
writeConstantReference(node.tearOffConstant);
|
|
writeSpace();
|
|
writeSymbol('<');
|
|
writeList(node.types, writeType);
|
|
writeSymbol('>');
|
|
endLine();
|
|
}
|
|
|
|
@override
|
|
void visitStringConstant(StringConstant node) {
|
|
writeIndentation();
|
|
writeConstantReference(node);
|
|
writeSpaced('=');
|
|
endLine('"${escapeString(node.value)}"');
|
|
}
|
|
|
|
@override
|
|
void visitStaticTearOffConstant(StaticTearOffConstant node) {
|
|
writeIndentation();
|
|
writeConstantReference(node);
|
|
writeSpaced('=');
|
|
writeWord('static-tearoff');
|
|
writeSpace();
|
|
writeMemberReferenceFromReference(node.targetReference);
|
|
endLine();
|
|
}
|
|
|
|
@override
|
|
void visitTypedefTearOffConstant(TypedefTearOffConstant node) {
|
|
writeIndentation();
|
|
writeConstantReference(node);
|
|
writeSpaced('=');
|
|
writeWord('typedef-tearoff');
|
|
writeSpace();
|
|
writeStructuralParameterList(node.parameters);
|
|
state = SYMBOL;
|
|
writeSymbol('.(');
|
|
writeConstantReference(node.tearOffConstant);
|
|
if (node.types.isNotEmpty) {
|
|
writeSymbol('<');
|
|
writeList(node.types, writeType);
|
|
writeSymbol('>');
|
|
}
|
|
writeSymbol(')');
|
|
state = WORD;
|
|
endLine();
|
|
}
|
|
|
|
@override
|
|
void visitUnevaluatedConstant(UnevaluatedConstant node) {
|
|
writeIndentation();
|
|
writeConstantReference(node);
|
|
writeSpaced('=');
|
|
writeSymbol('eval');
|
|
writeSpace();
|
|
writeExpression(node.expression);
|
|
endLine();
|
|
}
|
|
|
|
@override
|
|
void visitConstructorTearOffConstant(ConstructorTearOffConstant node) {
|
|
writeIndentation();
|
|
writeConstantReference(node);
|
|
writeSpaced('=');
|
|
writeWord('constructor-tearoff');
|
|
writeSpace();
|
|
writeMemberReferenceFromReference(node.targetReference);
|
|
endLine();
|
|
}
|
|
|
|
@override
|
|
void visitRedirectingFactoryTearOffConstant(
|
|
RedirectingFactoryTearOffConstant node,
|
|
) {
|
|
writeIndentation();
|
|
writeConstantReference(node);
|
|
writeSpaced('=');
|
|
writeWord('redirecting-factory-tearoff');
|
|
writeSpace();
|
|
writeMemberReferenceFromReference(node.targetReference);
|
|
endLine();
|
|
}
|
|
|
|
@override
|
|
void defaultNode(Node node) {
|
|
write('<${node.runtimeType}>');
|
|
}
|
|
}
|
|
|
|
class Precedence implements ExpressionVisitor<int> {
|
|
static final Precedence instance = new Precedence();
|
|
|
|
static int of(Expression node) => node.accept(instance);
|
|
|
|
static const int EXPRESSION = 1;
|
|
static const int CONDITIONAL = 2;
|
|
static const int LOGICAL_NULL_AWARE = 3;
|
|
static const int LOGICAL_OR = 4;
|
|
static const int LOGICAL_AND = 5;
|
|
static const int EQUALITY = 6;
|
|
static const int RELATIONAL = 7;
|
|
static const int BITWISE_OR = 8;
|
|
static const int BITWISE_XOR = 9;
|
|
static const int BITWISE_AND = 10;
|
|
static const int SHIFT = 11;
|
|
static const int ADDITIVE = 12;
|
|
static const int MULTIPLICATIVE = 13;
|
|
static const int PREFIX = 14;
|
|
static const int POSTFIX = 15;
|
|
static const int TYPE_LITERAL = 19;
|
|
static const int PRIMARY = 20;
|
|
static const int CALLEE = 21;
|
|
|
|
static const Map<String?, int> binaryPrecedence = const {
|
|
'&&': LOGICAL_AND,
|
|
'||': LOGICAL_OR,
|
|
'??': LOGICAL_NULL_AWARE,
|
|
'==': EQUALITY,
|
|
'!=': EQUALITY,
|
|
'>': RELATIONAL,
|
|
'>=': RELATIONAL,
|
|
'<': RELATIONAL,
|
|
'<=': RELATIONAL,
|
|
'|': BITWISE_OR,
|
|
'^': BITWISE_XOR,
|
|
'&': BITWISE_AND,
|
|
'>>': SHIFT,
|
|
'<<': SHIFT,
|
|
'+': ADDITIVE,
|
|
'-': ADDITIVE,
|
|
'*': MULTIPLICATIVE,
|
|
'%': MULTIPLICATIVE,
|
|
'/': MULTIPLICATIVE,
|
|
'~/': MULTIPLICATIVE,
|
|
null: EXPRESSION,
|
|
};
|
|
|
|
@override
|
|
int visitAuxiliaryExpression(AuxiliaryExpression node) => EXPRESSION;
|
|
|
|
@override
|
|
int visitInvalidExpression(InvalidExpression node) => CALLEE;
|
|
|
|
@override
|
|
int visitInstanceInvocation(InstanceInvocation node) => CALLEE;
|
|
|
|
@override
|
|
int visitInstanceGetterInvocation(InstanceGetterInvocation node) => CALLEE;
|
|
|
|
@override
|
|
int visitDynamicInvocation(DynamicInvocation node) => CALLEE;
|
|
|
|
@override
|
|
int visitFunctionInvocation(FunctionInvocation node) => CALLEE;
|
|
|
|
@override
|
|
int visitLocalFunctionInvocation(LocalFunctionInvocation node) => CALLEE;
|
|
|
|
@override
|
|
int visitEqualsCall(EqualsCall node) => EQUALITY;
|
|
|
|
@override
|
|
int visitEqualsNull(EqualsNull node) => EQUALITY;
|
|
|
|
@override
|
|
int visitAbstractSuperMethodInvocation(AbstractSuperMethodInvocation node) =>
|
|
CALLEE;
|
|
|
|
@override
|
|
int visitSuperMethodInvocation(SuperMethodInvocation node) => CALLEE;
|
|
|
|
@override
|
|
int visitStaticInvocation(StaticInvocation node) => CALLEE;
|
|
|
|
@override
|
|
int visitConstructorInvocation(ConstructorInvocation node) => CALLEE;
|
|
|
|
@override
|
|
int visitRedirectingFactoryInvocation(RedirectingFactoryInvocation node) =>
|
|
CALLEE;
|
|
|
|
@override
|
|
int visitNot(Not node) => PREFIX;
|
|
|
|
@override
|
|
int visitNullCheck(NullCheck node) => PRIMARY;
|
|
|
|
@override
|
|
int visitLogicalExpression(LogicalExpression node) =>
|
|
binaryPrecedence[logicalExpressionOperatorToString(node.operatorEnum)]!;
|
|
|
|
@override
|
|
int visitConditionalExpression(ConditionalExpression node) => CONDITIONAL;
|
|
|
|
@override
|
|
int visitStringConcatenation(StringConcatenation node) => PRIMARY;
|
|
|
|
@override
|
|
int visitIsExpression(IsExpression node) => RELATIONAL;
|
|
|
|
@override
|
|
int visitAsExpression(AsExpression node) => RELATIONAL;
|
|
|
|
@override
|
|
int visitSymbolLiteral(SymbolLiteral node) => PRIMARY;
|
|
|
|
@override
|
|
int visitTypeLiteral(TypeLiteral node) => PRIMARY;
|
|
|
|
@override
|
|
int visitThisExpression(ThisExpression node) => CALLEE;
|
|
|
|
@override
|
|
int visitRethrow(Rethrow node) => PRIMARY;
|
|
|
|
@override
|
|
int visitThrow(Throw node) => EXPRESSION;
|
|
|
|
@override
|
|
int visitListLiteral(ListLiteral node) => PRIMARY;
|
|
|
|
@override
|
|
int visitSetLiteral(SetLiteral node) => PRIMARY;
|
|
|
|
@override
|
|
int visitMapLiteral(MapLiteral node) => PRIMARY;
|
|
|
|
@override
|
|
int visitRecordLiteral(RecordLiteral node) => PRIMARY;
|
|
|
|
@override
|
|
int visitAwaitExpression(AwaitExpression node) => PREFIX;
|
|
|
|
@override
|
|
int visitFunctionExpression(FunctionExpression node) => EXPRESSION;
|
|
|
|
@override
|
|
int visitStringLiteral(StringLiteral node) => CALLEE;
|
|
|
|
@override
|
|
int visitIntLiteral(IntLiteral node) => CALLEE;
|
|
|
|
@override
|
|
int visitDoubleLiteral(DoubleLiteral node) => CALLEE;
|
|
|
|
@override
|
|
int visitBoolLiteral(BoolLiteral node) => CALLEE;
|
|
|
|
@override
|
|
int visitNullLiteral(NullLiteral node) => CALLEE;
|
|
|
|
@override
|
|
int visitVariableGet(VariableGet node) => PRIMARY;
|
|
|
|
@override
|
|
int visitVariableSet(VariableSet node) => EXPRESSION;
|
|
|
|
@override
|
|
int visitInstanceGet(InstanceGet node) => PRIMARY;
|
|
|
|
@override
|
|
int visitDynamicGet(DynamicGet node) => PRIMARY;
|
|
|
|
@override
|
|
int visitInstanceTearOff(InstanceTearOff node) => PRIMARY;
|
|
|
|
@override
|
|
int visitFunctionTearOff(FunctionTearOff node) => PRIMARY;
|
|
|
|
@override
|
|
int visitAbstractSuperPropertyGet(AbstractSuperPropertyGet node) => PRIMARY;
|
|
|
|
@override
|
|
int visitRecordIndexGet(RecordIndexGet node) => PRIMARY;
|
|
|
|
@override
|
|
int visitRecordNameGet(RecordNameGet node) => PRIMARY;
|
|
|
|
@override
|
|
int visitAbstractSuperPropertySet(AbstractSuperPropertySet node) =>
|
|
EXPRESSION;
|
|
|
|
@override
|
|
int visitSuperPropertyGet(SuperPropertyGet node) => PRIMARY;
|
|
|
|
@override
|
|
int visitSuperPropertySet(SuperPropertySet node) => EXPRESSION;
|
|
|
|
@override
|
|
int visitStaticGet(StaticGet node) => PRIMARY;
|
|
|
|
@override
|
|
int visitStaticTearOff(StaticTearOff node) => PRIMARY;
|
|
|
|
@override
|
|
int visitStaticSet(StaticSet node) => EXPRESSION;
|
|
|
|
@override
|
|
int visitConstructorTearOff(ConstructorTearOff node) => PRIMARY;
|
|
|
|
@override
|
|
int visitRedirectingFactoryTearOff(RedirectingFactoryTearOff node) => PRIMARY;
|
|
|
|
@override
|
|
int visitTypedefTearOff(TypedefTearOff node) => EXPRESSION;
|
|
|
|
@override
|
|
int visitLet(Let node) => EXPRESSION;
|
|
|
|
@override
|
|
int visitBlockExpression(BlockExpression node) => EXPRESSION;
|
|
|
|
@override
|
|
int visitCheckLibraryIsLoaded(CheckLibraryIsLoaded node) => EXPRESSION;
|
|
|
|
@override
|
|
int visitConstantExpression(ConstantExpression node) => PRIMARY;
|
|
|
|
@override
|
|
int visitDynamicSet(DynamicSet node) => EXPRESSION;
|
|
|
|
@override
|
|
int visitFileUriExpression(FileUriExpression node) => EXPRESSION;
|
|
|
|
@override
|
|
int visitInstanceCreation(InstanceCreation node) => EXPRESSION;
|
|
|
|
@override
|
|
int visitInstanceSet(InstanceSet node) => EXPRESSION;
|
|
|
|
@override
|
|
int visitInstantiation(Instantiation node) => EXPRESSION;
|
|
|
|
@override
|
|
int visitListConcatenation(ListConcatenation node) => EXPRESSION;
|
|
|
|
@override
|
|
int visitLoadLibrary(LoadLibrary node) => EXPRESSION;
|
|
|
|
@override
|
|
int visitMapConcatenation(MapConcatenation node) => EXPRESSION;
|
|
|
|
@override
|
|
int visitSetConcatenation(SetConcatenation node) => EXPRESSION;
|
|
|
|
@override
|
|
int visitSwitchExpression(SwitchExpression node) => PRIMARY;
|
|
|
|
@override
|
|
int visitPatternAssignment(PatternAssignment node) => EXPRESSION;
|
|
}
|
|
|
|
String procedureKindToString(ProcedureKind kind) {
|
|
switch (kind) {
|
|
case ProcedureKind.Method:
|
|
return 'method';
|
|
case ProcedureKind.Getter:
|
|
return 'get';
|
|
case ProcedureKind.Setter:
|
|
return 'set';
|
|
case ProcedureKind.Operator:
|
|
return 'operator';
|
|
case ProcedureKind.Factory:
|
|
return 'factory';
|
|
}
|
|
}
|