blob: 7252dccf6b1d0bc6f887367cad782b1b7d3b2366 [file] [log] [blame]
// 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_binary;
import 'dart:convert';
import 'dart:developer';
import 'dart:io' show BytesBuilder;
import 'dart:typed_data';
import '../ast.dart';
import 'ast_from_binary.dart' show mergeCompilationModeOrThrow;
import 'tag.dart';
/// Writes to a binary file.
///
/// A [BinaryPrinter] can be used to write one file and must then be
/// discarded.
class BinaryPrinter implements Visitor<void>, BinarySink {
VariableIndexer? _variableIndexer;
LabelIndexer? _labelIndexer;
SwitchCaseIndexer? _switchCaseIndexer;
TypeParameterIndexer _typeParameterIndexer = new TypeParameterIndexer();
final StringIndexer stringIndexer;
late ConstantIndexer _constantIndexer;
final UriIndexer _sourceUriIndexer = new UriIndexer();
bool _currentlyInNonimplementation = false;
final List<bool> _sourcesFromRealImplementation = <bool>[];
final List<bool> _sourcesUsedInLibrary = <bool>[];
Map<LibraryDependency, int> _libraryDependencyIndex =
<LibraryDependency, int>{};
NonNullableByDefaultCompiledMode? compilationMode;
List<_MetadataSubsection>? _metadataSubsections;
final BufferedSink _mainSink;
final BufferedSink _metadataSink;
final BytesSink _constantsBytesSink;
late BufferedSink _constantsSink;
late BufferedSink _sink;
final bool includeSources;
final bool includeOffsets;
final LibraryFilter? libraryFilter;
late List<int> libraryOffsets;
late List<int> classOffsets;
late List<int> procedureOffsets;
int _binaryOffsetForSourceTable = -1;
int _binaryOffsetForLinkTable = -1;
int _binaryOffsetForMetadataPayloads = -1;
int _binaryOffsetForMetadataMappings = -1;
int _binaryOffsetForStringTable = -1;
int _binaryOffsetForConstantTable = -1;
late List<CanonicalName> _canonicalNameList;
Set<CanonicalName> _knownCanonicalNameNonRootTops = new Set<CanonicalName>();
Library? _currentLibrary;
/// Create a printer that writes to the given [sink].
///
/// The BinaryPrinter will use its own buffer, so the [sink] does not need
/// one.
BinaryPrinter(Sink<List<int>> sink,
{this.libraryFilter,
StringIndexer? stringIndexer,
this.includeSources = true,
this.includeOffsets = true})
: _mainSink = new BufferedSink(sink),
_metadataSink = new BufferedSink(new BytesSink()),
_constantsBytesSink = new BytesSink(),
stringIndexer = stringIndexer ?? new StringIndexer() {
_constantsSink = new BufferedSink(_constantsBytesSink);
_constantIndexer = new ConstantIndexer(this.stringIndexer, this);
_sink = _mainSink;
}
void _flush() {
_sink.flushAndDestroy();
}
int _getVariableIndex(VariableDeclaration variable) {
int? index = (_variableIndexer ??= new VariableIndexer())[variable];
assert(index != null, "No index found for ${variable}");
return index!;
}
void writeByte(int byte) {
assert((byte & 0xFF) == byte);
_sink.addByte(byte);
}
void writeBytes(List<int> bytes) {
_sink.addBytes(bytes);
}
@pragma("vm:prefer-inline")
void writeUInt30(int value) {
assert(value >= 0 && value >> 30 == 0);
if (value < 0x80) {
_sink.addByte(value);
} else if (value < 0x4000) {
_sink.addByte2((value >> 8) | 0x80, value & 0xFF);
} else {
_sink.addByte4((value >> 24) | 0xC0, (value >> 16) & 0xFF,
(value >> 8) & 0xFF, value & 0xFF);
}
}
void writeUInt32(int value) {
_sink.addByte4((value >> 24) & 0xFF, (value >> 16) & 0xFF,
(value >> 8) & 0xFF, value & 0xFF);
}
void writeByteList(List<int> bytes) {
writeUInt30(bytes.length);
writeBytes(bytes);
}
int getBufferOffset() {
return _sink.offset;
}
void writeStringTable(StringIndexer indexer) {
_binaryOffsetForStringTable = getBufferOffset();
// Containers for the WTF-8 encoded strings.
final List<Uint8List> data = <Uint8List>[];
int totalLength = 0;
const int minLength = 1 << 16;
Uint8List? buffer;
int index = 0;
// Write the end offsets.
writeUInt30(indexer.index.length);
for (String key in indexer.index.keys) {
if (key.isNotEmpty) {
int requiredMinLength = key.length;
int allocateMinLength = requiredMinLength * 3;
int newIndex;
while (true) {
if (buffer == null || index + requiredMinLength >= buffer.length) {
int newLength = minLength;
if (allocateMinLength > newLength) newLength = allocateMinLength;
if (buffer != null && index > 0) {
data.add(new Uint8List.view(buffer.buffer, 0, index));
}
index = 0;
buffer = new Uint8List(newLength);
}
newIndex = _writeWtf8(buffer, index, key);
if (newIndex != -1) break;
requiredMinLength = allocateMinLength;
}
assert(newIndex >= 0);
totalLength += newIndex - index;
index = newIndex;
}
writeUInt30(totalLength);
}
if (buffer != null && index > 0) {
data.add(Uint8List.view(buffer.buffer, 0, index));
}
// Write the WTF-8 encoded strings.
for (int i = 0; i < data.length; ++i) {
writeBytes(data[i]);
}
}
void writeStringReference(String string) {
writeUInt30(stringIndexer.put(string));
}
void writeStringReferenceList(List<String> strings) {
writeList(strings, writeStringReference);
}
void writeConstantReference(Constant constant) {
writeUInt30(_constantIndexer.put(constant));
}
void writeConstantTable(ConstantIndexer indexer) {
_binaryOffsetForConstantTable = getBufferOffset();
writeUInt30(indexer.entries.length);
assert(identical(_sink, _mainSink));
_constantsSink.flushAndDestroy();
writeBytes(_constantsBytesSink.builder.takeBytes());
}
int writeConstantTableEntry(Constant constant) {
TypeParameterIndexer oldTypeParameterIndexer = _typeParameterIndexer;
_typeParameterIndexer = new TypeParameterIndexer();
BufferedSink oldSink = _sink;
_sink = _constantsSink;
int initialOffset = _sink.offset;
if (constant is NullConstant) {
writeByte(ConstantTag.NullConstant);
} else if (constant is BoolConstant) {
writeByte(ConstantTag.BoolConstant);
writeByte(constant.value ? 1 : 0);
} else if (constant is IntConstant) {
writeByte(ConstantTag.IntConstant);
writeInteger(constant.value);
} else if (constant is DoubleConstant) {
writeByte(ConstantTag.DoubleConstant);
writeDouble(constant.value);
} else if (constant is StringConstant) {
writeByte(ConstantTag.StringConstant);
writeStringReference(constant.value);
} else if (constant is SymbolConstant) {
writeByte(ConstantTag.SymbolConstant);
writeNullAllowedReference(constant.libraryReference);
writeStringReference(constant.name);
} else if (constant is MapConstant) {
writeByte(ConstantTag.MapConstant);
writeDartType(constant.keyType);
writeDartType(constant.valueType);
writeUInt30(constant.entries.length);
for (final ConstantMapEntry entry in constant.entries) {
writeConstantReference(entry.key);
writeConstantReference(entry.value);
}
} else if (constant is ListConstant) {
writeByte(ConstantTag.ListConstant);
writeDartType(constant.typeArgument);
writeUInt30(constant.entries.length);
constant.entries.forEach(writeConstantReference);
} else if (constant is SetConstant) {
writeByte(ConstantTag.SetConstant);
writeDartType(constant.typeArgument);
writeUInt30(constant.entries.length);
constant.entries.forEach(writeConstantReference);
} else if (constant is InstanceConstant) {
writeByte(ConstantTag.InstanceConstant);
writeClassReference(constant.classNode);
writeUInt30(constant.typeArguments.length);
constant.typeArguments.forEach(writeDartType);
writeUInt30(constant.fieldValues.length);
constant.fieldValues.forEach((Reference fieldRef, Constant value) {
writeNonNullCanonicalNameReference(fieldRef.canonicalName!);
writeConstantReference(value);
});
} else if (constant is PartialInstantiationConstant) {
writeByte(ConstantTag.PartialInstantiationConstant);
writeConstantReference(constant.tearOffConstant);
final int length = constant.types.length;
writeUInt30(length);
for (int i = 0; i < length; ++i) {
writeDartType(constant.types[i]);
}
} else if (constant is TearOffConstant) {
writeByte(ConstantTag.TearOffConstant);
writeNonNullCanonicalNameReference(
constant.procedure.reference.canonicalName!);
} else if (constant is TypeLiteralConstant) {
writeByte(ConstantTag.TypeLiteralConstant);
writeDartType(constant.type);
} else if (constant is UnevaluatedConstant) {
writeByte(ConstantTag.UnevaluatedConstant);
writeNode(constant.expression);
} else {
throw new ArgumentError('Unsupported constant $constant');
}
_sink = oldSink;
_typeParameterIndexer = oldTypeParameterIndexer;
return _constantsSink.offset - initialOffset;
}
void writeDartType(DartType type) {
type.accept(this);
}
// Returns the new active file uri.
void writeUriReference(Uri uri) {
final int index = _sourceUriIndexer.put(uri);
writeUInt30(index);
if (!_currentlyInNonimplementation) {
if (_sourcesFromRealImplementation.length <= index) {
_sourcesFromRealImplementation.length = index + 1;
}
_sourcesFromRealImplementation[index] = true;
}
if (_sourcesUsedInLibrary.length <= index) {
_sourcesUsedInLibrary.length = index + 1;
}
_sourcesUsedInLibrary[index] = true;
}
void writeList<T>(List<T> items, void writeItem(T x)) {
writeUInt30(items.length);
for (int i = 0; i < items.length; ++i) {
writeItem(items[i]);
}
}
void writeNodeList(List<Node> nodes) {
final int len = nodes.length;
writeUInt30(len);
for (int i = 0; i < len; i++) {
final Node node = nodes[i];
writeNode(node);
}
}
void writeProcedureNodeList(List<Procedure> nodes) {
final int len = nodes.length;
writeUInt30(len);
for (int i = 0; i < len; i++) {
final Procedure node = nodes[i];
writeProcedureNode(node);
}
}
void writeFieldNodeList(List<Field> nodes) {
final int len = nodes.length;
writeUInt30(len);
for (int i = 0; i < len; i++) {
final Field node = nodes[i];
writeFieldNode(node);
}
}
void writeClassNodeList(List<Class> nodes) {
final int len = nodes.length;
writeUInt30(len);
for (int i = 0; i < len; i++) {
final Class node = nodes[i];
writeClassNode(node);
}
}
void writeExtensionNodeList(List<Extension> nodes) {
final int len = nodes.length;
writeUInt30(len);
for (int i = 0; i < len; i++) {
final Extension node = nodes[i];
writeExtensionNode(node);
}
}
void writeConstructorNodeList(List<Constructor> nodes) {
final int len = nodes.length;
writeUInt30(len);
for (int i = 0; i < len; i++) {
final Constructor node = nodes[i];
writeConstructorNode(node);
}
}
void writeRedirectingFactoryConstructorNodeList(
List<RedirectingFactoryConstructor> nodes) {
final int len = nodes.length;
writeUInt30(len);
for (int i = 0; i < len; i++) {
final RedirectingFactoryConstructor node = nodes[i];
writeRedirectingFactoryConstructorNode(node);
}
}
void writeSwitchCaseNodeList(List<SwitchCase> nodes) {
final int len = nodes.length;
writeUInt30(len);
for (int i = 0; i < len; i++) {
final SwitchCase node = nodes[i];
writeSwitchCaseNode(node);
}
}
void writeCatchNodeList(List<Catch> nodes) {
final int len = nodes.length;
writeUInt30(len);
for (int i = 0; i < len; i++) {
final Catch node = nodes[i];
writeCatchNode(node);
}
}
void writeTypedefNodeList(List<Typedef> nodes) {
final int len = nodes.length;
writeUInt30(len);
for (int i = 0; i < len; i++) {
final Typedef node = nodes[i];
writeTypedefNode(node);
}
}
void writeNode(Node node) {
if (_metadataSubsections != null) {
_writeNodeMetadata(node);
}
node.accept(this);
}
void writeFunctionNode(FunctionNode node) {
if (_metadataSubsections != null) {
_writeNodeMetadata(node);
}
node.accept(this);
}
void writeArgumentsNode(Arguments node) {
if (_metadataSubsections != null) {
_writeNodeMetadata(node);
}
node.accept(this);
}
void writeLibraryNode(Library node) {
if (_metadataSubsections != null) {
_writeNodeMetadata(node);
}
node.accept(this);
}
void writeProcedureNode(Procedure node) {
if (_metadataSubsections != null) {
_writeNodeMetadata(node);
}
node.accept(this);
}
void writeFieldNode(Field node) {
if (_metadataSubsections != null) {
_writeNodeMetadata(node);
}
node.accept(this);
}
void writeClassNode(Class node) {
if (_metadataSubsections != null) {
_writeNodeMetadata(node);
}
node.accept(this);
}
void writeExtensionNode(Extension node) {
if (_metadataSubsections != null) {
_writeNodeMetadata(node);
}
node.accept(this);
}
void writeConstructorNode(Constructor node) {
if (_metadataSubsections != null) {
_writeNodeMetadata(node);
}
node.accept(this);
}
void writeRedirectingFactoryConstructorNode(
RedirectingFactoryConstructor node) {
if (_metadataSubsections != null) {
_writeNodeMetadata(node);
}
node.accept(this);
}
void writeSwitchCaseNode(SwitchCase node) {
if (_metadataSubsections != null) {
_writeNodeMetadata(node);
}
node.accept(this);
}
void writeCatchNode(Catch node) {
if (_metadataSubsections != null) {
_writeNodeMetadata(node);
}
node.accept(this);
}
void writeTypedefNode(Typedef node) {
if (_metadataSubsections != null) {
_writeNodeMetadata(node);
}
node.accept(this);
}
void writeOptionalNode(Node? node) {
if (node == null) {
writeByte(Tag.Nothing);
} else {
writeByte(Tag.Something);
writeNode(node);
}
}
void writeLinkTable(Component component) {
_binaryOffsetForLinkTable = getBufferOffset();
writeList(_canonicalNameList, writeCanonicalNameEntry);
}
void indexLinkTable(Component component) {
_canonicalNameList = <CanonicalName>[];
for (int i = 0; i < component.libraries.length; ++i) {
Library library = component.libraries[i];
if (libraryFilter == null || libraryFilter!(library)) {
_indexLinkTableInternal(library.reference.canonicalName!);
_knownCanonicalNameNonRootTops.add(library.reference.canonicalName!);
}
}
}
void _indexLinkTableInternal(CanonicalName node) {
node.index = _canonicalNameList.length;
_canonicalNameList.add(node);
Iterable<CanonicalName>? children = node.childrenOrNull;
if (children != null) {
for (CanonicalName child in children) {
_indexLinkTableInternal(child);
}
}
}
/// Compute canonical names for the whole component or parts of it.
void computeCanonicalNames(Component component) {
for (int i = 0; i < component.libraries.length; ++i) {
Library library = component.libraries[i];
if (libraryFilter == null || libraryFilter!(library)) {
component.computeCanonicalNamesForLibrary(library);
}
}
}
void writeCanonicalNameEntry(CanonicalName node) {
assert(node.isConsistent, node.getInconsistency());
CanonicalName parent = node.parent!;
if (parent.isRoot) {
writeUInt30(0);
} else {
writeUInt30(parent.index + 1);
}
writeStringReference(node.name);
}
void writeComponentFile(Component component) {
Timeline.timeSync("BinaryPrinter.writeComponentFile", () {
compilationMode = component.mode;
computeCanonicalNames(component);
final int componentOffset = getBufferOffset();
writeUInt32(Tag.ComponentFile);
writeUInt32(Tag.BinaryFormatVersion);
writeBytes(ascii.encode(expectedSdkHash));
writeListOfStrings(component.problemsAsJson);
indexLinkTable(component);
_collectMetadata(component);
if (_metadataSubsections != null) {
_writeNodeMetadataImpl(component, componentOffset);
}
libraryOffsets = <int>[];
Procedure? mainMethod = component.mainMethod;
if (mainMethod != null) {
checkCanonicalName(getCanonicalNameOfMemberGetter(mainMethod));
}
writeLibraries(component);
writeUriToSource(component.uriToSource);
writeLinkTable(component);
_writeMetadataSection(component);
writeStringTable(stringIndexer);
writeConstantTable(_constantIndexer);
List<Library> libraries = component.libraries;
if (libraryFilter != null) {
List<Library> librariesNew = <Library>[];
for (int i = 0; i < libraries.length; i++) {
Library library = libraries[i];
if (libraryFilter!(library)) librariesNew.add(library);
}
libraries = librariesNew;
}
writeComponentIndex(component, libraries);
_flush();
});
}
void writeListOfStrings(List<String>? strings) {
writeUInt30(strings?.length ?? 0);
if (strings != null) {
for (int i = 0; i < strings.length; i++) {
String s = strings[i];
outputStringViaBuffer(s, new Uint8List(s.length * 3));
}
}
}
/// Collect metadata repositories associated with the component.
void _collectMetadata(Component component) {
if (component.metadata.isNotEmpty) {
// Component might be loaded lazily - meaning that we can't
// just skip empty repositories here, they might be populated by
// the serialization process. Instead we will filter empty repositories
// later before writing the section out.
_metadataSubsections = component.metadata.values
.map((MetadataRepository repository) =>
new _MetadataSubsection(repository))
.toList();
}
}
/// Writes metadata associated with the given [Node].
void _writeNodeMetadata(Node node) {
_writeNodeMetadataImpl(node, getBufferOffset());
}
void _writeNodeMetadataImpl(Node node, int nodeOffset) {
for (_MetadataSubsection subsection in _metadataSubsections!) {
final MetadataRepository<Object?> repository = subsection.repository;
final Object? value = repository.mapping[node];
if (value == null) {
continue;
}
if (!MetadataRepository.isSupported(node)) {
throw new ArgumentError(
"Nodes of type ${node.runtimeType} can't have metadata.");
}
if (!identical(_sink, _mainSink)) {
throw new ArgumentError(
"Node written into metadata can't have metadata "
"(metadata: ${repository.tag}, node: ${node.runtimeType} $node)");
}
_sink = _metadataSink;
subsection.metadataMapping.add(nodeOffset);
subsection.metadataMapping.add(getBufferOffset());
repository.writeToBinary(value, node, this);
_sink = _mainSink;
}
}
@override
void enterScope(
{List<TypeParameter>? typeParameters,
bool memberScope: false,
bool variableScope: false}) {
if (typeParameters != null) {
_typeParameterIndexer.enter(typeParameters);
}
if (memberScope) {
_variableIndexer = null;
}
if (variableScope) {
_variableIndexer ??= new VariableIndexer();
_variableIndexer!.pushScope();
}
}
@override
void leaveScope(
{List<TypeParameter>? typeParameters,
bool memberScope: false,
bool variableScope: false}) {
if (variableScope) {
_variableIndexer!.popScope();
}
if (memberScope) {
_variableIndexer = null;
}
if (typeParameters != null) {
_typeParameterIndexer.exit(typeParameters);
}
}
void _writeMetadataSection(Component component) {
// Make sure metadata payloads section is 8-byte aligned,
// so certain kinds of metadata can contain aligned data.
const int metadataPayloadsAlignment = 8;
int padding = ((getBufferOffset() + metadataPayloadsAlignment - 1) &
-metadataPayloadsAlignment) -
getBufferOffset();
for (int i = 0; i < padding; ++i) {
writeByte(0);
}
_binaryOffsetForMetadataPayloads = getBufferOffset();
_metadataSubsections
?.removeWhere((_MetadataSubsection s) => s.metadataMapping.isEmpty);
if (_metadataSubsections == null || _metadataSubsections!.isEmpty) {
_binaryOffsetForMetadataMappings = getBufferOffset();
writeUInt32(0); // Empty section.
return;
}
assert(identical(_sink, _mainSink));
_metadataSink.flushAndDestroy();
writeBytes((_metadataSink._sink as BytesSink).builder.takeBytes());
// RList<MetadataMapping> metadataMappings
_binaryOffsetForMetadataMappings = getBufferOffset();
for (_MetadataSubsection subsection in _metadataSubsections!) {
// UInt32 tag
writeUInt32(stringIndexer.put(subsection.repository.tag));
// RList<Pair<UInt32, UInt32>> nodeOffsetToMetadataOffset
final int mappingLength = subsection.metadataMapping.length;
for (int i = 0; i < mappingLength; i += 2) {
writeUInt32(subsection.metadataMapping[i]); // node offset
writeUInt32(subsection.metadataMapping[i + 1]); // metadata offset
}
writeUInt32(mappingLength ~/ 2);
}
writeUInt32(_metadataSubsections!.length);
}
/// Write all of some of the libraries of the [component].
void writeLibraries(Component component) {
for (int i = 0; i < component.libraries.length; ++i) {
Library library = component.libraries[i];
if (libraryFilter == null || libraryFilter!(library)) {
writeLibraryNode(library);
}
}
}
void writeComponentIndex(Component component, List<Library> libraries) {
// It is allowed to concatenate several kernel binaries to create a
// multi-component kernel file. In order to maintain alignment of
// metadata sections within kernel binaries after concatenation,
// size of each kernel binary should be aligned.
// Component index is located at the end of a kernel binary, so padding
// is added before component index.
const int kernelFileAlignment = 8;
// Keep this in sync with number of writeUInt32 below.
int numComponentIndexEntries = 8 + libraryOffsets.length + 3;
int unalignedSize = getBufferOffset() + numComponentIndexEntries * 4;
int padding =
((unalignedSize + kernelFileAlignment - 1) & -kernelFileAlignment) -
unalignedSize;
for (int i = 0; i < padding; ++i) {
writeByte(0);
}
// Fixed-size ints at the end used as an index.
assert(_binaryOffsetForSourceTable >= 0);
writeUInt32(_binaryOffsetForSourceTable);
assert(_binaryOffsetForLinkTable >= 0);
writeUInt32(_binaryOffsetForLinkTable);
assert(_binaryOffsetForMetadataPayloads >= 0);
writeUInt32(_binaryOffsetForMetadataPayloads);
assert(_binaryOffsetForMetadataMappings >= 0);
writeUInt32(_binaryOffsetForMetadataMappings);
assert(_binaryOffsetForStringTable >= 0);
writeUInt32(_binaryOffsetForStringTable);
assert(_binaryOffsetForConstantTable >= 0);
writeUInt32(_binaryOffsetForConstantTable);
Procedure? mainMethod = component.mainMethod;
if (mainMethod == null) {
writeUInt32(0);
} else {
CanonicalName main = getCanonicalNameOfMemberGetter(mainMethod);
writeUInt32(main.index + 1);
}
assert(component.modeRaw != null, "Component mode not set.");
writeUInt32(component.mode.index);
assert(libraryOffsets.length == libraries.length);
for (int offset in libraryOffsets) {
writeUInt32(offset);
}
writeUInt32(_binaryOffsetForSourceTable); // end of last library.
writeUInt32(libraries.length);
writeUInt32(getBufferOffset() + 4); // total size.
}
void writeUriToSource(Map<Uri, Source> uriToSource) {
_binaryOffsetForSourceTable = getBufferOffset();
int length = _sourceUriIndexer.index.length;
writeUInt32(length);
List<int> index = new List<int>.filled(
length,
// Dummy element value.
-1);
// Write data.
int i = 0;
Uint8List buffer = new Uint8List(1 << 16);
for (Uri uri in _sourceUriIndexer.index.keys) {
index[i] = getBufferOffset();
Source? source = uriToSource[uri];
if (source == null ||
!(includeSources &&
_sourcesFromRealImplementation.length > i &&
_sourcesFromRealImplementation[i] == true)) {
source = new Source(
<int>[], const <int>[], source?.importUri, source?.fileUri);
}
String uriAsString = "$uri";
outputStringViaBuffer(uriAsString, buffer);
writeByteList(source.source);
{
List<int> lineStarts = source.lineStarts!;
writeUInt30(lineStarts.length);
int previousLineStart = 0;
for (int j = 0; j < lineStarts.length; ++j) {
int lineStart = lineStarts[j];
writeUInt30(lineStart - previousLineStart);
previousLineStart = lineStart;
}
}
String importUriAsString =
source.importUri == null ? "" : "${source.importUri}";
outputStringViaBuffer(importUriAsString, buffer);
{
Set<Reference>? coverage = source.constantCoverageConstructors;
if (coverage == null || coverage.isEmpty) {
writeUInt30(0);
} else {
writeUInt30(coverage.length);
for (Reference reference in coverage) {
writeNonNullReference(reference);
}
}
}
i++;
}
// Write index for random access.
for (int i = 0; i < index.length; ++i) {
writeUInt32(index[i]);
}
}
void outputStringViaBuffer(String s, Uint8List buffer) {
int length = _writeWtf8(buffer, 0, s);
if (length >= 0) {
writeUInt30(length);
for (int j = 0; j < length; j++) {
writeByte(buffer[j]);
}
} else {
// Uncommon case with very long url.
outputStringViaBuffer(s, new Uint8List(s.length * 3));
}
}
void writeLibraryDependencyReference(LibraryDependency node) {
int? index = _libraryDependencyIndex[node];
if (index == null) {
throw new ArgumentError(
'Reference to library dependency $node out of scope');
}
writeUInt30(index);
}
void writeNullAllowedInstanceMemberReference(Reference? reference) {
writeNullAllowedReference(reference);
writeNullAllowedReference(
getMemberReferenceGetter(reference?.asMember.memberSignatureOrigin));
}
void writeNullAllowedReference(Reference? reference) {
if (reference == null) {
writeUInt30(0);
} else {
assert(reference.isConsistent, reference.getInconsistency());
CanonicalName? name = reference.canonicalName;
if (name == null) {
throw new ArgumentError('Missing canonical name for $reference');
}
checkCanonicalName(name);
writeUInt30(name.index + 1);
}
}
void writeNonNullInstanceMemberReference(Reference reference) {
writeNonNullReference(reference);
writeNullAllowedReference(
getMemberReferenceGetter(reference.asMember.memberSignatureOrigin));
}
void writeNonNullReference(Reference reference) {
// ignore: unnecessary_null_comparison
if (reference == null) {
throw new ArgumentError('Got null reference');
} else {
assert(reference.isConsistent, reference.getInconsistency());
CanonicalName? name = reference.canonicalName;
if (name == null) {
throw new ArgumentError('Missing canonical name for $reference');
}
checkCanonicalName(name);
writeUInt30(name.index + 1);
}
}
void checkCanonicalName(CanonicalName node) {
if (_knownCanonicalNameNonRootTops.contains(node.nonRootTop)) return;
if (node.isRoot) return;
if (node.index >= 0 && node.index < _canonicalNameList.length) {
CanonicalName claim = _canonicalNameList[node.index];
if (node == claim) {
// Already has the claimed index.
return;
}
}
checkCanonicalName(node.parent!);
node.index = _canonicalNameList.length;
_canonicalNameList.add(node);
}
void writeNullAllowedCanonicalNameReference(CanonicalName? name) {
if (name == null) {
writeUInt30(0);
} else {
checkCanonicalName(name);
writeUInt30(name.index + 1);
}
}
void writeNonNullCanonicalNameReference(CanonicalName name) {
// ignore: unnecessary_null_comparison
if (name == null) {
throw new ArgumentError(
'Expected a canonical name to be valid but was `null`.');
} else {
checkCanonicalName(name);
writeUInt30(name.index + 1);
}
}
void writeLibraryReference(Library node, {bool allowNull: false}) {
if (node.reference.canonicalName == null && !allowNull) {
throw new ArgumentError(
'Expected a library reference to be valid but was `null`.');
}
writeNullAllowedCanonicalNameReference(node.reference.canonicalName);
}
writeOffset(int offset) {
// TODO(jensj): Delta-encoding.
// File offset ranges from -1 and up,
// but is here saved as unsigned (thus the +1)
if (!includeOffsets) {
writeUInt30(0);
} else {
writeUInt30(offset + 1);
}
}
void writeClassReference(Class class_) {
// ignore: unnecessary_null_comparison
if (class_ == null) {
throw new ArgumentError(
'Expected a class reference to be valid but was `null`.');
}
writeNonNullCanonicalNameReference(getCanonicalNameOfClass(class_));
}
void writeName(Name node) {
if (_metadataSubsections != null) {
_writeNodeMetadata(node);
}
writeStringReference(node.text);
// TODO: Consider a more compressed format for private names within the
// enclosing library.
if (node.isPrivate) {
writeLibraryReference(node.library!);
}
}
bool insideExternalLibrary = false;
@override
void visitLibrary(Library node) {
_currentLibrary = node;
libraryOffsets.add(getBufferOffset());
writeByte(node.flags);
assert(
mergeCompilationModeOrThrow(
compilationMode, node.nonNullableByDefaultCompiledMode) ==
compilationMode,
"Cannot have ${node.nonNullableByDefaultCompiledMode} "
"in component with mode $compilationMode");
writeUInt30(node.languageVersion.major);
writeUInt30(node.languageVersion.minor);
writeNonNullCanonicalNameReference(getCanonicalNameOfLibrary(node));
writeStringReference(node.name ?? '');
writeUriReference(node.fileUri);
writeListOfStrings(node.problemsAsJson);
enterScope(memberScope: true);
writeAnnotationList(node.annotations);
writeLibraryDependencies(node);
writeAdditionalExports(node.additionalExports);
writeLibraryParts(node);
leaveScope(memberScope: true);
writeTypedefNodeList(node.typedefs);
classOffsets = <int>[];
writeClassNodeList(node.classes);
classOffsets.add(getBufferOffset());
writeExtensionNodeList(node.extensions);
writeFieldNodeList(node.fields);
procedureOffsets = <int>[];
writeProcedureNodeList(node.procedures);
procedureOffsets.add(getBufferOffset());
// Dump all source-references used in this library; used by the VM.
int sourceReferencesOffset = getBufferOffset();
int sourceReferencesCount = 0;
// Note: We start at 1 because 0 is the null-entry and we don't want to
// include that.
for (int i = 1; i < _sourcesUsedInLibrary.length; i++) {
if (_sourcesUsedInLibrary[i] == true) {
sourceReferencesCount++;
}
}
writeUInt30(sourceReferencesCount);
for (int i = 1; i < _sourcesUsedInLibrary.length; i++) {
if (_sourcesUsedInLibrary[i] == true) {
writeUInt30(i);
_sourcesUsedInLibrary[i] = false;
}
}
// Fixed-size ints at the end used as an index.
writeUInt32(sourceReferencesOffset);
assert(classOffsets.length > 0);
for (int i = 0; i < classOffsets.length; ++i) {
int offset = classOffsets[i];
writeUInt32(offset);
}
writeUInt32(classOffsets.length - 1);
assert(procedureOffsets.length > 0);
for (int i = 0; i < procedureOffsets.length; ++i) {
int offset = procedureOffsets[i];
writeUInt32(offset);
}
writeUInt32(procedureOffsets.length - 1);
_currentLibrary = null;
}
void writeLibraryDependencies(Library library) {
_libraryDependencyIndex = library.dependencies.isEmpty
? const <LibraryDependency, int>{}
: <LibraryDependency, int>{};
writeUInt30(library.dependencies.length);
for (int i = 0; i < library.dependencies.length; ++i) {
LibraryDependency importNode = library.dependencies[i];
_libraryDependencyIndex[importNode] = i;
writeLibraryDependency(importNode);
}
}
void writeAdditionalExports(List<Reference> additionalExports) {
writeUInt30(additionalExports.length);
for (Reference ref in additionalExports) {
writeNonNullReference(ref);
}
}
void writeLibraryDependency(LibraryDependency node) {
if (_metadataSubsections != null) {
_writeNodeMetadata(node);
}
writeOffset(node.fileOffset);
writeByte(node.flags);
writeAnnotationList(node.annotations);
writeLibraryReference(node.targetLibrary, allowNull: true);
writeStringReference(node.name ?? '');
writeNodeList(node.combinators);
}
void visitCombinator(Combinator node) {
writeByte(node.isShow ? 1 : 0);
writeStringReferenceList(node.names);
}
void writeLibraryParts(Library library) {
writeUInt30(library.parts.length);
for (int i = 0; i < library.parts.length; ++i) {
LibraryPart partNode = library.parts[i];
writeLibraryPart(partNode);
}
}
void writeLibraryPart(LibraryPart node) {
if (_metadataSubsections != null) {
_writeNodeMetadata(node);
}
writeAnnotationList(node.annotations);
writeStringReference(node.partUri);
}
void visitTypedef(Typedef node) {
enterScope(memberScope: true);
writeNonNullCanonicalNameReference(getCanonicalNameOfTypedef(node));
writeUriReference(node.fileUri);
writeOffset(node.fileOffset);
writeStringReference(node.name);
writeAnnotationList(node.annotations);
enterScope(typeParameters: node.typeParameters, variableScope: true);
writeNodeList(node.typeParameters);
writeNode(node.type!);
enterScope(typeParameters: node.typeParametersOfFunctionType);
writeNodeList(node.typeParametersOfFunctionType);
writeVariableDeclarationList(node.positionalParameters);
writeVariableDeclarationList(node.namedParameters);
leaveScope(typeParameters: node.typeParametersOfFunctionType);
leaveScope(typeParameters: node.typeParameters, variableScope: true);
leaveScope(memberScope: true);
}
void writeAnnotation(Expression annotation) {
writeNode(annotation);
}
void writeAnnotationList(List<Expression> annotations) {
final int len = annotations.length;
writeUInt30(len);
for (int i = 0; i < len; i++) {
final Expression annotation = annotations[i];
writeAnnotation(annotation);
}
}
@override
void visitClass(Class node) {
classOffsets.add(getBufferOffset());
if (node.isAnonymousMixin) _currentlyInNonimplementation = true;
if (node.reference.canonicalName == null) {
throw new ArgumentError('Missing canonical name for $node');
}
writeByte(Tag.Class);
writeNonNullCanonicalNameReference(getCanonicalNameOfClass(node));
writeUriReference(node.fileUri);
writeOffset(node.startFileOffset);
writeOffset(node.fileOffset);
writeOffset(node.fileEndOffset);
writeByte(node.flags);
writeStringReference(node.name);
enterScope(memberScope: true);
writeAnnotationList(node.annotations);
leaveScope(memberScope: true);
enterScope(typeParameters: node.typeParameters);
writeNodeList(node.typeParameters);
writeOptionalNode(node.supertype);
writeOptionalNode(node.mixedInType);
writeNodeList(node.implementedTypes);
writeFieldNodeList(node.fields);
writeConstructorNodeList(node.constructors);
procedureOffsets = <int>[];
writeProcedureNodeList(node.procedures);
procedureOffsets.add(getBufferOffset());
writeRedirectingFactoryConstructorNodeList(
node.redirectingFactoryConstructors);
leaveScope(typeParameters: node.typeParameters);
assert(procedureOffsets.length > 0);
for (int i = 0; i < procedureOffsets.length; ++i) {
int offset = procedureOffsets[i];
writeUInt32(offset);
}
writeUInt32(procedureOffsets.length - 1);
_currentlyInNonimplementation = false;
}
@override
void visitConstructor(Constructor node) {
if (node.reference.canonicalName == null) {
throw new ArgumentError('Missing canonical name for $node');
}
enterScope(memberScope: true);
writeByte(Tag.Constructor);
writeNonNullCanonicalNameReference(getCanonicalNameOfMemberGetter(node));
writeUriReference(node.fileUri);
writeOffset(node.startFileOffset);
writeOffset(node.fileOffset);
writeOffset(node.fileEndOffset);
writeByte(node.flags);
writeName(node.name);
writeAnnotationList(node.annotations);
assert(node.function.typeParameters.isEmpty);
writeFunctionNode(node.function);
// Parameters are in scope in the initializers.
_variableIndexer ??= new VariableIndexer();
_variableIndexer!.restoreScope(node.function.positionalParameters.length +
node.function.namedParameters.length);
writeNodeList(node.initializers);
leaveScope(memberScope: true);
}
@override
void visitProcedure(Procedure node) {
assert(!(node.isMemberSignature && node.stubTargetReference == null),
"No member signature origin for member signature $node.");
assert(
!(node.abstractForwardingStubTarget is Procedure &&
(node.abstractForwardingStubTarget as Procedure).isMemberSignature),
"Forwarding stub interface target is member signature: $node.");
assert(
!(node.concreteForwardingStubTarget is Procedure &&
(node.concreteForwardingStubTarget as Procedure).isMemberSignature),
"Forwarding stub super target is member signature: $node.");
procedureOffsets.add(getBufferOffset());
CanonicalName? canonicalName = node.reference.canonicalName;
if (canonicalName == null) {
throw new ArgumentError('Missing canonical name for $node');
}
String? orphancy = node.reference.getOrphancyDescription(node);
if (orphancy != null) {
throw new ArgumentError(
'Trying to serialize orphaned procedure reference.\n'
'Orphaned procedure ${node} (${node.runtimeType}:${node.hashCode})\n'
'${orphancy}');
}
orphancy = canonicalName.getOrphancyDescription(node, node.reference);
if (orphancy != null) {
throw new ArgumentError(
'Trying to serialize orphaned procedure canonical name.\n'
'Orphaned procedure ${node} (${node.runtimeType}:${node.hashCode})\n'
'${orphancy}');
}
final bool currentlyInNonimplementationSaved =
_currentlyInNonimplementation;
if (node.isNoSuchMethodForwarder || node.isSyntheticForwarder) {
_currentlyInNonimplementation = true;
}
enterScope(memberScope: true);
writeByte(Tag.Procedure);
writeNonNullCanonicalNameReference(getCanonicalNameOfMemberGetter(node));
writeUriReference(node.fileUri);
writeOffset(node.startFileOffset);
writeOffset(node.fileOffset);
writeOffset(node.fileEndOffset);
writeByte(node.kind.index);
writeByte(node.stubKind.index);
writeUInt30(node.flags);
writeName(node.name);
writeAnnotationList(node.annotations);
writeNullAllowedReference(node.stubTargetReference);
writeFunctionNode(node.function);
leaveScope(memberScope: true);
_currentlyInNonimplementation = currentlyInNonimplementationSaved;
assert(
(node.concreteForwardingStubTarget != null) ||
!(node.isForwardingStub && node.function.body != null),
"Invalid forwarding stub $node.");
}
@override
void visitField(Field node) {
CanonicalName? getterCanonicalName = node.getterReference.canonicalName;
if (getterCanonicalName == null) {
throw new ArgumentError('Missing canonical name for $node');
}
String? getterOrphancy = node.getterReference.getOrphancyDescription(node);
if (getterOrphancy != null) {
throw new ArgumentError('Trying to serialize orphaned getter reference.\n'
'${getterOrphancy}');
}
getterOrphancy =
getterCanonicalName.getOrphancyDescription(node, node.getterReference);
if (getterOrphancy != null) {
throw new ArgumentError(
'Trying to serialize orphaned getter canonical name.\n'
'(${node.runtimeType}:${node.hashCode})\n'
'${getterOrphancy}');
}
CanonicalName? setterCanonicalName;
if (node.hasSetter) {
Reference setterReference = node.setterReference!;
setterCanonicalName = setterReference.canonicalName;
if (setterCanonicalName == null) {
throw new ArgumentError('Missing canonical name for $node');
}
String? setterOrphancy = setterReference.getOrphancyDescription(node);
if (setterOrphancy != null) {
throw new ArgumentError(
'Trying to serialize orphaned setter reference.\n'
'${setterOrphancy}');
}
setterOrphancy =
setterCanonicalName.getOrphancyDescription(node, setterReference);
if (setterOrphancy != null) {
throw new ArgumentError(
'Trying to serialize orphaned setter canonical name.\n'
'${setterOrphancy}');
}
}
enterScope(memberScope: true);
writeByte(Tag.Field);
writeNonNullCanonicalNameReference(getterCanonicalName);
writeNullAllowedCanonicalNameReference(setterCanonicalName);
writeUriReference(node.fileUri);
writeOffset(node.fileOffset);
writeOffset(node.fileEndOffset);
writeUInt30(node.flags);
writeName(node.name);
writeAnnotationList(node.annotations);
writeNode(node.type);
writeOptionalNode(node.initializer);
leaveScope(memberScope: true);
}
@override
void visitRedirectingFactoryConstructor(RedirectingFactoryConstructor node) {
if (node.reference.canonicalName == null) {
throw new ArgumentError('Missing canonical name for $node');
}
writeByte(Tag.RedirectingFactoryConstructor);
enterScope(
typeParameters: node.typeParameters,
memberScope: true,
variableScope: true);
writeNonNullCanonicalNameReference(getCanonicalNameOfMemberGetter(node));
writeUriReference(node.fileUri);
writeOffset(node.fileOffset);
writeOffset(node.fileEndOffset);
writeByte(node.flags);
writeName(node.name);
writeAnnotationList(node.annotations);
writeNonNullReference(node.targetReference!);
writeNodeList(node.typeArguments);
writeNodeList(node.typeParameters);
writeUInt30(node.positionalParameters.length + node.namedParameters.length);
writeUInt30(node.requiredParameterCount);
writeVariableDeclarationList(node.positionalParameters);
writeVariableDeclarationList(node.namedParameters);
leaveScope(
typeParameters: node.typeParameters,
memberScope: true,
variableScope: true);
}
@override
void visitInvalidInitializer(InvalidInitializer node) {
writeByte(Tag.InvalidInitializer);
writeByte(node.isSynthetic ? 1 : 0);
}
@override
void visitFieldInitializer(FieldInitializer node) {
writeByte(Tag.FieldInitializer);
writeByte(node.isSynthetic ? 1 : 0);
writeNonNullReference(node.fieldReference);
writeNode(node.value);
}
@override
void visitSuperInitializer(SuperInitializer node) {
writeByte(Tag.SuperInitializer);
writeByte(node.isSynthetic ? 1 : 0);
writeOffset(node.fileOffset);
writeNonNullReference(node.targetReference);
writeArgumentsNode(node.arguments);
}
@override
void visitRedirectingInitializer(RedirectingInitializer node) {
writeByte(Tag.RedirectingInitializer);
writeByte(node.isSynthetic ? 1 : 0);
writeOffset(node.fileOffset);
writeNonNullReference(node.targetReference);
writeArgumentsNode(node.arguments);
}
@override
void visitLocalInitializer(LocalInitializer node) {
writeByte(Tag.LocalInitializer);
writeByte(node.isSynthetic ? 1 : 0);
writeVariableDeclaration(node.variable);
}
@override
void visitAssertInitializer(AssertInitializer node) {
writeByte(Tag.AssertInitializer);
writeByte(node.isSynthetic ? 1 : 0);
writeNode(node.statement);
}
@override
void visitFunctionNode(FunctionNode node) {
writeByte(Tag.FunctionNode);
enterScope(typeParameters: node.typeParameters, variableScope: true);
LabelIndexer? oldLabels = _labelIndexer;
_labelIndexer = null;
SwitchCaseIndexer? oldCases = _switchCaseIndexer;
_switchCaseIndexer = null;
// Note: FunctionNode has no tag.
writeOffset(node.fileOffset);
writeOffset(node.fileEndOffset);
writeByte(node.asyncMarker.index);
writeByte(node.dartAsyncMarker.index);
writeNodeList(node.typeParameters);
writeUInt30(node.positionalParameters.length + node.namedParameters.length);
writeUInt30(node.requiredParameterCount);
writeVariableDeclarationList(node.positionalParameters);
writeVariableDeclarationList(node.namedParameters);
writeNode(node.returnType);
writeOptionalNode(node.futureValueType);
writeOptionalNode(node.body);
_labelIndexer = oldLabels;
_switchCaseIndexer = oldCases;
leaveScope(typeParameters: node.typeParameters, variableScope: true);
}
@override
void visitInvalidExpression(InvalidExpression node) {
writeByte(Tag.InvalidExpression);
writeOffset(node.fileOffset);
writeStringReference(node.message ?? '');
}
@override
void visitVariableGet(VariableGet node) {
int index = _getVariableIndex(node.variable);
if (index & Tag.SpecializedPayloadMask == index &&
node.promotedType == null) {
writeByte(Tag.SpecializedVariableGet + index);
writeOffset(node.fileOffset);
writeUInt30(node.variable.binaryOffsetNoTag);
} else {
writeByte(Tag.VariableGet);
writeOffset(node.fileOffset);
writeUInt30(node.variable.binaryOffsetNoTag);
writeUInt30(index);
writeOptionalNode(node.promotedType);
}
}
@override
void visitVariableSet(VariableSet node) {
int index = _getVariableIndex(node.variable);
if (index & Tag.SpecializedPayloadMask == index) {
writeByte(Tag.SpecializedVariableSet + index);
writeOffset(node.fileOffset);
writeUInt30(node.variable.binaryOffsetNoTag);
writeNode(node.value);
} else {
writeByte(Tag.VariableSet);
writeOffset(node.fileOffset);
writeUInt30(node.variable.binaryOffsetNoTag);
writeUInt30(index);
writeNode(node.value);
}
}
@override
void visitDynamicGet(DynamicGet node) {
writeByte(Tag.DynamicGet);
writeByte(node.kind.index);
writeOffset(node.fileOffset);
writeNode(node.receiver);
writeName(node.name);
}
@override
void visitInstanceGet(InstanceGet node) {
writeByte(Tag.InstanceGet);
writeByte(node.kind.index);
writeOffset(node.fileOffset);
writeNode(node.receiver);
writeName(node.name);
writeDartType(node.resultType);
writeNonNullInstanceMemberReference(node.interfaceTargetReference);
}
@override
void visitInstanceTearOff(InstanceTearOff node) {
writeByte(Tag.InstanceTearOff);
writeByte(node.kind.index);
writeOffset(node.fileOffset);
writeNode(node.receiver);
writeName(node.name);
writeDartType(node.resultType);
writeNonNullInstanceMemberReference(node.interfaceTargetReference);
}
@override
void visitPropertyGet(PropertyGet node) {
writeByte(Tag.PropertyGet);
writeOffset(node.fileOffset);
writeNode(node.receiver);
writeName(node.name);
writeNullAllowedInstanceMemberReference(node.interfaceTargetReference);
}
@override
void visitDynamicSet(DynamicSet node) {
writeByte(Tag.DynamicSet);
writeByte(node.kind.index);
writeOffset(node.fileOffset);
writeNode(node.receiver);
writeName(node.name);
writeNode(node.value);
}
@override
void visitInstanceSet(InstanceSet node) {
writeByte(Tag.InstanceSet);
writeByte(node.kind.index);
writeOffset(node.fileOffset);
writeNode(node.receiver);
writeName(node.name);
writeNode(node.value);
writeNonNullInstanceMemberReference(node.interfaceTargetReference);
}
@override
void visitPropertySet(PropertySet node) {
writeByte(Tag.PropertySet);
writeOffset(node.fileOffset);
writeNode(node.receiver);
writeName(node.name);
writeNode(node.value);
writeNullAllowedInstanceMemberReference(node.interfaceTargetReference);
}
@override
void visitSuperPropertyGet(SuperPropertyGet node) {
writeByte(Tag.SuperPropertyGet);
writeOffset(node.fileOffset);
writeName(node.name);
writeNullAllowedInstanceMemberReference(node.interfaceTargetReference);
}
@override
void visitSuperPropertySet(SuperPropertySet node) {
writeByte(Tag.SuperPropertySet);
writeOffset(node.fileOffset);
writeName(node.name);
writeNode(node.value);
writeNullAllowedInstanceMemberReference(node.interfaceTargetReference);
}
@override
void visitStaticGet(StaticGet node) {
writeByte(Tag.StaticGet);
writeOffset(node.fileOffset);
writeNonNullReference(node.targetReference);
}
@override
void visitStaticTearOff(StaticTearOff node) {
writeByte(Tag.StaticTearOff);
writeOffset(node.fileOffset);
writeNonNullReference(node.targetReference);
}
@override
void visitStaticSet(StaticSet node) {
writeByte(Tag.StaticSet);
writeOffset(node.fileOffset);
writeNonNullReference(node.targetReference);
writeNode(node.value);
}
@override
void visitDynamicInvocation(DynamicInvocation node) {
writeByte(Tag.DynamicInvocation);
writeByte(node.kind.index);
writeOffset(node.fileOffset);
writeNode(node.receiver);
writeName(node.name);
writeArgumentsNode(node.arguments);
}
@override
void visitEqualsCall(EqualsCall node) {
writeByte(Tag.EqualsCall);
writeOffset(node.fileOffset);
writeNode(node.left);
writeNode(node.right);
writeDartType(node.functionType);
writeNonNullInstanceMemberReference(node.interfaceTargetReference);
}
@override
void visitEqualsNull(EqualsNull node) {
writeByte(Tag.EqualsNull);
writeOffset(node.fileOffset);
writeNode(node.expression);
}
@override
void visitFunctionInvocation(FunctionInvocation node) {
writeByte(Tag.FunctionInvocation);
writeByte(node.kind.index);
writeOffset(node.fileOffset);
writeNode(node.receiver);
writeArgumentsNode(node.arguments);
// `const DynamicType()` is used to encode a missing function type.
writeDartType(node.functionType ?? const DynamicType());
}
@override
void visitInstanceInvocation(InstanceInvocation node) {
writeByte(Tag.InstanceInvocation);
writeByte(node.kind.index);
writeByte(node.flags);
writeOffset(node.fileOffset);
writeNode(node.receiver);
writeName(node.name);
writeArgumentsNode(node.arguments);
writeDartType(node.functionType);
writeNonNullInstanceMemberReference(node.interfaceTargetReference);
}
@override
void visitInstanceGetterInvocation(InstanceGetterInvocation node) {
writeByte(Tag.InstanceGetterInvocation);
writeByte(node.kind.index);
writeByte(node.flags);
writeOffset(node.fileOffset);
writeNode(node.receiver);
writeName(node.name);
writeArgumentsNode(node.arguments);
// `const DynamicType()` is used to encode a missing function type.
writeDartType(node.functionType ?? const DynamicType());
writeNonNullInstanceMemberReference(node.interfaceTargetReference);
}
@override
void visitLocalFunctionInvocation(LocalFunctionInvocation node) {
writeByte(Tag.LocalFunctionInvocation);
writeOffset(node.fileOffset);
int index = _getVariableIndex(node.variable);
writeUInt30(node.variable.binaryOffsetNoTag);
writeUInt30(index);
writeArgumentsNode(node.arguments);
writeDartType(node.functionType);
}
@override
void visitMethodInvocation(MethodInvocation node) {
writeByte(Tag.MethodInvocation);
writeByte(node.flags);
writeOffset(node.fileOffset);
writeNode(node.receiver);
writeName(node.name);
writeArgumentsNode(node.arguments);
writeNullAllowedInstanceMemberReference(node.interfaceTargetReference);
}
@override
void visitSuperMethodInvocation(SuperMethodInvocation node) {
writeByte(Tag.SuperMethodInvocation);
writeOffset(node.fileOffset);
writeName(node.name);
writeArgumentsNode(node.arguments);
writeNullAllowedInstanceMemberReference(node.interfaceTargetReference);
}
@override
void visitStaticInvocation(StaticInvocation node) {
writeByte(node.isConst ? Tag.ConstStaticInvocation : Tag.StaticInvocation);
writeOffset(node.fileOffset);
writeNonNullReference(node.targetReference);
writeArgumentsNode(node.arguments);
}
@override
void visitConstructorInvocation(ConstructorInvocation node) {
writeByte(node.isConst
? Tag.ConstConstructorInvocation
: Tag.ConstructorInvocation);
writeOffset(node.fileOffset);
writeNonNullReference(node.targetReference);
writeArgumentsNode(node.arguments);
}
@override
void visitArguments(Arguments node) {
writeUInt30(node.positional.length + node.named.length);
writeNodeList(node.types);
writeNodeList(node.positional);
writeNodeList(node.named);
}
@override
void visitNamedExpression(NamedExpression node) {
writeStringReference(node.name);
writeNode(node.value);
}
@override
void visitNot(Not node) {
writeByte(Tag.Not);
writeNode(node.operand);
}
@override
void visitNullCheck(NullCheck node) {
writeByte(Tag.NullCheck);
writeOffset(node.fileOffset);
writeNode(node.operand);
}
int logicalOperatorIndex(LogicalExpressionOperator operator) {
switch (operator) {
case LogicalExpressionOperator.AND:
return 0;
case LogicalExpressionOperator.OR:
return 1;
}
}
@override
void visitLogicalExpression(LogicalExpression node) {
writeByte(Tag.LogicalExpression);
writeNode(node.left);
writeByte(logicalOperatorIndex(node.operatorEnum));
writeNode(node.right);
}
@override
void visitConditionalExpression(ConditionalExpression node) {
writeByte(Tag.ConditionalExpression);
writeNode(node.condition);
writeNode(node.then);
writeNode(node.otherwise);
writeOptionalNode(node.staticType);
}
@override
void visitStringConcatenation(StringConcatenation node) {
writeByte(Tag.StringConcatenation);
writeOffset(node.fileOffset);
writeNodeList(node.expressions);
}
@override
void visitListConcatenation(ListConcatenation node) {
writeByte(Tag.ListConcatenation);
writeOffset(node.fileOffset);
writeNode(node.typeArgument);
writeNodeList(node.lists);
}
@override
void visitSetConcatenation(SetConcatenation node) {
writeByte(Tag.SetConcatenation);
writeOffset(node.fileOffset);
writeNode(node.typeArgument);
writeNodeList(node.sets);
}
@override
void visitMapConcatenation(MapConcatenation node) {
writeByte(Tag.MapConcatenation);
writeOffset(node.fileOffset);
writeNode(node.keyType);
writeNode(node.valueType);
writeNodeList(node.maps);
}
@override
void visitInstanceCreation(InstanceCreation node) {
writeByte(Tag.InstanceCreation);
writeOffset(node.fileOffset);
writeNonNullReference(node.classReference);
writeNodeList(node.typeArguments);
writeUInt30(node.fieldValues.length);
node.fieldValues.forEach((Reference fieldRef, Expression value) {
writeNonNullReference(fieldRef);
writeNode(value);
});
writeNodeList(node.asserts);
writeNodeList(node.unusedArguments);
}
@override
void visitFileUriExpression(FileUriExpression node) {
writeByte(Tag.FileUriExpression);
writeUriReference(node.fileUri);
writeOffset(node.fileOffset);
writeNode(node.expression);
}
@override
void visitIsExpression(IsExpression node) {
writeByte(Tag.IsExpression);
writeOffset(node.fileOffset);
writeByte(node.flags);
writeNode(node.operand);
writeNode(node.type);
}
@override
void visitAsExpression(AsExpression node) {
writeByte(Tag.AsExpression);
writeOffset(node.fileOffset);
writeByte(node.flags);
writeNode(node.operand);
writeNode(node.type);
}
@override
void visitStringLiteral(StringLiteral node) {
writeByte(Tag.StringLiteral);
writeStringReference(node.value);
}
@override
void visitIntLiteral(IntLiteral node) {
writeInteger(node.value);
}
writeInteger(int value) {
int biasedValue = value + Tag.SpecializedIntLiteralBias;
if (biasedValue >= 0 &&
biasedValue & Tag.SpecializedPayloadMask == biasedValue) {
writeByte(Tag.SpecializedIntLiteral + biasedValue);
} else if (value.abs() >> 30 == 0) {
if (value < 0) {
writeByte(Tag.NegativeIntLiteral);
writeUInt30(-value);
} else {
writeByte(Tag.PositiveIntLiteral);
writeUInt30(value);
}
} else {
// TODO: Pick a better format for big int literals.
writeByte(Tag.BigIntLiteral);
writeStringReference('$value');
}
}
@override
void visitDoubleLiteral(DoubleLiteral node) {
writeByte(Tag.DoubleLiteral);
writeDouble(node.value);
}
writeDouble(double value) {
_sink.addDouble(value);
}
@override
void visitBoolLiteral(BoolLiteral node) {
writeByte(node.value ? Tag.TrueLiteral : Tag.FalseLiteral);
}
@override
void visitNullLiteral(NullLiteral node) {
writeByte(Tag.NullLiteral);
}
@override
void visitSymbolLiteral(SymbolLiteral node) {
writeByte(Tag.SymbolLiteral);
writeStringReference(node.value);
}
@override
void visitTypeLiteral(TypeLiteral node) {
writeByte(Tag.TypeLiteral);
writeNode(node.type);
}
@override
void visitThisExpression(ThisExpression node) {
writeByte(Tag.ThisExpression);
}
@override
void visitRethrow(Rethrow node) {
writeByte(Tag.Rethrow);
writeOffset(node.fileOffset);
}
@override
void visitThrow(Throw node) {
writeByte(Tag.Throw);
writeOffset(node.fileOffset);
writeNode(node.expression);
}
@override
void visitListLiteral(ListLiteral node) {
writeByte(node.isConst ? Tag.ConstListLiteral : Tag.ListLiteral);
writeOffset(node.fileOffset);
writeNode(node.typeArgument);
writeNodeList(node.expressions);
}
@override
void visitSetLiteral(SetLiteral node) {
writeByte(node.isConst ? Tag.ConstSetLiteral : Tag.SetLiteral);
writeOffset(node.fileOffset);
writeNode(node.typeArgument);
writeNodeList(node.expressions);
}
@override
void visitMapLiteral(MapLiteral node) {
writeByte(node.isConst ? Tag.ConstMapLiteral : Tag.MapLiteral);
writeOffset(node.fileOffset);
writeNode(node.keyType);
writeNode(node.valueType);
writeNodeList(node.entries);
}
@override
void visitMapLiteralEntry(MapLiteralEntry node) {
// Note: there is no tag on MapEntry
writeNode(node.key);
writeNode(node.value);
}
@override
void visitAwaitExpression(AwaitExpression node) {
writeByte(Tag.AwaitExpression);
writeNode(node.operand);
}
@override
void visitFunctionExpression(FunctionExpression node) {
writeByte(Tag.FunctionExpression);
writeOffset(node.fileOffset);
writeFunctionNode(node.function);
}
@override
void visitLet(Let node) {
writeByte(Tag.Let);
writeOffset(node.fileOffset);
VariableIndexer variableIndexer =
_variableIndexer ??= new VariableIndexer();
variableIndexer.pushScope();
writeVariableDeclaration(node.variable);
writeNode(node.body);
variableIndexer.popScope();
}
@override
void visitBlockExpression(BlockExpression node) {
writeByte(Tag.BlockExpression);
VariableIndexer variableIndexer =
_variableIndexer ??= new VariableIndexer();
variableIndexer.pushScope();
writeNodeList(node.body.statements);
writeNode(node.value);
variableIndexer.popScope();
}
@override
void visitInstantiation(Instantiation node) {
writeByte(Tag.Instantiation);
writeNode(node.expression);
writeNodeList(node.typeArguments);
}
@override
void visitLoadLibrary(LoadLibrary node) {
writeByte(Tag.LoadLibrary);
writeLibraryDependencyReference(node.import);
}
@override
void visitCheckLibraryIsLoaded(CheckLibraryIsLoaded node) {
writeByte(Tag.CheckLibraryIsLoaded);
writeLibraryDependencyReference(node.import);
}
writeStatementOrEmpty(Statement? node) {
if (node == null) {
writeByte(Tag.EmptyStatement);
} else {
writeNode(node);
}
}
@override
void visitExpressionStatement(ExpressionStatement node) {
writeByte(Tag.ExpressionStatement);
writeNode(node.expression);
}
@override
void visitBlock(Block node) {
VariableIndexer variableIndexer =
_variableIndexer ??= new VariableIndexer();
variableIndexer.pushScope();
writeByte(Tag.Block);
writeOffset(node.fileOffset);
writeOffset(node.fileEndOffset);
writeNodeList(node.statements);
variableIndexer.popScope();
}
@override
void visitAssertBlock(AssertBlock node) {
VariableIndexer variableIndexer =
_variableIndexer ??= new VariableIndexer();
variableIndexer.pushScope();
writeByte(Tag.AssertBlock);
writeNodeList(node.statements);
variableIndexer.popScope();
}
@override
void visitEmptyStatement(EmptyStatement node) {
writeByte(Tag.EmptyStatement);
}
@override
void visitAssertStatement(AssertStatement node) {
writeByte(Tag.AssertStatement);
writeNode(node.condition);
writeOffset(node.conditionStartOffset);
writeOffset(node.conditionEndOffset);
writeOptionalNode(node.message);
}
@override
void visitLabeledStatement(LabeledStatement node) {
LabelIndexer labelIndexer = _labelIndexer ??= new LabelIndexer();
labelIndexer.enter(node);
writeByte(Tag.LabeledStatement);
writeNode(node.body);
labelIndexer.exit();
}
@override
void visitConstantExpression(ConstantExpression node) {
writeByte(Tag.ConstantExpression);
writeOffset(node.fileOffset);
writeDartType(node.type);
writeConstantReference(node.constant);
}
@override
void visitBreakStatement(BreakStatement node) {
writeByte(Tag.BreakStatement);
writeOffset(node.fileOffset);
writeUInt30(_labelIndexer![node.target]!);
}
@override
void visitWhileStatement(WhileStatement node) {
writeByte(Tag.WhileStatement);
writeOffset(node.fileOffset);
writeNode(node.condition);
writeNode(node.body);
}
@override
void visitDoStatement(DoStatement node) {
writeByte(Tag.DoStatement);
writeOffset(node.fileOffset);
writeNode(node.body);
writeNode(node.condition);
}
@override
void visitForStatement(ForStatement node) {
VariableIndexer variableIndexer =
_variableIndexer ??= new VariableIndexer();
variableIndexer.pushScope();
writeByte(Tag.ForStatement);
writeOffset(node.fileOffset);
writeVariableDeclarationList(node.variables);
writeOptionalNode(node.condition);
writeNodeList(node.updates);
writeNode(node.body);
variableIndexer.popScope();
}
@override
void visitForInStatement(ForInStatement node) {
VariableIndexer variableIndexer =
_variableIndexer ??= new VariableIndexer();
variableIndexer.pushScope();
writeByte(node.isAsync ? Tag.AsyncForInStatement : Tag.ForInStatement);
writeOffset(node.fileOffset);
writeOffset(node.bodyOffset);
writeVariableDeclaration(node.variable);
writeNode(node.iterable);
writeNode(node.body);
variableIndexer.popScope();
}
@override
void visitSwitchStatement(SwitchStatement node) {
SwitchCaseIndexer switchCaseIndexer =
_switchCaseIndexer ??= new SwitchCaseIndexer();
switchCaseIndexer.enter(node);
writeByte(Tag.SwitchStatement);
writeOffset(node.fileOffset);
writeNode(node.expression);
writeSwitchCaseNodeList(node.cases);
switchCaseIndexer.exit(node);
}
@override
void visitSwitchCase(SwitchCase node) {
// Note: there is no tag on SwitchCase.
int length = node.expressions.length;
writeUInt30(length);
for (int i = 0; i < length; ++i) {
writeOffset(node.expressionOffsets[i]);
writeNode(node.expressions[i]);
}
writeByte(node.isDefault ? 1 : 0);
writeNode(node.body);
}
@override
void visitContinueSwitchStatement(ContinueSwitchStatement node) {
writeByte(Tag.ContinueSwitchStatement);
writeOffset(node.fileOffset);
writeUInt30(_switchCaseIndexer![node.target]!);
}
@override
void visitIfStatement(IfStatement node) {
writeByte(Tag.IfStatement);
writeOffset(node.fileOffset);
writeNode(node.condition);
writeNode(node.then);
writeStatementOrEmpty(node.otherwise);
}
@override
void visitReturnStatement(ReturnStatement node) {
writeByte(Tag.ReturnStatement);
writeOffset(node.fileOffset);
writeOptionalNode(node.expression);
}
int _encodeTryCatchFlags(bool needsStackTrace, bool isSynthetic) {
return (needsStackTrace ? 1 : 0) | (isSynthetic ? 2 : 0);
}
@override
void visitTryCatch(TryCatch node) {
writeByte(Tag.TryCatch);
writeNode(node.body);
bool needsStackTrace = node.catches.any((Catch c) => c.stackTrace != null);
writeByte(_encodeTryCatchFlags(needsStackTrace, node.isSynthetic));
writeCatchNodeList(node.catches);
}
@override
void visitCatch(Catch node) {
// Note: there is no tag on Catch.
VariableIndexer variableIndexer =
_variableIndexer ??= new VariableIndexer();
variableIndexer.pushScope();
writeOffset(node.fileOffset);
writeNode(node.guard);
writeOptionalVariableDeclaration(node.exception);
writeOptionalVariableDeclaration(node.stackTrace);
writeNode(node.body);
variableIndexer.popScope();
}
@override
void visitTryFinally(TryFinally node) {
writeByte(Tag.TryFinally);
writeNode(node.body);
writeNode(node.finalizer);
}
@override
void visitYieldStatement(YieldStatement node) {
writeByte(Tag.YieldStatement);
writeOffset(node.fileOffset);
writeByte(node.flags);
writeNode(node.expression);
}
@override
void visitVariableDeclaration(VariableDeclaration node) {
writeByte(Tag.VariableDeclaration);
writeVariableDeclaration(node);
}
void writeVariableDeclaration(VariableDeclaration node) {
if (_metadataSubsections != null) {
_writeNodeMetadata(node);
}
node.binaryOffsetNoTag = getBufferOffset();
writeOffset(node.fileOffset);
writeOffset(node.fileEqualsOffset);
writeAnnotationList(node.annotations);
writeByte(node.flags);
writeStringReference(node.name ?? '');
writeNode(node.type);
writeOptionalNode(node.initializer);
// Declare the variable after its initializer. It is not in scope in its
// own initializer.
(_variableIndexer ??= new VariableIndexer()).declare(node);
}
void writeVariableDeclarationList(List<VariableDeclaration> nodes) {
writeList(nodes, writeVariableDeclaration);
}
void writeOptionalVariableDeclaration(VariableDeclaration? node) {
if (node == null) {
writeByte(Tag.Nothing);
} else {
writeByte(Tag.Something);
writeVariableDeclaration(node);
}
}
@override
void visitFunctionDeclaration(FunctionDeclaration node) {
writeByte(Tag.FunctionDeclaration);
writeOffset(node.fileOffset);
writeVariableDeclaration(node.variable);
writeFunctionNode(node.function);
}
@override
void visitNeverType(NeverType node) {
writeByte(Tag.NeverType);
writeByte(node.nullability.index);
}
@override
void visitInvalidType(InvalidType node) {
writeByte(Tag.InvalidType);
}
@override
void visitDynamicType(DynamicType node) {
writeByte(Tag.DynamicType);
}
@override
void visitVoidType(VoidType node) {
writeByte(Tag.VoidType);
}
@override
void visitInterfaceType(InterfaceType node) {
if (node.typeArguments.isEmpty) {
writeByte(Tag.SimpleInterfaceType);
writeByte(node.nullability.index);
writeNonNullReference(node.className);
} else {
writeByte(Tag.InterfaceType);
writeByte(node.nullability.index);
writeNonNullReference(node.className);
writeNodeList(node.typeArguments);
}
}
@override
void visitExtensionType(ExtensionType node) {
// TODO(dmitryas): Serialize ExtensionType.
node.onType.accept(this);
}
@override
void visitFutureOrType(FutureOrType node) {
// TODO(dmitryas): Remove special treatment of FutureOr when the VM supports
// the new encoding: just write the tag.
assert(_knownCanonicalNameNonRootTops.isNotEmpty);
CanonicalName root = _knownCanonicalNameNonRootTops.first;
while (!root.isRoot) {
root = root.parent!;
}
CanonicalName canonicalNameOfFutureOr =
root.getChild("dart:async").getChild("FutureOr");
writeByte(Tag.InterfaceType);
writeByte(node.declaredNullability.index);
checkCanonicalName(canonicalNameOfFutureOr);
writeUInt30(canonicalNameOfFutureOr.index + 1);
writeUInt30(1); // Type argument count.
writeNode(node.typeArgument);
}
@override
void visitNullType(NullType node) {
// TODO(dmitryas): Remove special treatment of Null when the VM supports the
// new encoding: just write the tag.
assert(_knownCanonicalNameNonRootTops.isNotEmpty);
CanonicalName root = _knownCanonicalNameNonRootTops.first;
while (!root.isRoot) {
root = root.parent!;
}
CanonicalName canonicalNameOfNull =
root.getChild("dart:core").getChild("Null");
writeByte(Tag.SimpleInterfaceType);
writeByte(node.declaredNullability.index);
checkCanonicalName(canonicalNameOfNull);
writeUInt30(canonicalNameOfNull.index + 1);
}
@override
void visitSupertype(Supertype node) {
// Writing nullability below is only necessary because
// BinaryBuilder.readSupertype reads the supertype as an InterfaceType and
// breaks it into components afterwards, and reading an InterfaceType
// requires the nullability byte.
if (node.typeArguments.isEmpty) {
writeByte(Tag.SimpleInterfaceType);
writeByte(_currentLibrary!.nonNullable.index);
writeNonNullReference(node.className);
} else {
writeByte(Tag.InterfaceType);
writeByte(_currentLibrary!.nonNullable.index);
writeNonNullReference(node.className);
writeNodeList(node.typeArguments);
}
}
@override
void visitFunctionType(FunctionType node) {
if (node.requiredParameterCount == node.positionalParameters.length &&
node.typeParameters.isEmpty &&
node.namedParameters.isEmpty &&
node.typedefType == null) {
writeByte(Tag.SimpleFunctionType);
writeByte(node.nullability.index);
writeNodeList(node.positionalParameters);
writeNode(node.returnType);
} else {
writeByte(Tag.FunctionType);
writeByte(node.nullability.index);
enterScope(typeParameters: node.typeParameters);
writeNodeList(node.typeParameters);
writeUInt30(node.requiredParameterCount);
writeUInt30(
node.positionalParameters.length + node.namedParameters.length);
writeNodeList(node.positionalParameters);
writeNodeList(node.namedParameters);
writeOptionalNode(node.typedefType);
writeNode(node.returnType);
leaveScope(typeParameters: node.typeParameters);
}
}
@override
void visitNamedType(NamedType node) {
writeStringReference(node.name);
writeNode(node.type);
int flags = (node.isRequired ? NamedType.FlagRequiredNamedType : 0);
writeByte(flags);
}
@override
void visitTypeParameterType(TypeParameterType node) {
writeByte(Tag.TypeParameterType);
writeByte(node.declaredNullability.index);
writeUInt30(_typeParameterIndexer[node.parameter]);
writeOptionalNode(node.promotedBound);
}
@override
void visitTypedefType(TypedefType node) {
writeByte(Tag.TypedefType);
writeByte(node.nullability.index);
writeNullAllowedReference(node.typedefReference);
writeNodeList(node.typeArguments);
}
@override
void visitTypeParameter(TypeParameter node) {
writeByte(node.flags);
writeAnnotationList(node.annotations);
if (node.isLegacyCovariant) {
writeByte(TypeParameter.legacyCovariantSerializationMarker);
} else {
writeByte(node.variance);
}
writeStringReference(node.name ?? '');
writeNode(node.bound);
writeNode(node.defaultType);
}
@override
void visitExtension(Extension node) {
if (node.reference.canonicalName == null) {
throw new ArgumentError('Missing canonical name for $node');
}
writeByte(Tag.Extension);
writeNonNullCanonicalNameReference(getCanonicalNameOfExtension(node));
writeStringReference(node.name);
writeAnnotationList(node.annotations);
writeUriReference(node.fileUri);
writeOffset(node.fileOffset);
enterScope(typeParameters: node.typeParameters);
writeNodeList(node.typeParameters);
writeDartType(node.onType);
leaveScope(typeParameters: node.typeParameters);
final int len = node.members.length;
writeUInt30(len);
for (int i = 0; i < len; i++) {
final ExtensionMemberDescriptor descriptor = node.members[i];
writeName(descriptor.name);
writeByte(descriptor.kind.index);
writeByte(descriptor.flags);
writeNonNullCanonicalNameReference(descriptor.member.canonicalName!);
}
}
@override
void visitFunctionTearOff(FunctionTearOff node) {
writeByte(Tag.FunctionTearOff);
writeOffset(node.fileOffset);
writeNode(node.receiver);
}
// ================================================================
// These are nodes that are never serialized directly. Reaching one
// during serialization is an error.
@override
void defaultNode(Node node) {
throw new UnsupportedError(
'serialization of generic Node: ${node} (${node.runtimeType})');
}
@override
void defaultConstant(Constant node) {
throw new UnsupportedError(
'serialization of generic Constant: ${node} (${node.runtimeType})');
}
@override
void defaultBasicLiteral(BasicLiteral node) {
throw new UnsupportedError(
'serialization of generic BasicLiteral: ${node} (${node.runtimeType})');
}
@override
void defaultConstantReference(Constant node) {
throw new UnsupportedError('serialization of generic Constant reference: '
'${node} (${node.runtimeType})');
}
@override
void defaultDartType(DartType node) {
throw new UnsupportedError(
'serialization of generic DartType: ${node} (${node.runtimeType})');
}
@override
void defaultExpression(Expression node) {
throw new UnsupportedError(
'serialization of generic Expression: ${node} (${node.runtimeType})');
}
@override
void defaultInitializer(Initializer node) {
throw new UnsupportedError(
'serialization of generic Initializer: ${node} (${node.runtimeType})');
}
@override
void defaultMember(Member node) {
throw new UnsupportedError(
'serialization of generic Member: ${node} (${node.runtimeType})');
}
@override
void defaultMemberReference(Member node) {
throw new UnsupportedError('serialization of generic Member reference: '
'${node} (${node.runtimeType})');
}
@override
void defaultStatement(Statement node) {
throw new UnsupportedError(
'serialization of generic Statement: ${node} (${node.runtimeType})');
}
@override
void defaultTreeNode(TreeNode node) {
throw new UnsupportedError(
'serialization of generic TreeNode: ${node} (${node.runtimeType})');
}
@override
void visitBoolConstant(BoolConstant node) {
throw new UnsupportedError('serialization of BoolConstants');
}
@override
void visitBoolConstantReference(BoolConstant node) {
throw new UnsupportedError('serialization of BoolConstant references');
}
@override
void visitClassReference(Class node) {
throw new UnsupportedError('serialization of Class references');
}
@override
void visitExtensionReference(Extension node) {
throw new UnsupportedError('serialization of Class references');
}
@override
void visitConstructorReference(Constructor node) {
throw new UnsupportedError('serialization of Constructor references');
}
@override
void visitDoubleConstant(DoubleConstant node) {
throw new UnsupportedError('serialization of DoubleConstants');
}
@override
void visitDoubleConstantReference(DoubleConstant node) {
throw new UnsupportedError('serialization of DoubleConstant references');
}
@override
void visitFieldReference(Field node) {
throw new UnsupportedError('serialization of Field references');
}
@override
void visitInstanceConstant(InstanceConstant node) {
throw new UnsupportedError('serialization of InstanceConstants');
}
@override
void visitInstanceConstantReference(InstanceConstant node) {
throw new UnsupportedError('serialization of InstanceConstant references');
}
@override
void visitIntConstant(IntConstant node) {
throw new UnsupportedError('serialization of IntConstants');
}
@override
void visitIntConstantReference(IntConstant node) {
throw new UnsupportedError('serialization of IntConstant references');
}
@override
void visitLibraryDependency(LibraryDependency node) {
throw new UnsupportedError('serialization of LibraryDependencies');
}
@override
void visitLibraryPart(LibraryPart node) {
throw new UnsupportedError('serialization of LibraryParts');
}
@override
void visitListConstant(ListConstant node) {
throw new UnsupportedError('serialization of ListConstants');
}
@override
void visitListConstantReference(ListConstant node) {
throw new UnsupportedError('serialization of ListConstant references');
}
@override
void visitSetConstant(SetConstant node) {
throw new UnsupportedError('serialization of SetConstants');
}
@override
void visitSetConstantReference(SetConstant node) {
throw new UnsupportedError('serialization of SetConstant references');
}
@override
void visitMapConstant(MapConstant node) {
throw new UnsupportedError('serialization of MapConstants');
}
@override
void visitMapConstantReference(MapConstant node) {
throw new UnsupportedError('serialization of MapConstant references');
}
@override
void visitName(Name node) {
throw new UnsupportedError('serialization of Names');
}
@override
void visitNullConstant(NullConstant node) {
throw new UnsupportedError('serialization of NullConstants');
}
@override
void visitNullConstantReference(NullConstant node) {
throw new UnsupportedError('serialization of NullConstant references');
}
@override
void visitProcedureReference(Procedure node) {
throw new UnsupportedError('serialization of Procedure references');
}
@override
void visitComponent(Component node) {
throw new UnsupportedError('serialization of Components');
}
@override
void visitRedirectingFactoryConstructorReference(
RedirectingFactoryConstructor node) {
throw new UnsupportedError(
'serialization of RedirectingFactoryConstructor references');
}
@override
void visitStringConstant(StringConstant node) {
throw new UnsupportedError('serialization of StringConstants');
}
@override
void visitStringConstantReference(StringConstant node) {
throw new UnsupportedError('serialization of StringConstant references');
}
@override
void visitSymbolConstant(SymbolConstant node) {
throw new UnsupportedError('serialization of SymbolConstants');
}
@override
void visitSymbolConstantReference(SymbolConstant node) {
throw new UnsupportedError('serialization of SymbolConstant references');
}
@override
void visitPartialInstantiationConstant(PartialInstantiationConstant node) {
throw new UnsupportedError(
'serialization of PartialInstantiationConstants ');
}
@override
void visitPartialInstantiationConstantReference(
PartialInstantiationConstant node) {
throw new UnsupportedError(
'serialization of PartialInstantiationConstant references');
}
@override
void visitTearOffConstant(TearOffConstant node) {
throw new UnsupportedError('serialization of TearOffConstants ');
}
@override
void visitTearOffConstantReference(TearOffConstant node) {
throw new UnsupportedError('serialization of TearOffConstant references');
}
@override
void visitTypeLiteralConstant(TypeLiteralConstant node) {
throw new UnsupportedError('serialization of TypeLiteralConstants');
}
@override
void visitTypeLiteralConstantReference(TypeLiteralConstant node) {
throw new UnsupportedError(
'serialization of TypeLiteralConstant references');
}
@override
void visitTypedefReference(Typedef node) {
throw new UnsupportedError('serialization of Typedef references');
}
@override
void visitUnevaluatedConstant(UnevaluatedConstant node) {
throw new UnsupportedError('serialization of UnevaluatedConstants');
}
@override
void visitUnevaluatedConstantReference(UnevaluatedConstant node) {
throw new UnsupportedError(
'serialization of UnevaluatedConstant references');
}
}
typedef bool LibraryFilter(Library _);
class VariableIndexer {
Map<VariableDeclaration, int>? index;
List<int>? scopes;
int stackHeight = 0;
void declare(VariableDeclaration node) {
(index ??= <VariableDeclaration, int>{})[node] = stackHeight++;
}
void pushScope() {
(scopes ??= <int>[]).add(stackHeight);
}
void popScope() {
stackHeight = scopes!.removeLast();
}
void restoreScope(int numberOfVariables) {
stackHeight += numberOfVariables;
}
int? operator [](VariableDeclaration node) {
return index == null ? null : index![node];
}
}
class LabelIndexer {
final Map<LabeledStatement, int> index = <LabeledStatement, int>{};
int stackHeight = 0;
void enter(LabeledStatement node) {
index[node] = stackHeight++;
}
void exit() {
--stackHeight;
}
int? operator [](LabeledStatement node) => index[node];
}
class SwitchCaseIndexer {
final Map<SwitchCase, int> index = <SwitchCase, int>{};
int stackHeight = 0;
void enter(SwitchStatement node) {
for (SwitchCase caseNode in node.cases) {
index[caseNode] = stackHeight++;
}
}
void exit(SwitchStatement node) {
stackHeight -= node.cases.length;
}
int? operator [](SwitchCase node) => index[node];
}
class ConstantIndexer extends RecursiveResultVisitor {
final StringIndexer stringIndexer;
final List<Constant> entries = <Constant>[];
final Map<Constant, int> offsets = <Constant, int>{};
int nextOffset = 0;
final BinaryPrinter _printer;
ConstantIndexer(this.stringIndexer, this._printer);
int put(Constant constant) {
final int? oldOffset = offsets[constant];
if (oldOffset != null) return oldOffset;
// Traverse DAG in post-order to ensure children have their offsets assigned
// before the parent.
constant.visitChildren(this);
if (constant is StringConstant) {
stringIndexer.put(constant.value);
} else if (constant is SymbolConstant) {
stringIndexer.put(constant.name);
} else if (constant is DoubleConstant) {
stringIndexer.put('${constant.value}');
} else if (constant is IntConstant) {
final int value = constant.value;
if ((value.abs() >> 30) != 0) {
stringIndexer.put('$value');
}
}
final int newOffset = nextOffset;
entries.add(constant);
nextOffset += _printer.writeConstantTableEntry(constant);
return offsets[constant] = newOffset;
}
defaultConstantReference(Constant node) {
put(node);
}
int? operator [](Constant node) => offsets[node];
}
class TypeParameterIndexer {
final Map<TypeParameter, int> index = <TypeParameter, int>{};
int stackHeight = 0;
void enter(List<TypeParameter> typeParameters) {
for (int i = 0; i < typeParameters.length; ++i) {
TypeParameter parameter = typeParameters[i];
index[parameter] = stackHeight;
++stackHeight;
}
}
void exit(List<TypeParameter> typeParameters) {
stackHeight -= typeParameters.length;
for (int i = 0; i < typeParameters.length; ++i) {
index.remove(typeParameters[i]);
}
}
int operator [](TypeParameter parameter) =>
index[parameter] ??
(throw new ArgumentError('Type parameter $parameter is not indexed'));
}
class StringIndexer {
// Note that the iteration order is important.
final Map<String, int> index = new Map<String, int>();
StringIndexer() {
put('');
}
int put(String string) {
int? result = index[string];
if (result == null) {
result = index.length;
index[string] = result;
}
return result;
}
int? operator [](String string) => index[string];
}
class UriIndexer {
// Note that the iteration order is important.
final Map<Uri, int> index = new Map<Uri, int>();
UriIndexer();
int put(Uri uri) {
int? result = index[uri];
if (result == null) {
result = index.length;
index[uri] = result;
}
return result;
}
}
/// Puts a buffer in front of a [Sink<List<int>>].
class BufferedSink {
static const int SIZE = 100000;
static const int SAFE_SIZE = SIZE - 5;
static const int SMALL = 10000;
final Sink<List<int>> _sink;
Uint8List _buffer = new Uint8List(SIZE);
int length = 0;
int flushedLength = 0;
Float64List _doubleBuffer = new Float64List(1);
Uint8List? _doubleBufferUint8;
int get offset => length + flushedLength;
BufferedSink(this._sink);
void addDouble(double d) {
Uint8List doubleBufferUint8 =
_doubleBufferUint8 ??= _doubleBuffer.buffer.asUint8List();
_doubleBuffer[0] = d;
addByte4(doubleBufferUint8[0], doubleBufferUint8[1], doubleBufferUint8[2],
doubleBufferUint8[3]);
addByte4(doubleBufferUint8[4], doubleBufferUint8[5], doubleBufferUint8[6],
doubleBufferUint8[7]);
}
@pragma("vm:prefer-inline")
void addByte(int byte) {
_buffer[length++] = byte;
if (length == SIZE) {
_sink.add(_buffer);
_buffer = new Uint8List(SIZE);
length = 0;
flushedLength += SIZE;
}
}
@pragma("vm:prefer-inline")
void addByte2(int byte1, int byte2) {
if (length < SAFE_SIZE) {
_buffer[length++] = byte1;
_buffer[length++] = byte2;
} else {
addByte(byte1);
addByte(byte2);
}
}
@pragma("vm:prefer-inline")
void addByte4(int byte1, int byte2, int byte3, int byte4) {
if (length < SAFE_SIZE) {
_buffer[length++] = byte1;
_buffer[length++] = byte2;
_buffer[length++] = byte3;
_buffer[length++] = byte4;
} else {
addByte(byte1);
addByte(byte2);
addByte(byte3);
addByte(byte4);
}
}
void addBytes(List<int> bytes) {
// Avoid copying a large buffer into the another large buffer. Also, if
// the bytes buffer is too large to fit in our own buffer, just emit both.
if (length + bytes.length < SIZE &&
(bytes.length < SMALL || length < SMALL)) {
_buffer.setRange(length, length + bytes.length, bytes);
length += bytes.length;
} else if (bytes.length < SMALL) {
// Flush as much as we can in the current buffer.
_buffer.setRange(length, SIZE, bytes);
_sink.add(_buffer);
// Copy over the remainder into a new buffer. It is guaranteed to fit
// because the input byte array is small.
int alreadyEmitted = SIZE - length;
int remainder = bytes.length - alreadyEmitted;
_buffer = new Uint8List(SIZE);
_buffer.setRange(0, remainder, bytes, alreadyEmitted);
length = remainder;
flushedLength += SIZE;
} else {
flush();
_sink.add(bytes);
flushedLength += bytes.length;
}
}
void flush() {
_sink.add(_buffer.sublist(0, length));
_buffer = new Uint8List(SIZE);
flushedLength += length;
length = 0;
}
void flushAndDestroy() {
_sink.add(_buffer.sublist(0, length));
}
}
/// Non-empty metadata subsection.
class _MetadataSubsection {
final MetadataRepository<Object?> repository;
/// List of (nodeOffset, metadataOffset) pairs.
/// Gradually filled by the writer as writing progresses, which by
/// construction guarantees that pairs are sorted by first component
/// (nodeOffset) in ascending order.
final List<int> metadataMapping = <int>[];
_MetadataSubsection(this.repository);
}
/// A [Sink] that directly writes data into a byte builder.
// TODO(dartbug.com/28316): Remove this wrapper class.
class BytesSink implements Sink<List<int>> {
final BytesBuilder builder = new BytesBuilder();
@override
void add(List<int> data) {
builder.add(data);
}
@override
void close() {
// Nothing to do.
}
}
/**
* Write [source] string into [target] starting at index [index].
*
* The output space needed is at most [source.length] * 3.
*
* Returns
* * Non-negative on success (the new index in [target]).
* * -1 when [target] doesn't have enough space. Note that [target] can be
* polluted starting at [index].
*/
int _writeWtf8(Uint8List target, int index, String source) {
int end = source.length;
if (end == 0) return index;
int length = target.length;
assert(index <= length);
int i = 0;
do {
int codeUnit = source.codeUnitAt(i++);
while (codeUnit < 128) {
// ASCII.
if (index >= length) return -1;
target[index++] = codeUnit;
if (i >= end) return index;
codeUnit = source.codeUnitAt(i++);
}
if (codeUnit < 0x800) {
// Two-byte sequence (11-bit unicode value).
index += 2;
if (index > length) return -1;
target[index - 2] = 0xC0 | (codeUnit >> 6);
target[index - 1] = 0x80 | (codeUnit & 0x3f);
} else if ((codeUnit & 0xFC00) == 0xD800 &&
i < end &&
(source.codeUnitAt(i) & 0xFC00) == 0xDC00) {
// Surrogate pair -> four-byte sequence (non-BMP unicode value).
index += 4;
if (index > length) return -1;
int codeUnit2 = source.codeUnitAt(i++);
int unicode = 0x10000 + ((codeUnit & 0x3FF) << 10) + (codeUnit2 & 0x3FF);
target[index - 4] = 0xF0 | (unicode >> 18);
target[index - 3] = 0x80 | ((unicode >> 12) & 0x3F);
target[index - 2] = 0x80 | ((unicode >> 6) & 0x3F);
target[index - 1] = 0x80 | (unicode & 0x3F);
} else {
// Three-byte sequence (16-bit unicode value), including lone
// surrogates.
index += 3;
if (index > length) return -1;
target[index - 3] = 0xE0 | (codeUnit >> 12);
target[index - 2] = 0x80 | ((codeUnit >> 6) & 0x3f);
target[index - 1] = 0x80 | (codeUnit & 0x3f);
}
} while (i < end);
return index;
}