Files
sdk/pkg/kernel/lib/text/ast_to_text.dart
T
Aske Simon Christensen 50efd7f187 [Kernel] Add list of unused arguments to InstanceCreation nodes.
During constant evaluation, unused arguments to a const constructor are
thrown away after evaluation, since their values do not affect the
resulting instance constant. If such an unused argument ends up
unevaluated, any errors that would arise in the final evaluation are
not reported.

This CL adds space in the Kernel AST for saving these unevaluated
expressions so they can be checked during final constant evaluation.

Even though this is an incompatible change, no update is needed to the
VM code (except for the version bump), since the VM does not support
InstanceCreation nodes in the first place.

Change-Id: I4752562c1164efbba79eb018c15b07ed8354ce5f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/105761
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Aske Simon Christensen <askesc@google.com>
2019-06-13 09:42:26 +00:00

2200 lines
60 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' hide MapEntry;
import 'dart:convert' show json;
import '../ast.dart';
import '../import_table.dart';
abstract 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> {
final String prefix;
NormalNamer(this.prefix);
}
class ConstantNamer extends RecursiveVisitor<Null> with Namer<Constant> {
final String prefix;
ConstantNamer(this.prefix);
String getName(Constant constant) {
if (!map.containsKey(constant)) {
// Name everything in post-order visit of DAG.
constant.visitChildren(this);
}
return super.getName(constant);
}
defaultConstantReference(Constant constant) {
getName(constant);
}
}
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()) {
getNewName() {
var 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 debugLibraryName(Library node) {
return node == null
? 'null'
: node.name ?? globalDebuggingNames.nameLibrary(node);
}
String debugClassName(Class node) {
return node == null
? 'null'
: node.name ?? globalDebuggingNames.nameClass(node);
}
String debugQualifiedClassName(Class node) {
return debugLibraryName(node.enclosingLibrary) + '::' + debugClassName(node);
}
String debugMemberName(Member node) {
return node.name?.name ?? globalDebuggingNames.nameMember(node);
}
String debugQualifiedMemberName(Member node) {
if (node.enclosingClass != null) {
return debugQualifiedClassName(node.enclosingClass) +
'::' +
debugMemberName(node);
} else {
return debugLibraryName(node.enclosingLibrary) +
'::' +
debugMemberName(node);
}
}
String debugTypeParameterName(TypeParameter node) {
return node.name ?? globalDebuggingNames.nameTypeParameter(node);
}
String debugQualifiedTypeParameterName(TypeParameter node) {
if (node.parent is Class) {
return debugQualifiedClassName(node.parent) +
'::' +
debugTypeParameterName(node);
}
if (node.parent is Member) {
return debugQualifiedMemberName(node.parent) +
'::' +
debugTypeParameterName(node);
}
return debugTypeParameterName(node);
}
String debugVariableDeclarationName(VariableDeclaration node) {
return node.name ?? globalDebuggingNames.nameVariable(node);
}
String debugNodeToString(Node node) {
StringBuffer buffer = new StringBuffer();
new Printer(buffer, syntheticNames: globalDebuggingNames).writeNode(node);
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<Member> members = new NormalNamer<Member>('#m');
final Namer<Class> classes = new NormalNamer<Class>('#class');
final Namer<Library> libraries = new NormalNamer<Library>('#lib');
final Namer<TypeParameter> typeParameters =
new NormalNamer<TypeParameter>('#T');
final Namer<TreeNode> labels = new NormalNamer<TreeNode>('#L');
final Namer<Constant> constants = new ConstantNamer('#C');
final Disambiguator<Reference, CanonicalName> prefixes =
new Disambiguator<Reference, CanonicalName>();
nameVariable(VariableDeclaration node) => variables.getName(node);
nameMember(Member node) => members.getName(node);
nameClass(Class node) => classes.getName(node);
nameLibrary(Library node) => libraries.getName(node);
nameTypeParameter(TypeParameter node) => typeParameters.getName(node);
nameSwitchCase(SwitchCase node) => labels.getName(node);
nameLabeledStatement(LabeledStatement node) => labels.getName(node);
nameConstant(Constant node) => constants.getName(node);
final RegExp pathSeparator = new RegExp('[\\/]');
nameLibraryPrefix(Library node, {String proposedName}) {
return prefixes.disambiguate(node.reference, node.reference.canonicalName,
() {
if (proposedName != null) return proposedName;
if (node.name != null) return abbreviateName(node.name);
if (node.importUri != null) {
var 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);
}
return 'L';
});
}
nameCanonicalNameAsLibraryPrefix(Reference node, CanonicalName name,
{String proposedName}) {
return prefixes.disambiguate(node, name, () {
if (proposedName != null) return proposedName;
CanonicalName canonicalName = name ?? node.canonicalName;
if (canonicalName?.name != null) {
var 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';
});
}
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 Visitor<Null> {
final NameSystem syntheticNames;
final StringSink sink;
final Annotator annotator;
final Map<String, MetadataRepository<Object>> metadata;
ImportTable importTable;
int indentation = 0;
int column = 0;
bool showExternal;
bool showOffsets;
bool showMetadata;
static int SPACE = 0;
static int WORD = 1;
static int SYMBOL = 2;
int state = SPACE;
Printer(this.sink,
{NameSystem syntheticNames,
this.showExternal,
this.showOffsets: false,
this.showMetadata: false,
this.importTable,
this.annotator,
this.metadata})
: this.syntheticNames = syntheticNames ?? new NameSystem();
Printer createInner(ImportTable importTable,
Map<String, MetadataRepository<Object>> metadata) {
return new Printer(sink,
importTable: importTable,
metadata: metadata,
syntheticNames: syntheticNames,
annotator: annotator,
showExternal: showExternal,
showOffsets: showOffsets,
showMetadata: showMetadata);
}
bool shouldHighlight(Node node) {
return false;
}
void startHighlight(Node node) {}
void endHighlight(Node node) {}
String getLibraryName(Library node) {
return node.name ?? syntheticNames.nameLibrary(node);
}
String getLibraryReference(Library node) {
if (node == null) return '<No Library>';
if (importTable != null && importTable.getImportIndex(node) != -1) {
return syntheticNames.nameLibraryPrefix(node);
}
return getLibraryName(node);
}
String getClassName(Class node) {
return node.name ?? syntheticNames.nameClass(node);
}
String getClassReference(Class node) {
if (node == null) return '<No Class>';
String name = getClassName(node);
String library = getLibraryReference(node.enclosingLibrary);
return '$library::$name';
}
String getTypedefReference(Typedef node) {
if (node == null) return '<No Typedef>';
String library = getLibraryReference(node.enclosingLibrary);
return '$library::${node.name}';
}
static final String emptyNameString = '';
static final Name emptyName = new Name(emptyNameString);
Name getMemberName(Member node) {
if (node.name?.name == '') return emptyName;
if (node.name != null) return node.name;
return new Name(syntheticNames.nameMember(node));
}
String getMemberReference(Member node) {
if (node == null) return '<No Member>';
String name = getMemberName(node).name;
if (node.parent is Class) {
String className = getClassReference(node.parent);
return '$className::$name';
} else {
String library = getLibraryReference(node.enclosingLibrary);
return '$library::$name';
}
}
String getVariableName(VariableDeclaration node) {
return node.name ?? syntheticNames.nameVariable(node);
}
String getVariableReference(VariableDeclaration node) {
if (node == null) return '<No VariableDeclaration>';
return getVariableName(node);
}
String getTypeParameterName(TypeParameter node) {
return node.name ?? syntheticNames.nameTypeParameter(node);
}
String getTypeParameterReference(TypeParameter node) {
if (node == null) return '<No TypeParameter>';
String name = getTypeParameterName(node);
if (node.parent is FunctionNode && node.parent.parent is Member) {
String member = getMemberReference(node.parent.parent);
return '$member::$name';
} else if (node.parent is Class) {
String className = getClassReference(node.parent);
return '$className::$name';
} else {
return name; // Bound inside a function type.
}
}
void writeComponentProblems(Component component) {
writeProblemsAsJson("Problems in component", component.problemsAsJson);
}
void writeProblemsAsJson(String header, List<String> problemsAsJson) {
if (problemsAsJson?.isEmpty == false) {
endLine("//");
write("// ");
write(header);
endLine(":");
endLine("//");
for (String s in problemsAsJson) {
Map<String, Object> decoded = json.decode(s);
List<Object> plainTextFormatted = decoded["plainTextFormatted"];
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');
if (library.name != null) {
writeWord(library.name);
}
endLine(';');
LibraryImportTable imports = new LibraryImportTable(library);
Printer inner = createInner(imports, library.enclosingComponent?.metadata);
inner.writeStandardLibraryContent(library,
outerPrinter: this, importsToPrint: imports);
}
void printLibraryImportTable(LibraryImportTable imports) {
for (var library in imports.importedLibraries) {
var importPath = imports.getImportPath(library);
if (importPath == "") {
var prefix =
syntheticNames.nameLibraryPrefix(library, proposedName: 'self');
endLine('import self as $prefix;');
} else {
var prefix = syntheticNames.nameLibraryPrefix(library);
endLine('import "$importPath" as $prefix;');
}
}
}
void writeStandardLibraryContent(Library library,
{Printer outerPrinter, LibraryImportTable importsToPrint}) {
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.fields.forEach(writeNode);
library.procedures.forEach(writeNode);
}
void writeAdditionalExports(List<Reference> additionalExports) {
if (additionalExports.isEmpty) return;
write('additionalExports = (');
bool isFirst = true;
for (Reference reference in additionalExports) {
if (isFirst) {
isFirst = false;
} else {
write(', ');
}
var node = reference.node;
if (node is Class) {
Library nodeLibrary = node.enclosingLibrary;
String prefix = syntheticNames.nameLibraryPrefix(nodeLibrary);
write(prefix + '::' + node.name);
} else if (node is Field) {
Library nodeLibrary = node.enclosingLibrary;
String prefix = syntheticNames.nameLibraryPrefix(nodeLibrary);
write(prefix + '::' + node.name.name);
} else if (node is Procedure) {
Library nodeLibrary = node.enclosingLibrary;
String prefix = syntheticNames.nameLibraryPrefix(nodeLibrary);
write(prefix + '::' + node.name.name);
} else if (node is Typedef) {
Library nodeLibrary = node.enclosingLibrary;
String prefix = syntheticNames.nameLibraryPrefix(nodeLibrary);
write(prefix + '::' + node.name);
} else if (reference.canonicalName != null) {
write(reference.canonicalName.toString());
} else {
throw new UnimplementedError('${node.runtimeType}');
}
endLine(')');
}
endLine();
}
void writeComponentFile(Component component) {
ImportTable imports = new ComponentImportTable(component);
var inner = createInner(imports, component.metadata);
writeWord('main');
writeSpaced('=');
inner.writeMemberReferenceFromReference(component.mainMethodName);
endLine(';');
if (showMetadata) {
inner.writeMetadata(component);
}
writeComponentProblems(component);
for (var library in component.libraries) {
if (library.isExternal) {
if (!showExternal) {
continue;
}
writeWord('external');
}
if (showMetadata) {
inner.writeMetadata(library);
}
writeAnnotationList(library.annotations);
writeWord('library');
if (library.name != null) {
writeWord(library.name);
}
if (library.importUri != null) {
writeSpaced('from');
writeWord('"${library.importUri}"');
}
var prefix = syntheticNames.nameLibraryPrefix(library);
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);
var 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(TreeNode 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 highlight = shouldHighlight(node);
if (highlight) {
startHighlight(node);
}
if (showOffsets && node is TreeNode) {
writeWord("[${node.fileOffset}]");
}
if (showMetadata && node is TreeNode) {
writeMetadata(node);
}
node.accept(this);
if (highlight) {
endHighlight(node);
}
}
}
void writeOptionalNode(Node node) {
if (node != null) {
node.accept(this);
}
}
void writeMetadata(TreeNode node) {
if (metadata != null) {
for (var md in metadata.values) {
final nodeMetadata = md.mapping[node];
if (nodeMetadata != null) {
writeWord("[@${md.tag}=${nodeMetadata}]");
}
}
}
}
void writeAnnotatedType(DartType type, String annotation) {
writeType(type);
if (annotation != null) {
write('/');
write(annotation);
state = WORD;
}
}
void writeType(DartType type) {
if (type == null) {
write('<No DartType>');
} else {
type.accept(this);
}
}
void writeOptionalType(DartType type) {
if (type != null) {
type.accept(this);
}
}
visitSupertype(Supertype type) {
if (type == null) {
write('<No Supertype>');
} else {
writeClassReferenceFromReference(type.className);
if (type.typeArguments.isNotEmpty) {
writeSymbol('<');
writeList(type.typeArguments, writeType);
writeSymbol('>');
}
}
}
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) {
if (name?.name == '') {
writeWord(emptyNameString);
} else {
writeWord(name?.name ?? '<anon>'); // TODO: write library name
}
}
void endLine([String string]) {
if (string != null) {
write(string);
}
write('\n');
state = SPACE;
column = 0;
}
void writeFunction(FunctionNode function,
{name, List<Initializer> initializers, bool terminateLine: true}) {
if (name is String) {
writeWord(name);
} else if (name is Name) {
writeName(name);
} else {
assert(name == null);
}
writeTypeParameterList(function.typeParameters);
writeParameterList(function.positionalParameters, function.namedParameters,
function.requiredParameterCount);
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.dartAsyncMarker != AsyncMarker.Sync &&
function.dartAsyncMarker != function.asyncMarker) {
writeSpaced("/* originally");
writeSpaced(getAsyncMarkerKeyword(function.dartAsyncMarker));
writeSpaced("*/");
}
if (function.body != null) {
writeFunctionBody(function.body, terminateLine: terminateLine);
} else if (terminateLine) {
endLine(';');
}
}
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*';
case AsyncMarker.SyncYielding:
return 'yielding';
default:
return '<Invalid async marker: $marker>';
}
}
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();
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);
}
}
writeFunctionType(FunctionType node,
{List<VariableDeclaration> typedefPositional,
List<VariableDeclaration> typedefNamed}) {
if (state == WORD) {
ensureSpace();
}
writeTypeParameterList(node.typeParameters);
writeSymbol('(');
List<DartType> positional = node.positionalParameters;
bool parametersAnnotated = false;
if (typedefPositional != null) {
for (VariableDeclaration formal in typedefPositional) {
parametersAnnotated =
parametersAnnotated || formal.annotations.length > 0;
}
}
if (typedefNamed != null) {
for (VariableDeclaration formal in typedefNamed) {
parametersAnnotated =
parametersAnnotated || formal.annotations.length > 0;
}
}
if (parametersAnnotated && typedefPositional != null) {
writeList(typedefPositional.take(node.requiredParameterCount),
writeVariableDeclaration);
} else {
writeList(positional.take(node.requiredParameterCount), writeType);
}
if (node.requiredParameterCount < positional.length) {
if (node.requiredParameterCount > 0) {
writeComma();
}
writeSymbol('[');
if (parametersAnnotated && typedefPositional != null) {
writeList(typedefPositional.skip(node.requiredParameterCount),
writeVariableDeclaration);
} else {
writeList(positional.skip(node.requiredParameterCount), writeType);
}
writeSymbol(']');
}
if (node.namedParameters.isNotEmpty) {
if (node.positionalParameters.isNotEmpty) {
writeComma();
}
writeSymbol('{');
if (parametersAnnotated && typedefNamed != null) {
writeList(typedefNamed, writeVariableDeclaration);
} else {
writeList(node.namedParameters, visitNamedType);
}
writeSymbol('}');
}
writeSymbol(')');
writeSpaced('');
writeType(node.returnType);
}
void writeBody(Statement body) {
if (body is Block) {
endLine(' {');
++indentation;
body.statements.forEach(writeNode);
--indentation;
writeIndentation();
endLine('}');
} else {
endLine();
++indentation;
writeNode(body);
--indentation;
}
}
void writeReturnType(DartType type, String annotation) {
if (type == null) return;
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 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 (var 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 == null) return '<No Class>';
if (reference.node != null) return getClassReference(reference.asClass);
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 @';
libraryString(CanonicalName lib) {
if (lib.reference?.node != null)
return getLibraryReference(lib.reference.asLibrary);
return syntheticNames.nameCanonicalNameAsLibraryPrefix(
lib.reference, lib);
}
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 highlight = shouldHighlight(variable);
if (highlight) {
startHighlight(variable);
}
writeWord(getVariableReference(variable));
if (highlight) {
endHighlight(variable);
}
}
void writeTypeParameterReference(TypeParameter node) {
writeWord(getTypeParameterReference(node));
}
void writeExpression(Expression node, [int minimumPrecedence]) {
final highlight = shouldHighlight(node);
if (highlight) {
startHighlight(node);
}
if (showOffsets) writeWord("[${node.fileOffset}]");
bool needsParenteses = false;
if (minimumPrecedence != null && getPrecedence(node) < minimumPrecedence) {
needsParenteses = true;
writeSymbol('(');
}
writeNode(node);
if (needsParenteses) {
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();
}
}
}
visitLibrary(Library node) {}
visitField(Field node) {
writeAnnotationList(node.annotations);
writeIndentation();
writeModifier(node.isStatic, 'static');
writeModifier(node.isCovariant, 'covariant');
writeModifier(node.isGenericCovariantImpl, 'generic-covariant-impl');
writeModifier(node.isFinal, 'final');
writeModifier(node.isConst, 'const');
// Only show implicit getter/setter modifiers in cases where they are
// out of the ordinary.
if (node.isStatic) {
writeModifier(node.hasImplicitGetter, '[getter]');
writeModifier(node.hasImplicitSetter, '[setter]');
} else {
writeModifier(!node.hasImplicitGetter, '[no-getter]');
if (node.isFinal) {
writeModifier(node.hasImplicitSetter, '[setter]');
} else {
writeModifier(!node.hasImplicitSetter, '[no-setter]');
}
}
writeWord('field');
writeSpace();
writeAnnotatedType(node.type, annotator?.annotateField(this, node));
writeName(getMemberName(node));
if (node.initializer != null) {
writeSpaced('=');
writeExpression(node.initializer);
}
if ((node.enclosingClass == null &&
node.enclosingLibrary.fileUri != node.fileUri) ||
(node.enclosingClass != null &&
node.enclosingClass.fileUri != node.fileUri)) {
writeWord("/* from ${node.fileUri} */");
}
endLine(';');
}
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.isNoSuchMethodForwarder, 'no-such-method-forwarder');
writeWord(procedureKindToString(node.kind));
if ((node.enclosingClass == null &&
node.enclosingLibrary.fileUri != node.fileUri) ||
(node.enclosingClass != null &&
node.enclosingClass.fileUri != node.fileUri)) {
writeWord("/* from ${node.fileUri} */");
}
writeFunction(node.function, name: getMemberName(node));
}
visitConstructor(Constructor node) {
writeAnnotationList(node.annotations);
writeIndentation();
writeModifier(node.isExternal, 'external');
writeModifier(node.isConst, 'const');
writeModifier(node.isSynthetic, 'synthetic');
writeWord('constructor');
writeFunction(node.function,
name: node.name, initializers: node.initializers);
}
visitRedirectingFactoryConstructor(RedirectingFactoryConstructor node) {
writeAnnotationList(node.annotations);
writeIndentation();
writeModifier(node.isExternal, 'external');
writeModifier(node.isConst, 'const');
writeWord('redirecting_factory');
if (node.name != null) {
writeName(node.name);
}
writeTypeParameterList(node.typeParameters);
writeParameterList(node.positionalParameters, node.namedParameters,
node.requiredParameterCount);
writeSpaced('=');
writeMemberReferenceFromReference(node.targetReference);
if (node.typeArguments.isNotEmpty) {
writeSymbol('<');
writeList(node.typeArguments, writeType);
writeSymbol('>');
}
endLine(';');
}
visitClass(Class node) {
writeAnnotationList(node.annotations);
writeIndentation();
writeModifier(node.isAbstract, 'abstract');
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);
}
var 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);
node.redirectingFactoryConstructors.forEach(writeNode);
--indentation;
writeIndentation();
endLine('}');
}
visitTypedef(Typedef node) {
writeAnnotationList(node.annotations);
writeIndentation();
writeWord('typedef');
writeWord(node.name);
writeTypeParameterList(node.typeParameters);
writeSpaced('=');
if (node.type is FunctionType) {
writeFunctionType(node.type,
typedefPositional: node.positionalParameters,
typedefNamed: node.namedParameters);
} else {
writeNode(node.type);
}
endLine(';');
}
visitInvalidExpression(InvalidExpression node) {
writeWord('invalid-expression');
if (node.message != null) {
writeWord('"${escapeString(node.message)}"');
}
}
visitMethodInvocation(MethodInvocation node) {
writeExpression(node.receiver, Precedence.PRIMARY);
writeSymbol('.');
writeInterfaceTarget(node.name, node.interfaceTargetReference);
writeNode(node.arguments);
}
visitDirectMethodInvocation(DirectMethodInvocation node) {
writeExpression(node.receiver, Precedence.PRIMARY);
writeSymbol('.{=');
writeMemberReferenceFromReference(node.targetReference);
writeSymbol('}');
writeNode(node.arguments);
}
visitSuperMethodInvocation(SuperMethodInvocation node) {
writeWord('super');
writeSymbol('.');
writeInterfaceTarget(node.name, node.interfaceTargetReference);
writeNode(node.arguments);
}
visitStaticInvocation(StaticInvocation node) {
writeModifier(node.isConst, 'const');
writeMemberReferenceFromReference(node.targetReference);
writeNode(node.arguments);
}
visitConstructorInvocation(ConstructorInvocation node) {
writeWord(node.isConst ? 'const' : 'new');
writeMemberReferenceFromReference(node.targetReference);
writeNode(node.arguments);
}
visitNot(Not node) {
writeSymbol('!');
writeExpression(node.operand, Precedence.PREFIX);
}
visitLogicalExpression(LogicalExpression node) {
int precedence = Precedence.binaryPrecedence[node.operator];
writeExpression(node.left, precedence);
writeSpaced(node.operator);
writeExpression(node.right, precedence + 1);
}
visitConditionalExpression(ConditionalExpression node) {
writeExpression(node.condition, Precedence.LOGICAL_OR);
ensureSpace();
write('?');
writeStaticType(node.staticType);
writeSpace();
writeExpression(node.then);
writeSpaced(':');
writeExpression(node.otherwise);
}
String getEscapedCharacter(int codeUnit) {
switch (codeUnit) {
case 9:
return r'\t';
case 10:
return r'\n';
case 11:
return r'\v';
case 12:
return r'\f';
case 13:
return r'\r';
case 34:
return r'\"';
case 36:
return r'\$';
case 92:
return r'\\';
default:
if (codeUnit < 32 || codeUnit > 126) {
return r'\u' + '$codeUnit'.padLeft(4, '0');
} else {
return null;
}
}
}
String escapeString(String string) {
StringBuffer buffer;
for (int i = 0; i < string.length; ++i) {
String character = getEscapedCharacter(string.codeUnitAt(i));
if (character != null) {
buffer ??= new StringBuffer(string.substring(0, i));
buffer.write(character);
} else {
buffer?.write(string[i]);
}
}
return buffer == null ? string : buffer.toString();
}
visitStringConcatenation(StringConcatenation node) {
if (state == WORD) {
writeSpace();
}
write('"');
for (var part in node.expressions) {
if (part is StringLiteral) {
writeSymbol(escapeString(part.value));
} else {
writeSymbol(r'${');
writeExpression(part);
writeSymbol('}');
}
}
write('"');
state = WORD;
}
visitListConcatenation(ListConcatenation node) {
bool first = true;
for (Expression part in node.lists) {
if (!first) writeSpaced('+');
writeExpression(part);
first = false;
}
}
visitSetConcatenation(SetConcatenation node) {
bool first = true;
for (Expression part in node.sets) {
if (!first) writeSpaced('+');
writeExpression(part);
first = false;
}
}
visitMapConcatenation(MapConcatenation node) {
bool first = true;
for (Expression part in node.maps) {
if (!first) writeSpaced('+');
writeExpression(part);
first = false;
}
}
visitInstanceCreation(InstanceCreation node) {
write('${node.classNode}');
if (node.typeArguments.isNotEmpty) {
writeSymbol('<');
writeList(node.typeArguments, writeType);
writeSymbol('>');
}
write(' {');
bool first = true;
node.fieldValues.forEach((Reference fieldRef, Expression value) {
if (!first) {
writeComma();
}
write('${fieldRef.asField.name}: ');
writeExpression(value);
first = false;
});
for (AssertStatement assert_ in node.asserts) {
if (!first) {
writeComma();
}
write('assert(');
writeExpression(assert_.condition);
if (assert_.message != null) {
writeComma();
writeExpression(assert_.message);
}
write(')');
first = false;
}
for (Expression unusedArgument in node.unusedArguments) {
if (!first) {
writeComma();
}
writeExpression(unusedArgument);
first = false;
}
write('}');
}
visitIsExpression(IsExpression node) {
writeExpression(node.operand, Precedence.BITWISE_OR);
writeSpaced('is');
writeType(node.type);
}
visitAsExpression(AsExpression node) {
writeExpression(node.operand, Precedence.BITWISE_OR);
writeSpaced(node.isTypeError ? 'as{TypeError}' : 'as');
writeType(node.type);
}
visitSymbolLiteral(SymbolLiteral node) {
writeSymbol('#');
writeWord(node.value);
}
visitTypeLiteral(TypeLiteral node) {
writeType(node.type);
}
visitThisExpression(ThisExpression node) {
writeWord('this');
}
visitRethrow(Rethrow node) {
writeWord('rethrow');
}
visitThrow(Throw node) {
writeWord('throw');
writeSpace();
writeExpression(node.expression);
}
visitListLiteral(ListLiteral node) {
if (node.isConst) {
writeWord('const');
writeSpace();
}
if (node.typeArgument != null) {
writeSymbol('<');
writeType(node.typeArgument);
writeSymbol('>');
}
writeSymbol('[');
writeList(node.expressions, writeNode);
writeSymbol(']');
}
visitSetLiteral(SetLiteral node) {
if (node.isConst) {
writeWord('const');
writeSpace();
}
if (node.typeArgument != null) {
writeSymbol('<');
writeType(node.typeArgument);
writeSymbol('>');
}
writeSymbol('{');
writeList(node.expressions, writeNode);
writeSymbol('}');
}
visitMapLiteral(MapLiteral node) {
if (node.isConst) {
writeWord('const');
writeSpace();
}
if (node.keyType != null) {
writeSymbol('<');
writeList([node.keyType, node.valueType], writeType);
writeSymbol('>');
}
writeSymbol('{');
writeList(node.entries, writeNode);
writeSymbol('}');
}
visitMapEntry(MapEntry node) {
writeExpression(node.key);
writeComma(':');
writeExpression(node.value);
}
visitAwaitExpression(AwaitExpression node) {
writeWord('await');
writeExpression(node.operand);
}
visitFunctionExpression(FunctionExpression node) {
writeFunction(node.function, terminateLine: false);
}
visitStringLiteral(StringLiteral node) {
writeWord('"${escapeString(node.value)}"');
}
visitIntLiteral(IntLiteral node) {
writeWord('${node.value}');
}
visitDoubleLiteral(DoubleLiteral node) {
writeWord('${node.value}');
}
visitBoolLiteral(BoolLiteral node) {
writeWord('${node.value}');
}
visitNullLiteral(NullLiteral node) {
writeWord('null');
}
visitLet(Let node) {
writeWord('let');
writeVariableDeclaration(node.variable);
writeSpaced('in');
writeExpression(node.body);
}
visitBlockExpression(BlockExpression node) {
writeSpaced('block');
writeBlockBody(node.body.statements, asExpression: true);
writeSymbol(' =>');
writeExpression(node.value);
}
visitInstantiation(Instantiation node) {
writeExpression(node.expression);
writeSymbol('<');
writeList(node.typeArguments, writeType);
writeSymbol('>');
}
visitLoadLibrary(LoadLibrary node) {
writeWord('LoadLibrary');
writeSymbol('(');
writeWord(node.import.name);
writeSymbol(')');
state = WORD;
}
visitCheckLibraryIsLoaded(CheckLibraryIsLoaded node) {
writeWord('CheckLibraryIsLoaded');
writeSymbol('(');
writeWord(node.import.name);
writeSymbol(')');
state = WORD;
}
visitLibraryPart(LibraryPart node) {
writeAnnotationList(node.annotations);
writeIndentation();
writeWord('part');
writeWord(node.partUri);
endLine(";");
}
visitLibraryDependency(LibraryDependency node) {
writeIndentation();
writeWord(node.isImport ? 'import' : 'export');
var uriString;
if (node.importedLibraryReference?.node != null) {
uriString = '${node.targetLibrary.importUri}';
} else {
uriString = '${node.importedLibraryReference?.canonicalName?.name}';
}
writeWord('"$uriString"');
if (node.isDeferred) {
writeWord('deferred');
}
if (node.name != null) {
writeWord('as');
writeWord(node.name);
}
endLine(';');
}
defaultExpression(Expression node) {
writeWord('${node.runtimeType}');
}
visitVariableGet(VariableGet node) {
writeVariableReference(node.variable);
if (node.promotedType != null) {
writeSymbol('{');
writeNode(node.promotedType);
writeSymbol('}');
state = WORD;
}
}
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) {
if (type != null) {
writeSymbol('{');
writeType(type);
writeSymbol('}');
}
}
visitPropertyGet(PropertyGet node) {
writeExpression(node.receiver, Precedence.PRIMARY);
writeSymbol('.');
writeInterfaceTarget(node.name, node.interfaceTargetReference);
}
visitPropertySet(PropertySet node) {
writeExpression(node.receiver, Precedence.PRIMARY);
writeSymbol('.');
writeInterfaceTarget(node.name, node.interfaceTargetReference);
writeSpaced('=');
writeExpression(node.value);
}
visitSuperPropertyGet(SuperPropertyGet node) {
writeWord('super');
writeSymbol('.');
writeInterfaceTarget(node.name, node.interfaceTargetReference);
}
visitSuperPropertySet(SuperPropertySet node) {
writeWord('super');
writeSymbol('.');
writeInterfaceTarget(node.name, node.interfaceTargetReference);
writeSpaced('=');
writeExpression(node.value);
}
visitDirectPropertyGet(DirectPropertyGet node) {
writeExpression(node.receiver, Precedence.PRIMARY);
writeSymbol('.{=');
writeMemberReferenceFromReference(node.targetReference);
writeSymbol('}');
}
visitDirectPropertySet(DirectPropertySet node) {
writeExpression(node.receiver, Precedence.PRIMARY);
writeSymbol('.{=');
writeMemberReferenceFromReference(node.targetReference);
writeSymbol('}');
writeSpaced('=');
writeExpression(node.value);
}
visitStaticGet(StaticGet node) {
writeMemberReferenceFromReference(node.targetReference);
}
visitStaticSet(StaticSet node) {
writeMemberReferenceFromReference(node.targetReference);
writeSpaced('=');
writeExpression(node.value);
}
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('}');
}
visitBlock(Block node) {
writeIndentation();
writeBlockBody(node.statements);
}
visitAssertBlock(AssertBlock node) {
writeIndentation();
writeSpaced('assert');
writeBlockBody(node.statements);
}
visitEmptyStatement(EmptyStatement node) {
writeIndentation();
endLine(';');
}
visitAssertStatement(AssertStatement node, {bool asExpression = false}) {
if (!asExpression) {
writeIndentation();
}
writeWord('assert');
writeSymbol('(');
writeExpression(node.condition);
if (node.message != null) {
writeComma();
writeExpression(node.message);
}
if (!asExpression) {
endLine(');');
} else {
writeSymbol(')');
}
}
visitLabeledStatement(LabeledStatement node) {
writeIndentation();
writeWord(syntheticNames.nameLabeledStatement(node));
endLine(':');
writeNode(node.body);
}
visitBreakStatement(BreakStatement node) {
writeIndentation();
writeWord('break');
writeWord(syntheticNames.nameLabeledStatement(node.target));
endLine(';');
}
visitWhileStatement(WhileStatement node) {
writeIndentation();
writeSpaced('while');
writeSymbol('(');
writeExpression(node.condition);
writeSymbol(')');
writeBody(node.body);
}
visitDoStatement(DoStatement node) {
writeIndentation();
writeWord('do');
writeBody(node.body);
writeIndentation();
writeSpaced('while');
writeSymbol('(');
writeExpression(node.condition);
endLine(')');
}
visitForStatement(ForStatement node) {
writeIndentation();
writeSpaced('for');
writeSymbol('(');
writeList(node.variables, writeVariableDeclaration);
writeComma(';');
if (node.condition != null) {
writeExpression(node.condition);
}
writeComma(';');
writeList(node.updates, writeExpression);
writeSymbol(')');
writeBody(node.body);
}
visitForInStatement(ForInStatement node) {
writeIndentation();
if (node.isAsync) {
writeSpaced('await');
}
writeSpaced('for');
writeSymbol('(');
writeVariableDeclaration(node.variable, useVarKeyword: true);
writeSpaced('in');
writeExpression(node.iterable);
writeSymbol(')');
writeBody(node.body);
}
visitSwitchStatement(SwitchStatement node) {
writeIndentation();
writeWord('switch');
writeSymbol('(');
writeExpression(node.expression);
endLine(') {');
++indentation;
node.cases.forEach(writeNode);
--indentation;
writeIndentation();
endLine('}');
}
visitSwitchCase(SwitchCase node) {
String label = syntheticNames.nameSwitchCase(node);
writeIndentation();
writeWord(label);
endLine(':');
for (var expression in node.expressions) {
writeIndentation();
writeWord('case');
writeExpression(expression);
endLine(':');
}
if (node.isDefault) {
writeIndentation();
writeWord('default');
endLine(':');
}
++indentation;
writeNode(node.body);
--indentation;
}
visitContinueSwitchStatement(ContinueSwitchStatement node) {
writeIndentation();
writeWord('continue');
writeWord(syntheticNames.nameSwitchCase(node.target));
endLine(';');
}
visitIfStatement(IfStatement node) {
writeIndentation();
writeWord('if');
writeSymbol('(');
writeExpression(node.condition);
writeSymbol(')');
writeBody(node.then);
if (node.otherwise != null) {
writeIndentation();
writeWord('else');
writeBody(node.otherwise);
}
}
visitReturnStatement(ReturnStatement node) {
writeIndentation();
writeWord('return');
if (node.expression != null) {
writeSpace();
writeExpression(node.expression);
}
endLine(';');
}
visitTryCatch(TryCatch node) {
writeIndentation();
writeWord('try');
writeBody(node.body);
node.catches.forEach(writeNode);
}
visitCatch(Catch node) {
writeIndentation();
if (node.guard != null) {
writeWord('on');
writeType(node.guard);
writeSpace();
}
writeWord('catch');
writeSymbol('(');
if (node.exception != null) {
writeVariableDeclaration(node.exception);
} else {
writeWord('no-exception-var');
}
if (node.stackTrace != null) {
writeComma();
writeVariableDeclaration(node.stackTrace);
}
writeSymbol(')');
writeBody(node.body);
}
visitTryFinally(TryFinally node) {
writeIndentation();
writeWord('try');
writeBody(node.body);
writeIndentation();
writeWord('finally');
writeBody(node.finalizer);
}
visitYieldStatement(YieldStatement node) {
writeIndentation();
if (node.isYieldStar) {
writeWord('yield*');
} else if (node.isNative) {
writeWord('[yield]');
} else {
writeWord('yield');
}
writeExpression(node.expression);
endLine(';');
}
visitVariableDeclaration(VariableDeclaration node) {
writeIndentation();
writeVariableDeclaration(node, useVarKeyword: true);
endLine(';');
}
visitFunctionDeclaration(FunctionDeclaration node) {
writeAnnotationList(node.variable.annotations);
writeIndentation();
writeWord('function');
if (node.function != null) {
writeFunction(node.function, name: getVariableName(node.variable));
} else {
writeWord(getVariableName(node.variable));
endLine('...;');
}
}
void writeVariableDeclaration(VariableDeclaration node,
{bool useVarKeyword: false}) {
if (showOffsets) writeWord("[${node.fileOffset}]");
if (showMetadata) writeMetadata(node);
writeAnnotationList(node.annotations, separateLines: false);
writeModifier(node.isCovariant, 'covariant');
writeModifier(node.isGenericCovariantImpl, 'generic-covariant-impl');
writeModifier(node.isFinal, 'final');
writeModifier(node.isConst, 'const');
if (node.type != null) {
writeAnnotatedType(node.type, annotator?.annotateVariable(this, node));
}
if (useVarKeyword && !node.isFinal && !node.isConst && node.type == null) {
writeWord('var');
}
writeWord(getVariableName(node));
if (node.initializer != null) {
writeSpaced('=');
writeExpression(node.initializer);
}
}
visitArguments(Arguments node) {
if (node.types.isNotEmpty) {
writeSymbol('<');
writeList(node.types, writeType);
writeSymbol('>');
}
writeSymbol('(');
var allArgs =
<List<TreeNode>>[node.positional, node.named].expand((x) => x);
writeList(allArgs, writeNode);
writeSymbol(')');
}
visitNamedExpression(NamedExpression node) {
writeWord(node.name);
writeComma(':');
writeExpression(node.value);
}
defaultStatement(Statement node) {
writeIndentation();
endLine('${node.runtimeType}');
}
visitInvalidInitializer(InvalidInitializer node) {
writeWord('invalid-initializer');
}
visitFieldInitializer(FieldInitializer node) {
writeMemberReferenceFromReference(node.fieldReference);
writeSpaced('=');
writeExpression(node.value);
}
visitSuperInitializer(SuperInitializer node) {
writeWord('super');
writeMemberReferenceFromReference(node.targetReference);
writeNode(node.arguments);
}
visitRedirectingInitializer(RedirectingInitializer node) {
writeWord('this');
writeMemberReferenceFromReference(node.targetReference);
writeNode(node.arguments);
}
visitLocalInitializer(LocalInitializer node) {
writeVariableDeclaration(node.variable);
}
visitAssertInitializer(AssertInitializer node) {
visitAssertStatement(node.statement, asExpression: true);
}
defaultInitializer(Initializer node) {
writeIndentation();
endLine(': ${node.runtimeType}');
}
visitInvalidType(InvalidType node) {
writeWord('invalid-type');
}
visitDynamicType(DynamicType node) {
writeWord('dynamic');
}
visitVoidType(VoidType node) {
writeWord('void');
}
visitInterfaceType(InterfaceType node) {
writeClassReferenceFromReference(node.className);
if (node.typeArguments.isNotEmpty) {
writeSymbol('<');
writeList(node.typeArguments, writeType);
writeSymbol('>');
state = WORD; // Disallow a word immediately after the '>'.
}
}
visitFunctionType(FunctionType node) {
writeFunctionType(node);
}
visitNamedType(NamedType node) {
writeWord(node.name);
writeSymbol(':');
writeSpace();
writeType(node.type);
}
visitTypeParameterType(TypeParameterType node) {
writeTypeParameterReference(node.parameter);
if (node.promotedBound != null) {
writeSpace();
writeWord('extends');
writeSpace();
writeType(node.promotedBound);
}
}
visitTypeParameter(TypeParameter node) {
writeModifier(node.isGenericCovariantImpl, 'generic-covariant-impl');
writeAnnotationList(node.annotations, separateLines: false);
writeWord(getTypeParameterName(node));
writeSpaced('extends');
writeType(node.bound);
if (node.defaultType != null) {
writeSpaced('=');
writeType(node.defaultType);
}
}
visitConstantExpression(ConstantExpression node) {
writeWord(syntheticNames.nameConstant(node.constant));
}
defaultConstant(Constant node) {
final String name = syntheticNames.nameConstant(node);
endLine(' $name = $node');
}
visitListConstant(ListConstant node) {
final String name = syntheticNames.nameConstant(node);
write(' $name = ');
final String entries = node.entries.map((Constant constant) {
return syntheticNames.nameConstant(constant);
}).join(', ');
endLine('${node.runtimeType}<${node.typeArgument}>($entries)');
}
visitSetConstant(SetConstant node) {
final String name = syntheticNames.nameConstant(node);
write(' $name = ');
final String entries = node.entries.map((Constant constant) {
return syntheticNames.nameConstant(constant);
}).join(', ');
endLine('${node.runtimeType}<${node.typeArgument}>($entries)');
}
visitMapConstant(MapConstant node) {
final String name = syntheticNames.nameConstant(node);
write(' $name = ');
final String entries = node.entries.map((ConstantMapEntry entry) {
final String key = syntheticNames.nameConstant(entry.key);
final String value = syntheticNames.nameConstant(entry.value);
return '$key: $value';
}).join(', ');
endLine(
'${node.runtimeType}<${node.keyType}, ${node.valueType}>($entries)');
}
visitInstanceConstant(InstanceConstant node) {
final String name = syntheticNames.nameConstant(node);
write(' $name = ');
final sb = new StringBuffer();
sb.write('${node.classNode}');
if (!node.classNode.typeParameters.isEmpty) {
sb.write('<');
sb.write(node.typeArguments.map((type) => type.toString()).join(', '));
sb.write('>');
}
sb.write(' {');
bool first = true;
node.fieldValues.forEach((Reference fieldRef, Constant constant) {
final String name = syntheticNames.nameConstant(constant);
if (!first) {
sb.write(', ');
}
sb.write('${fieldRef.asField.name}: $name');
first = false;
});
sb.write('}');
endLine(sb.toString());
}
visitStringConstant(StringConstant node) {
final String name = syntheticNames.nameConstant(node);
endLine(' $name = "${escapeString(node.value)}"');
}
visitUnevaluatedConstant(UnevaluatedConstant node) {
final String name = syntheticNames.nameConstant(node);
write(' $name = (');
writeExpression(node.expression);
endLine(')');
}
defaultNode(Node node) {
write('<${node.runtimeType}>');
}
}
class Precedence extends 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,
};
static bool isAssociativeBinaryOperator(int precedence) {
return precedence != EQUALITY && precedence != RELATIONAL;
}
int defaultExpression(Expression node) => EXPRESSION;
int visitInvalidExpression(InvalidExpression node) => CALLEE;
int visitMethodInvocation(MethodInvocation node) => CALLEE;
int visitSuperMethodInvocation(SuperMethodInvocation node) => CALLEE;
int visitDirectMethodInvocation(DirectMethodInvocation node) => CALLEE;
int visitStaticInvocation(StaticInvocation node) => CALLEE;
int visitConstructorInvocation(ConstructorInvocation node) => CALLEE;
int visitNot(Not node) => PREFIX;
int visitLogicalExpression(LogicalExpression node) =>
binaryPrecedence[node.operator];
int visitConditionalExpression(ConditionalExpression node) => CONDITIONAL;
int visitStringConcatenation(StringConcatenation node) => PRIMARY;
int visitIsExpression(IsExpression node) => RELATIONAL;
int visitAsExpression(AsExpression node) => RELATIONAL;
int visitSymbolLiteral(SymbolLiteral node) => PRIMARY;
int visitTypeLiteral(TypeLiteral node) => PRIMARY;
int visitThisExpression(ThisExpression node) => CALLEE;
int visitRethrow(Rethrow node) => PRIMARY;
int visitThrow(Throw node) => EXPRESSION;
int visitListLiteral(ListLiteral node) => PRIMARY;
int visitSetLiteral(SetLiteral node) => PRIMARY;
int visitMapLiteral(MapLiteral node) => PRIMARY;
int visitAwaitExpression(AwaitExpression node) => PREFIX;
int visitFunctionExpression(FunctionExpression node) => EXPRESSION;
int visitStringLiteral(StringLiteral node) => CALLEE;
int visitIntLiteral(IntLiteral node) => CALLEE;
int visitDoubleLiteral(DoubleLiteral node) => CALLEE;
int visitBoolLiteral(BoolLiteral node) => CALLEE;
int visitNullLiteral(NullLiteral node) => CALLEE;
int visitVariableGet(VariableGet node) => PRIMARY;
int visitVariableSet(VariableSet node) => EXPRESSION;
int visitPropertyGet(PropertyGet node) => PRIMARY;
int visitPropertySet(PropertySet node) => EXPRESSION;
int visitSuperPropertyGet(SuperPropertyGet node) => PRIMARY;
int visitSuperPropertySet(SuperPropertySet node) => EXPRESSION;
int visitDirectPropertyGet(DirectPropertyGet node) => PRIMARY;
int visitDirectPropertySet(DirectPropertySet node) => EXPRESSION;
int visitStaticGet(StaticGet node) => PRIMARY;
int visitStaticSet(StaticSet node) => EXPRESSION;
int visitLet(Let 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';
}
throw 'illegal ProcedureKind: $kind';
}
class ExpressionPrinter {
final Printer writeer;
final int minimumPrecedence;
ExpressionPrinter(this.writeer, this.minimumPrecedence);
}