47f09a619e
Before, the part-URI was serialized as a file-URI and conflated with source location information. The part-URI is the URI in the following declaration in a Dart source file: part "URI"; This is different from what we normally call a file-URI. A file URI is used to point to the source location of the part declaration, not the URI in the part declaration. Furthermore, the field was serialized using writeUriReference which only works for URIs that are in the uriToSource map on a Component. Although this might seem like a safe optimization, it doesn't work if the uriToSource map is omitted or if a part declaration refers to a missing file. Finally, due to the confusing use of fileUri, LibraryPart was mistakenly implementing FileUriNode and annotations were stripped of source locations if the source for the part were omitted from uriToSource. The partUri field is now an unresolved string that can be resolved against either the parent library's import- or file-URI to obtain either version as needed. Change-Id: I255cb4eeaf89928292ab32a2f6be9ead6cc8cee1 Reviewed-on: https://dart-review.googlesource.com/49500 Commit-Queue: Peter von der Ahé <ahe@google.com> Reviewed-by: Jens Johansen <jensj@google.com>
2269 lines
64 KiB
Dart
2269 lines
64 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_binary;
|
|
|
|
import 'dart:core' hide MapEntry;
|
|
|
|
import '../ast.dart';
|
|
import 'tag.dart';
|
|
import 'dart:convert';
|
|
import 'dart:typed_data';
|
|
import 'dart:collection';
|
|
|
|
/// 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;
|
|
final TypeParameterIndexer _typeParameterIndexer = new TypeParameterIndexer();
|
|
final StringIndexer stringIndexer;
|
|
ConstantIndexer _constantIndexer;
|
|
final StringIndexer _sourceUriIndexer = new StringIndexer();
|
|
final Set<Uri> _knownSourceUri = new Set<Uri>();
|
|
Map<LibraryDependency, int> _libraryDependencyIndex =
|
|
<LibraryDependency, int>{};
|
|
|
|
List<_MetadataSubsection> _metadataSubsections;
|
|
|
|
/// Map used to assign reference ids to nodes contained within metadata
|
|
/// payloads.
|
|
Map<Node, int> _nodeReferences;
|
|
|
|
final BufferedSink _sink;
|
|
|
|
List<int> libraryOffsets;
|
|
List<int> classOffsets;
|
|
List<int> procedureOffsets;
|
|
int _binaryOffsetForSourceTable = -1;
|
|
int _binaryOffsetForStringTable = -1;
|
|
int _binaryOffsetForLinkTable = -1;
|
|
int _binaryOffsetForConstantTable = -1;
|
|
|
|
List<CanonicalName> _canonicalNameList;
|
|
Set<CanonicalName> _knownCanonicalNameNonRootTops = new Set<CanonicalName>();
|
|
Set<CanonicalName> _reindexedCanonicalNames = new Set<CanonicalName>();
|
|
|
|
/// Create a printer that writes to the given [sink].
|
|
///
|
|
/// The BinaryPrinter will use its own buffer, so the [sink] does not need
|
|
/// one.
|
|
///
|
|
/// If multiple binaries are to be written based on the same IR, a shared
|
|
/// [globalIndexer] may be passed in to avoid rebuilding the same indices
|
|
/// in every printer.
|
|
BinaryPrinter(Sink<List<int>> sink, {StringIndexer stringIndexer})
|
|
: _sink = new BufferedSink(sink),
|
|
stringIndexer = stringIndexer ?? new StringIndexer() {
|
|
_constantIndexer = new ConstantIndexer(this.stringIndexer);
|
|
}
|
|
|
|
void _flush() {
|
|
_sink.flushAndDestroy();
|
|
}
|
|
|
|
void writeByte(int byte) {
|
|
_sink.addByte(byte);
|
|
}
|
|
|
|
void writeBytes(List<int> bytes) {
|
|
_sink.addBytes(bytes);
|
|
}
|
|
|
|
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> utf8Bytes) {
|
|
writeUInt30(utf8Bytes.length);
|
|
writeBytes(utf8Bytes);
|
|
}
|
|
|
|
int getBufferOffset() {
|
|
return _sink.flushedLength + _sink.length;
|
|
}
|
|
|
|
void writeStringTable(StringIndexer indexer) {
|
|
_binaryOffsetForStringTable = getBufferOffset();
|
|
|
|
// Write the end offsets.
|
|
writeUInt30(indexer.numberOfStrings);
|
|
int endOffset = 0;
|
|
for (var entry in indexer.entries) {
|
|
endOffset += entry.utf8Bytes.length;
|
|
writeUInt30(endOffset);
|
|
}
|
|
// Write the UTF-8 encoded strings.
|
|
for (var entry in indexer.entries) {
|
|
writeBytes(entry.utf8Bytes);
|
|
}
|
|
}
|
|
|
|
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);
|
|
for (final entry in indexer.entries) {
|
|
writeConstantTableEntry(entry);
|
|
}
|
|
}
|
|
|
|
void writeConstantTableEntry(Constant constant) {
|
|
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);
|
|
writeStringReference('${constant.value}');
|
|
} else if (constant is StringConstant) {
|
|
writeByte(ConstantTag.StringConstant);
|
|
writeStringReference(constant.value);
|
|
} 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 InstanceConstant) {
|
|
writeByte(ConstantTag.InstanceConstant);
|
|
writeClassReference(constant.klass);
|
|
writeUInt30(constant.typeArguments.length);
|
|
constant.typeArguments.forEach(writeDartType);
|
|
writeUInt30(constant.fieldValues.length);
|
|
constant.fieldValues.forEach((Reference fieldRef, Constant value) {
|
|
writeCanonicalNameReference(fieldRef.canonicalName);
|
|
writeConstantReference(value);
|
|
});
|
|
} else if (constant is TearOffConstant) {
|
|
writeByte(ConstantTag.TearOffConstant);
|
|
writeCanonicalNameReference(constant.procedure.canonicalName);
|
|
} else if (constant is TypeLiteralConstant) {
|
|
writeByte(ConstantTag.TypeLiteralConstant);
|
|
writeDartType(constant.type);
|
|
} else {
|
|
throw 'Unsupported constant $constant';
|
|
}
|
|
}
|
|
|
|
void writeDartType(DartType type) {
|
|
type.accept(this);
|
|
}
|
|
|
|
// The currently active file uri where we are writing [TreeNode]s from. If
|
|
// this is set to `null` we cannot write file offsets. The [writeOffset]
|
|
// helper function will ensure this.
|
|
Uri _activeFileUri;
|
|
|
|
// Returns the new active file uri.
|
|
Uri writeUriReference(Uri uri) {
|
|
if (_knownSourceUri.contains(uri)) {
|
|
final int index = _sourceUriIndexer.put(uri == null ? "" : "$uri");
|
|
writeUInt30(index);
|
|
return uri;
|
|
} else {
|
|
final int index = 0; // equivalent to index = _sourceUriIndexer[""];
|
|
writeUInt30(index);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
void writeList<T>(List<T> items, void writeItem(T x)) {
|
|
writeUInt30(items.length);
|
|
items.forEach(writeItem);
|
|
}
|
|
|
|
void writeNodeList(List<Node> nodes) {
|
|
final len = nodes.length;
|
|
writeUInt30(len);
|
|
for (var i = 0; i < len; i++) {
|
|
final node = nodes[i];
|
|
writeNode(node);
|
|
}
|
|
}
|
|
|
|
void writeNode(Node node) {
|
|
if (_metadataSubsections != null) {
|
|
_recordNodeOffsetForMetadataMapping(node);
|
|
}
|
|
node.accept(this);
|
|
}
|
|
|
|
void writeOptionalNode(Node node) {
|
|
if (node == null) {
|
|
writeByte(Tag.Nothing);
|
|
} else {
|
|
writeByte(Tag.Something);
|
|
writeNode(node);
|
|
}
|
|
}
|
|
|
|
void writeOptionalReference(Reference ref) {
|
|
if (ref == null) {
|
|
writeByte(Tag.Nothing);
|
|
} else {
|
|
writeByte(Tag.Something);
|
|
writeReference(ref);
|
|
}
|
|
}
|
|
|
|
void writeLinkTable(Component component) {
|
|
_binaryOffsetForLinkTable = getBufferOffset();
|
|
writeList(_canonicalNameList, writeCanonicalNameEntry);
|
|
}
|
|
|
|
void indexLinkTable(Component component) {
|
|
_canonicalNameList = <CanonicalName>[];
|
|
void visitCanonicalName(CanonicalName node) {
|
|
node.index = _canonicalNameList.length;
|
|
_canonicalNameList.add(node);
|
|
node.children.forEach(visitCanonicalName);
|
|
}
|
|
|
|
for (var library in component.libraries) {
|
|
if (!shouldWriteLibraryCanonicalNames(library)) continue;
|
|
visitCanonicalName(library.canonicalName);
|
|
_knownCanonicalNameNonRootTops.add(library.canonicalName);
|
|
}
|
|
}
|
|
|
|
/// Compute canonical names for the whole component or parts of it.
|
|
void computeCanonicalNames(Component component) {
|
|
component.computeCanonicalNames();
|
|
}
|
|
|
|
/// Return `true` if all canonical names of the [library] should be written
|
|
/// into the link table. If some libraries of the component are skipped,
|
|
/// then all the additional names referenced by the libraries that are written
|
|
/// by [writeLibraries] are automatically added.
|
|
bool shouldWriteLibraryCanonicalNames(Library library) => true;
|
|
|
|
void writeCanonicalNameEntry(CanonicalName node) {
|
|
var parent = node.parent;
|
|
if (parent.isRoot) {
|
|
writeUInt30(0);
|
|
} else {
|
|
writeUInt30(parent.index + 1);
|
|
}
|
|
writeStringReference(node.name);
|
|
}
|
|
|
|
void writeComponentFile(Component component) {
|
|
computeCanonicalNames(component);
|
|
final componentOffset = getBufferOffset();
|
|
writeUInt32(Tag.ComponentFile);
|
|
writeUInt32(Tag.BinaryFormatVersion);
|
|
indexLinkTable(component);
|
|
indexUris(component);
|
|
// Note: must write metadata payloads before any other node in the component
|
|
// to collect references to nodes contained within metadata payloads.
|
|
_writeMetadataPayloads(component);
|
|
if (_metadataSubsections != null) {
|
|
_recordNodeOffsetForMetadataMappingImpl(component, componentOffset);
|
|
}
|
|
libraryOffsets = <int>[];
|
|
CanonicalName main = getCanonicalNameOfMember(component.mainMethod);
|
|
if (main != null) {
|
|
checkCanonicalName(main);
|
|
}
|
|
writeLibraries(component);
|
|
writeUriToSource(component.uriToSource);
|
|
writeLinkTable(component);
|
|
_writeMetadataMappingSection(component);
|
|
writeStringTable(stringIndexer);
|
|
writeConstantTable(_constantIndexer);
|
|
writeComponentIndex(component, component.libraries);
|
|
|
|
_flush();
|
|
}
|
|
|
|
@override
|
|
void writeNodeReference(Node node) {
|
|
if (!MetadataRepository.isSupported(node)) {
|
|
throw "Can't reference nodes of type ${node.runtimeType} from metadata.";
|
|
}
|
|
|
|
if (node == null) {
|
|
writeUInt30(0);
|
|
} else {
|
|
final id =
|
|
_nodeReferences.putIfAbsent(node, () => _nodeReferences.length);
|
|
writeUInt30(id + 1);
|
|
}
|
|
}
|
|
|
|
/// Collect and write out all metadata contained in metadata repositories
|
|
/// associated with the component.
|
|
///
|
|
/// Non-empty metadata subsections will be collected in [_metadataSubsections]
|
|
/// and used to generate metadata mappings after all nodes in the component
|
|
/// are written and all node offsets are known.
|
|
///
|
|
/// Note: must write metadata payloads before any other node in the component
|
|
/// to collect references to nodes contained within metadata payloads.
|
|
void _writeMetadataPayloads(Component component) {
|
|
component.metadata.forEach((tag, repository) {
|
|
if (repository.mapping.isEmpty) {
|
|
return;
|
|
}
|
|
|
|
// Write all payloads collecting outgoing node references and remembering
|
|
// metadata offset for each node that had associated metadata.
|
|
_nodeReferences = <Node, int>{};
|
|
final metadataOffsets = <Node, int>{};
|
|
repository.mapping.forEach((node, value) {
|
|
if (!MetadataRepository.isSupported(node)) {
|
|
throw "Nodes of type ${node.runtimeType} can't have metadata.";
|
|
}
|
|
|
|
metadataOffsets[node] = getBufferOffset();
|
|
repository.writeToBinary(value, this);
|
|
});
|
|
|
|
_metadataSubsections ??= <_MetadataSubsection>[];
|
|
_metadataSubsections.add(new _MetadataSubsection(
|
|
repository, metadataOffsets, _nodeReferences));
|
|
|
|
_nodeReferences = null;
|
|
});
|
|
}
|
|
|
|
/// If the given [Node] has any metadata associated with it or is referenced
|
|
/// from some metadata payload then we need to record its offset.
|
|
void _recordNodeOffsetForMetadataMapping(Node node) {
|
|
_recordNodeOffsetForMetadataMappingImpl(node, getBufferOffset());
|
|
}
|
|
|
|
void _recordNodeOffsetForMetadataMappingImpl(Node node, int nodeOffset) {
|
|
for (var subsection in _metadataSubsections) {
|
|
final metadataOffset = subsection.metadataOffsets[node];
|
|
if (metadataOffset != null) {
|
|
subsection.metadataMapping..add(nodeOffset)..add(metadataOffset);
|
|
}
|
|
if (subsection.nodeToReferenceId != null) {
|
|
final id = subsection.nodeToReferenceId[node];
|
|
if (id != null) {
|
|
subsection.offsetsOfReferencedNodes[id] = nodeOffset;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void _writeMetadataMappingSection(Component component) {
|
|
if (_metadataSubsections == null) {
|
|
writeUInt32(0); // Empty section.
|
|
return;
|
|
}
|
|
|
|
_recordNodeOffsetForMetadataMappingImpl(component, 0);
|
|
|
|
// RList<MetadataMapping> metadataMappings
|
|
for (var subsection in _metadataSubsections) {
|
|
// UInt32 tag
|
|
writeUInt32(stringIndexer.put(subsection.repository.tag));
|
|
|
|
// RList<Pair<UInt32, UInt32>> nodeOffsetToMetadataOffset
|
|
final mappingLength = subsection.metadataMapping.length;
|
|
for (var i = 0; i < mappingLength; i += 2) {
|
|
writeUInt32(subsection.metadataMapping[i]); // node offset
|
|
writeUInt32(subsection.metadataMapping[i + 1]); // metadata offset
|
|
}
|
|
writeUInt32(mappingLength ~/ 2);
|
|
|
|
// RList<UInt32> nodeReferences
|
|
if (subsection.nodeToReferenceId != null) {
|
|
for (var nodeOffset in subsection.offsetsOfReferencedNodes) {
|
|
writeUInt32(nodeOffset);
|
|
}
|
|
writeUInt32(subsection.offsetsOfReferencedNodes.length);
|
|
} else {
|
|
writeUInt32(0);
|
|
}
|
|
}
|
|
writeUInt32(_metadataSubsections.length);
|
|
}
|
|
|
|
/// Write all of some of the libraries of the [component].
|
|
void writeLibraries(Component component) {
|
|
component.libraries.forEach(writeNode);
|
|
}
|
|
|
|
void writeComponentIndex(Component component, List<Library> libraries) {
|
|
// Fixed-size ints at the end used as an index.
|
|
assert(_binaryOffsetForSourceTable >= 0);
|
|
writeUInt32(_binaryOffsetForSourceTable);
|
|
assert(_binaryOffsetForLinkTable >= 0);
|
|
writeUInt32(_binaryOffsetForLinkTable);
|
|
assert(_binaryOffsetForStringTable >= 0);
|
|
writeUInt32(_binaryOffsetForStringTable);
|
|
assert(_binaryOffsetForConstantTable >= 0);
|
|
writeUInt32(_binaryOffsetForConstantTable);
|
|
|
|
CanonicalName main = getCanonicalNameOfMember(component.mainMethod);
|
|
if (main == null) {
|
|
writeUInt32(0);
|
|
} else {
|
|
writeUInt32(main.index + 1);
|
|
}
|
|
|
|
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 indexUris(Component component) {
|
|
_knownSourceUri.addAll(component.uriToSource.keys);
|
|
}
|
|
|
|
void writeUriToSource(Map<Uri, Source> uriToSource) {
|
|
_binaryOffsetForSourceTable = getBufferOffset();
|
|
|
|
int length = _sourceUriIndexer.numberOfStrings;
|
|
writeUInt32(length);
|
|
List<int> index = new List<int>(_sourceUriIndexer.entries.length);
|
|
|
|
// Write data.
|
|
for (int i = 0; i < length; ++i) {
|
|
index[i] = getBufferOffset();
|
|
|
|
StringTableEntry uri = _sourceUriIndexer.entries[i];
|
|
Source source = uriToSource[Uri.parse(uri.value)] ??
|
|
new Source(<int>[], const <int>[]);
|
|
|
|
writeByteList(uri.utf8Bytes);
|
|
writeByteList(source.source);
|
|
List<int> lineStarts = source.lineStarts;
|
|
writeUInt30(lineStarts.length);
|
|
int previousLineStart = 0;
|
|
lineStarts.forEach((lineStart) {
|
|
writeUInt30(lineStart - previousLineStart);
|
|
previousLineStart = lineStart;
|
|
});
|
|
}
|
|
|
|
// Write index for random access.
|
|
for (int i = 0; i < index.length; ++i) {
|
|
writeUInt32(index[i]);
|
|
}
|
|
}
|
|
|
|
void writeLibraryDependencyReference(LibraryDependency node) {
|
|
int index = _libraryDependencyIndex[node];
|
|
if (index == null) {
|
|
throw 'Reference to library dependency $node out of scope';
|
|
}
|
|
writeUInt30(index);
|
|
}
|
|
|
|
void writeReference(Reference reference) {
|
|
if (reference == null) {
|
|
writeUInt30(0);
|
|
} else {
|
|
CanonicalName name = reference.canonicalName;
|
|
if (name == null) {
|
|
throw 'Missing canonical name for $reference';
|
|
}
|
|
checkCanonicalName(name);
|
|
writeUInt30(name.index + 1);
|
|
}
|
|
}
|
|
|
|
void checkCanonicalName(CanonicalName node) {
|
|
if (_knownCanonicalNameNonRootTops.contains(node.nonRootTop)) return;
|
|
if (node == null || node.isRoot) return;
|
|
if (_reindexedCanonicalNames.contains(node)) return;
|
|
|
|
checkCanonicalName(node.parent);
|
|
node.index = _canonicalNameList.length;
|
|
_canonicalNameList.add(node);
|
|
_reindexedCanonicalNames.add(node);
|
|
}
|
|
|
|
void writeCanonicalNameReference(CanonicalName name) {
|
|
if (name == null) {
|
|
writeUInt30(0);
|
|
} else {
|
|
checkCanonicalName(name);
|
|
writeUInt30(name.index + 1);
|
|
}
|
|
}
|
|
|
|
void writeLibraryReference(Library node) {
|
|
writeCanonicalNameReference(node.canonicalName);
|
|
}
|
|
|
|
writeOffset(int offset) {
|
|
if (_activeFileUri == null) {
|
|
offset = TreeNode.noOffset;
|
|
}
|
|
|
|
// TODO(jensj): Delta-encoding.
|
|
// File offset ranges from -1 and up,
|
|
// but is here saved as unsigned (thus the +1)
|
|
writeUInt30(offset + 1);
|
|
}
|
|
|
|
void writeClassReference(Class class_, {bool allowNull: false}) {
|
|
if (class_ == null && !allowNull) {
|
|
throw 'Expected a class reference to be valid but was `null`.';
|
|
}
|
|
writeCanonicalNameReference(getCanonicalNameOfClass(class_));
|
|
}
|
|
|
|
void writeMemberReference(Member member, {bool allowNull: false}) {
|
|
if (member == null && !allowNull) {
|
|
throw 'Expected a member reference to be valid but was `null`.';
|
|
}
|
|
writeCanonicalNameReference(getCanonicalNameOfMember(member));
|
|
}
|
|
|
|
void writeName(Name node) {
|
|
if (_metadataSubsections != null) {
|
|
_recordNodeOffsetForMetadataMapping(node);
|
|
}
|
|
writeStringReference(node.name);
|
|
// 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) {
|
|
insideExternalLibrary = node.isExternal;
|
|
libraryOffsets.add(getBufferOffset());
|
|
writeByte(insideExternalLibrary ? 1 : 0);
|
|
writeCanonicalNameReference(getCanonicalNameOfLibrary(node));
|
|
writeStringReference(node.name ?? '');
|
|
// TODO(jensj): We save (almost) the same URI twice.
|
|
|
|
final Uri activeFileUriSaved = _activeFileUri;
|
|
_activeFileUri = writeUriReference(node.fileUri);
|
|
|
|
writeAnnotationList(node.annotations);
|
|
writeLibraryDependencies(node);
|
|
writeAdditionalExports(node.additionalExports);
|
|
writeLibraryParts(node);
|
|
writeNodeList(node.typedefs);
|
|
classOffsets = <int>[];
|
|
writeNodeList(node.classes);
|
|
classOffsets.add(getBufferOffset());
|
|
writeNodeList(node.fields);
|
|
procedureOffsets = <int>[];
|
|
writeNodeList(node.procedures);
|
|
procedureOffsets.add(getBufferOffset());
|
|
|
|
_activeFileUri = activeFileUriSaved;
|
|
|
|
// Fixed-size ints at the end used as an index.
|
|
assert(classOffsets.length > 0);
|
|
for (int offset in classOffsets) {
|
|
writeUInt32(offset);
|
|
}
|
|
writeUInt32(classOffsets.length - 1);
|
|
|
|
assert(procedureOffsets.length > 0);
|
|
for (int offset in procedureOffsets) {
|
|
writeUInt32(offset);
|
|
}
|
|
writeUInt32(procedureOffsets.length - 1);
|
|
}
|
|
|
|
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) {
|
|
var importNode = library.dependencies[i];
|
|
_libraryDependencyIndex[importNode] = i;
|
|
writeLibraryDependency(importNode);
|
|
}
|
|
}
|
|
|
|
void writeAdditionalExports(List<Reference> additionalExports) {
|
|
writeUInt30(additionalExports.length);
|
|
for (Reference ref in additionalExports) {
|
|
writeReference(ref);
|
|
}
|
|
}
|
|
|
|
void writeLibraryDependency(LibraryDependency node) {
|
|
if (_metadataSubsections != null) {
|
|
_recordNodeOffsetForMetadataMapping(node);
|
|
}
|
|
writeOffset(node.fileOffset);
|
|
writeByte(node.flags);
|
|
writeNodeList(node.annotations);
|
|
writeLibraryReference(node.targetLibrary);
|
|
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) {
|
|
var partNode = library.parts[i];
|
|
writeLibraryPart(partNode);
|
|
}
|
|
}
|
|
|
|
void writeLibraryPart(LibraryPart node) {
|
|
if (_metadataSubsections != null) {
|
|
_recordNodeOffsetForMetadataMapping(node);
|
|
}
|
|
writeNodeList(node.annotations);
|
|
writeStringReference(node.partUri);
|
|
}
|
|
|
|
void visitTypedef(Typedef node) {
|
|
writeCanonicalNameReference(getCanonicalNameOfTypedef(node));
|
|
|
|
final Uri activeFileUriSaved = _activeFileUri;
|
|
_activeFileUri = writeUriReference(node.fileUri);
|
|
|
|
writeOffset(node.fileOffset);
|
|
writeStringReference(node.name);
|
|
writeAnnotationList(node.annotations);
|
|
_typeParameterIndexer.enter(node.typeParameters);
|
|
writeNodeList(node.typeParameters);
|
|
writeNode(node.type);
|
|
_typeParameterIndexer.exit(node.typeParameters);
|
|
|
|
_activeFileUri = activeFileUriSaved;
|
|
}
|
|
|
|
void writeAnnotation(Expression annotation) {
|
|
_variableIndexer ??= new VariableIndexer();
|
|
writeNode(annotation);
|
|
}
|
|
|
|
void writeAnnotationList(List<Expression> annotations) {
|
|
final len = annotations.length;
|
|
writeUInt30(len);
|
|
for (var i = 0; i < len; i++) {
|
|
final annotation = annotations[i];
|
|
writeAnnotation(annotation);
|
|
}
|
|
}
|
|
|
|
int _encodeClassFlags(bool isAbstract, bool isEnum,
|
|
bool isSyntheticMixinImplementation, ClassLevel level) {
|
|
int abstractFlag = isAbstract ? 1 : 0;
|
|
int isEnumFlag = isEnum ? 2 : 0;
|
|
int isSyntheticMixinImplementationFlag =
|
|
isSyntheticMixinImplementation ? 4 : 0;
|
|
int levelFlags = (level.index - 1) << 3;
|
|
return abstractFlag |
|
|
isEnumFlag |
|
|
isSyntheticMixinImplementationFlag |
|
|
levelFlags;
|
|
}
|
|
|
|
@override
|
|
void visitClass(Class node) {
|
|
classOffsets.add(getBufferOffset());
|
|
|
|
int flags = _encodeClassFlags(node.isAbstract, node.isEnum,
|
|
node.isSyntheticMixinImplementation, node.level);
|
|
if (node.canonicalName == null) {
|
|
throw 'Missing canonical name for $node';
|
|
}
|
|
writeByte(Tag.Class);
|
|
writeCanonicalNameReference(getCanonicalNameOfClass(node));
|
|
|
|
final Uri activeFileUriSaved = _activeFileUri;
|
|
_activeFileUri = writeUriReference(node.fileUri);
|
|
|
|
writeOffset(node.fileOffset);
|
|
writeOffset(node.fileEndOffset);
|
|
writeByte(flags);
|
|
writeStringReference(node.name ?? '');
|
|
|
|
writeAnnotationList(node.annotations);
|
|
_typeParameterIndexer.enter(node.typeParameters);
|
|
writeNodeList(node.typeParameters);
|
|
writeOptionalNode(node.supertype);
|
|
writeOptionalNode(node.mixedInType);
|
|
writeNodeList(node.implementedTypes);
|
|
writeNodeList(node.fields);
|
|
writeNodeList(node.constructors);
|
|
procedureOffsets = <int>[];
|
|
writeNodeList(node.procedures);
|
|
procedureOffsets.add(getBufferOffset());
|
|
writeNodeList(node.redirectingFactoryConstructors);
|
|
_typeParameterIndexer.exit(node.typeParameters);
|
|
|
|
_activeFileUri = activeFileUriSaved;
|
|
|
|
assert(procedureOffsets.length > 0);
|
|
for (int offset in procedureOffsets) {
|
|
writeUInt32(offset);
|
|
}
|
|
writeUInt32(procedureOffsets.length - 1);
|
|
}
|
|
|
|
static final Name _emptyName = new Name('');
|
|
|
|
@override
|
|
void visitConstructor(Constructor node) {
|
|
if (node.canonicalName == null) {
|
|
throw 'Missing canonical name for $node';
|
|
}
|
|
_variableIndexer = new VariableIndexer();
|
|
writeByte(Tag.Constructor);
|
|
writeCanonicalNameReference(getCanonicalNameOfMember(node));
|
|
|
|
final Uri activeFileUriSaved = _activeFileUri;
|
|
_activeFileUri = writeUriReference(node.fileUri);
|
|
|
|
writeOffset(node.fileOffset);
|
|
writeOffset(node.fileEndOffset);
|
|
writeByte(node.flags);
|
|
writeName(node.name ?? _emptyName);
|
|
|
|
writeAnnotationList(node.annotations);
|
|
assert(node.function.typeParameters.isEmpty);
|
|
writeNode(node.function);
|
|
// Parameters are in scope in the initializers.
|
|
_variableIndexer.restoreScope(node.function.positionalParameters.length +
|
|
node.function.namedParameters.length);
|
|
writeNodeList(node.initializers);
|
|
|
|
_activeFileUri = activeFileUriSaved;
|
|
|
|
_variableIndexer = null;
|
|
}
|
|
|
|
@override
|
|
void visitProcedure(Procedure node) {
|
|
procedureOffsets.add(getBufferOffset());
|
|
|
|
if (node.canonicalName == null) {
|
|
throw 'Missing canonical name for $node';
|
|
}
|
|
_variableIndexer = new VariableIndexer();
|
|
writeByte(Tag.Procedure);
|
|
writeCanonicalNameReference(getCanonicalNameOfMember(node));
|
|
|
|
final Uri activeFileUriSaved = _activeFileUri;
|
|
_activeFileUri = writeUriReference(node.fileUri);
|
|
|
|
writeOffset(node.fileOffset);
|
|
writeOffset(node.fileEndOffset);
|
|
writeByte(node.kind.index);
|
|
writeByte(node.flags);
|
|
writeByte(node.flags2);
|
|
writeName(node.name ?? '');
|
|
writeAnnotationList(node.annotations);
|
|
writeOptionalReference(node.forwardingStubSuperTargetReference);
|
|
writeOptionalReference(node.forwardingStubInterfaceTargetReference);
|
|
writeOptionalNode(node.function);
|
|
|
|
_activeFileUri = activeFileUriSaved;
|
|
|
|
_variableIndexer = null;
|
|
|
|
assert((node.forwardingStubSuperTarget != null) ||
|
|
!(node.isForwardingStub && node.function.body != null));
|
|
}
|
|
|
|
@override
|
|
void visitField(Field node) {
|
|
if (node.canonicalName == null) {
|
|
throw 'Missing canonical name for $node';
|
|
}
|
|
_variableIndexer = new VariableIndexer();
|
|
writeByte(Tag.Field);
|
|
writeCanonicalNameReference(getCanonicalNameOfMember(node));
|
|
|
|
final Uri activeFileUriSaved = _activeFileUri;
|
|
_activeFileUri = writeUriReference(node.fileUri);
|
|
|
|
writeOffset(node.fileOffset);
|
|
writeOffset(node.fileEndOffset);
|
|
writeByte(node.flags);
|
|
writeByte(node.flags2);
|
|
writeName(node.name);
|
|
writeAnnotationList(node.annotations);
|
|
writeNode(node.type);
|
|
writeOptionalNode(node.initializer);
|
|
|
|
_activeFileUri = activeFileUriSaved;
|
|
|
|
_variableIndexer = null;
|
|
}
|
|
|
|
@override
|
|
void visitRedirectingFactoryConstructor(RedirectingFactoryConstructor node) {
|
|
if (node.canonicalName == null) {
|
|
throw 'Missing canonical name for $node';
|
|
}
|
|
writeByte(Tag.RedirectingFactoryConstructor);
|
|
_variableIndexer = new VariableIndexer();
|
|
_variableIndexer.pushScope();
|
|
_typeParameterIndexer.enter(node.typeParameters);
|
|
writeCanonicalNameReference(getCanonicalNameOfMember(node));
|
|
|
|
final Uri activeFileUriSaved = _activeFileUri;
|
|
_activeFileUri = writeUriReference(node.fileUri);
|
|
|
|
writeOffset(node.fileOffset);
|
|
writeOffset(node.fileEndOffset);
|
|
writeByte(node.flags);
|
|
writeName(node.name);
|
|
|
|
writeAnnotationList(node.annotations);
|
|
writeReference(node.targetReference);
|
|
writeNodeList(node.typeArguments);
|
|
writeNodeList(node.typeParameters);
|
|
writeUInt30(node.positionalParameters.length + node.namedParameters.length);
|
|
writeUInt30(node.requiredParameterCount);
|
|
writeVariableDeclarationList(node.positionalParameters);
|
|
writeVariableDeclarationList(node.namedParameters);
|
|
_typeParameterIndexer.exit(node.typeParameters);
|
|
|
|
_activeFileUri = activeFileUriSaved;
|
|
|
|
_variableIndexer.popScope();
|
|
_variableIndexer = null;
|
|
}
|
|
|
|
@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);
|
|
writeReference(node.fieldReference);
|
|
writeNode(node.value);
|
|
}
|
|
|
|
@override
|
|
void visitSuperInitializer(SuperInitializer node) {
|
|
writeByte(Tag.SuperInitializer);
|
|
writeByte(node.isSynthetic ? 1 : 0);
|
|
writeReference(node.targetReference);
|
|
writeNode(node.arguments);
|
|
}
|
|
|
|
@override
|
|
void visitRedirectingInitializer(RedirectingInitializer node) {
|
|
writeByte(Tag.RedirectingInitializer);
|
|
writeByte(node.isSynthetic ? 1 : 0);
|
|
writeReference(node.targetReference);
|
|
writeNode(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);
|
|
assert(_variableIndexer != null);
|
|
_variableIndexer.pushScope();
|
|
var oldLabels = _labelIndexer;
|
|
_labelIndexer = null;
|
|
var oldCases = _switchCaseIndexer;
|
|
_switchCaseIndexer = null;
|
|
// Note: FunctionNode has no tag.
|
|
_typeParameterIndexer.enter(node.typeParameters);
|
|
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.body);
|
|
_labelIndexer = oldLabels;
|
|
_switchCaseIndexer = oldCases;
|
|
_typeParameterIndexer.exit(node.typeParameters);
|
|
_variableIndexer.popScope();
|
|
}
|
|
|
|
@override
|
|
void visitInvalidExpression(InvalidExpression node) {
|
|
writeByte(Tag.InvalidExpression);
|
|
writeOffset(node.fileOffset);
|
|
writeStringReference(node.message ?? '');
|
|
}
|
|
|
|
@override
|
|
void visitVariableGet(VariableGet node) {
|
|
assert(_variableIndexer != null);
|
|
int index = _variableIndexer[node.variable];
|
|
assert(index != null);
|
|
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(_variableIndexer[node.variable]);
|
|
writeOptionalNode(node.promotedType);
|
|
}
|
|
}
|
|
|
|
@override
|
|
void visitVariableSet(VariableSet node) {
|
|
assert(_variableIndexer != null);
|
|
int index = _variableIndexer[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(_variableIndexer[node.variable]);
|
|
writeNode(node.value);
|
|
}
|
|
}
|
|
|
|
@override
|
|
void visitPropertyGet(PropertyGet node) {
|
|
writeByte(Tag.PropertyGet);
|
|
writeOffset(node.fileOffset);
|
|
writeByte(node.flags);
|
|
writeNode(node.receiver);
|
|
writeName(node.name);
|
|
writeReference(node.interfaceTargetReference);
|
|
}
|
|
|
|
@override
|
|
void visitPropertySet(PropertySet node) {
|
|
writeByte(Tag.PropertySet);
|
|
writeOffset(node.fileOffset);
|
|
writeByte(node.flags);
|
|
writeNode(node.receiver);
|
|
writeName(node.name);
|
|
writeNode(node.value);
|
|
writeReference(node.interfaceTargetReference);
|
|
}
|
|
|
|
@override
|
|
void visitSuperPropertyGet(SuperPropertyGet node) {
|
|
writeByte(Tag.SuperPropertyGet);
|
|
writeOffset(node.fileOffset);
|
|
writeName(node.name);
|
|
writeReference(node.interfaceTargetReference);
|
|
}
|
|
|
|
@override
|
|
void visitSuperPropertySet(SuperPropertySet node) {
|
|
writeByte(Tag.SuperPropertySet);
|
|
writeOffset(node.fileOffset);
|
|
writeName(node.name);
|
|
writeNode(node.value);
|
|
writeReference(node.interfaceTargetReference);
|
|
}
|
|
|
|
@override
|
|
void visitDirectPropertyGet(DirectPropertyGet node) {
|
|
writeByte(Tag.DirectPropertyGet);
|
|
writeOffset(node.fileOffset);
|
|
writeByte(node.flags);
|
|
writeNode(node.receiver);
|
|
writeReference(node.targetReference);
|
|
}
|
|
|
|
@override
|
|
void visitDirectPropertySet(DirectPropertySet node) {
|
|
writeByte(Tag.DirectPropertySet);
|
|
writeOffset(node.fileOffset);
|
|
writeByte(node.flags);
|
|
writeNode(node.receiver);
|
|
writeReference(node.targetReference);
|
|
writeNode(node.value);
|
|
}
|
|
|
|
@override
|
|
void visitStaticGet(StaticGet node) {
|
|
writeByte(Tag.StaticGet);
|
|
writeOffset(node.fileOffset);
|
|
writeReference(node.targetReference);
|
|
}
|
|
|
|
@override
|
|
void visitStaticSet(StaticSet node) {
|
|
writeByte(Tag.StaticSet);
|
|
writeOffset(node.fileOffset);
|
|
writeReference(node.targetReference);
|
|
writeNode(node.value);
|
|
}
|
|
|
|
@override
|
|
void visitMethodInvocation(MethodInvocation node) {
|
|
writeByte(Tag.MethodInvocation);
|
|
writeOffset(node.fileOffset);
|
|
writeByte(node.flags);
|
|
writeNode(node.receiver);
|
|
writeName(node.name);
|
|
writeNode(node.arguments);
|
|
writeReference(node.interfaceTargetReference);
|
|
}
|
|
|
|
@override
|
|
void visitSuperMethodInvocation(SuperMethodInvocation node) {
|
|
writeByte(Tag.SuperMethodInvocation);
|
|
writeOffset(node.fileOffset);
|
|
writeName(node.name);
|
|
writeNode(node.arguments);
|
|
writeReference(node.interfaceTargetReference);
|
|
}
|
|
|
|
@override
|
|
void visitDirectMethodInvocation(DirectMethodInvocation node) {
|
|
writeByte(Tag.DirectMethodInvocation);
|
|
writeOffset(node.fileOffset);
|
|
writeByte(node.flags);
|
|
writeNode(node.receiver);
|
|
writeReference(node.targetReference);
|
|
writeNode(node.arguments);
|
|
}
|
|
|
|
@override
|
|
void visitStaticInvocation(StaticInvocation node) {
|
|
writeByte(node.isConst ? Tag.ConstStaticInvocation : Tag.StaticInvocation);
|
|
writeOffset(node.fileOffset);
|
|
writeReference(node.targetReference);
|
|
writeNode(node.arguments);
|
|
}
|
|
|
|
@override
|
|
void visitConstructorInvocation(ConstructorInvocation node) {
|
|
writeByte(node.isConst
|
|
? Tag.ConstConstructorInvocation
|
|
: Tag.ConstructorInvocation);
|
|
writeOffset(node.fileOffset);
|
|
writeReference(node.targetReference);
|
|
writeNode(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);
|
|
}
|
|
|
|
int logicalOperatorIndex(String operator) {
|
|
switch (operator) {
|
|
case '&&':
|
|
return 0;
|
|
case '||':
|
|
return 1;
|
|
}
|
|
throw 'Not a logical operator: $operator';
|
|
}
|
|
|
|
@override
|
|
void visitLogicalExpression(LogicalExpression node) {
|
|
writeByte(Tag.LogicalExpression);
|
|
writeNode(node.left);
|
|
writeByte(logicalOperatorIndex(node.operator));
|
|
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 visitIsExpression(IsExpression node) {
|
|
writeByte(Tag.IsExpression);
|
|
writeOffset(node.fileOffset);
|
|
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) {
|
|
writeDouble(node.value);
|
|
}
|
|
|
|
writeDouble(double value) {
|
|
// TODO: Pick a better format for double literals.
|
|
writeByte(Tag.DoubleLiteral);
|
|
writeStringReference('$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 visitMapLiteral(MapLiteral node) {
|
|
writeByte(node.isConst ? Tag.ConstMapLiteral : Tag.MapLiteral);
|
|
writeOffset(node.fileOffset);
|
|
writeNode(node.keyType);
|
|
writeNode(node.valueType);
|
|
writeNodeList(node.entries);
|
|
}
|
|
|
|
@override
|
|
void visitMapEntry(MapEntry 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);
|
|
writeNode(node.function);
|
|
}
|
|
|
|
@override
|
|
void visitLet(Let node) {
|
|
writeByte(Tag.Let);
|
|
writeVariableDeclaration(node.variable);
|
|
writeNode(node.body);
|
|
--_variableIndexer.stackHeight;
|
|
}
|
|
|
|
@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);
|
|
}
|
|
|
|
@override
|
|
void visitVectorCreation(VectorCreation node) {
|
|
writeByte(Tag.VectorCreation);
|
|
writeUInt30(node.length);
|
|
}
|
|
|
|
@override
|
|
void visitVectorGet(VectorGet node) {
|
|
writeByte(Tag.VectorGet);
|
|
writeNode(node.vectorExpression);
|
|
writeUInt30(node.index);
|
|
}
|
|
|
|
@override
|
|
void visitVectorSet(VectorSet node) {
|
|
writeByte(Tag.VectorSet);
|
|
writeNode(node.vectorExpression);
|
|
writeUInt30(node.index);
|
|
writeNode(node.value);
|
|
}
|
|
|
|
@override
|
|
void visitVectorCopy(VectorCopy node) {
|
|
writeByte(Tag.VectorCopy);
|
|
writeNode(node.vectorExpression);
|
|
}
|
|
|
|
@override
|
|
void visitClosureCreation(ClosureCreation node) {
|
|
writeByte(Tag.ClosureCreation);
|
|
writeReference(node.topLevelFunctionReference);
|
|
writeNode(node.contextVector);
|
|
writeNode(node.functionType);
|
|
writeNodeList(node.typeArguments);
|
|
}
|
|
|
|
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.pushScope();
|
|
writeByte(Tag.Block);
|
|
writeNodeList(node.statements);
|
|
_variableIndexer.popScope();
|
|
}
|
|
|
|
@override
|
|
void visitAssertBlock(AssertBlock node) {
|
|
_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) {
|
|
if (_labelIndexer == null) {
|
|
_labelIndexer = new LabelIndexer();
|
|
}
|
|
_labelIndexer.enter(node);
|
|
writeByte(Tag.LabeledStatement);
|
|
writeNode(node.body);
|
|
_labelIndexer.exit();
|
|
}
|
|
|
|
@override
|
|
void visitConstantExpression(ConstantExpression node) {
|
|
writeByte(Tag.ConstantExpression);
|
|
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.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.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) {
|
|
if (_switchCaseIndexer == null) {
|
|
_switchCaseIndexer = new SwitchCaseIndexer();
|
|
}
|
|
_switchCaseIndexer.enter(node);
|
|
writeByte(Tag.SwitchStatement);
|
|
writeOffset(node.fileOffset);
|
|
writeNode(node.expression);
|
|
writeNodeList(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);
|
|
}
|
|
|
|
@override
|
|
void visitTryCatch(TryCatch node) {
|
|
writeByte(Tag.TryCatch);
|
|
writeNode(node.body);
|
|
if (node.catches.any((Catch c) => c.stackTrace != null)) {
|
|
// at least one catch needs the stack trace.
|
|
writeByte(1);
|
|
} else {
|
|
// no catch needs the stack trace.
|
|
writeByte(0);
|
|
}
|
|
writeNodeList(node.catches);
|
|
}
|
|
|
|
@override
|
|
void visitCatch(Catch node) {
|
|
// Note: there is no tag on Catch.
|
|
_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) {
|
|
_recordNodeOffsetForMetadataMapping(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.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);
|
|
writeNode(node.function);
|
|
}
|
|
|
|
@override
|
|
void visitBottomType(BottomType node) {
|
|
writeByte(Tag.BottomType);
|
|
}
|
|
|
|
@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);
|
|
writeReference(node.className);
|
|
} else {
|
|
writeByte(Tag.InterfaceType);
|
|
writeReference(node.className);
|
|
writeNodeList(node.typeArguments);
|
|
}
|
|
}
|
|
|
|
@override
|
|
void visitSupertype(Supertype node) {
|
|
if (node.typeArguments.isEmpty) {
|
|
writeByte(Tag.SimpleInterfaceType);
|
|
writeReference(node.className);
|
|
} else {
|
|
writeByte(Tag.InterfaceType);
|
|
writeReference(node.className);
|
|
writeNodeList(node.typeArguments);
|
|
}
|
|
}
|
|
|
|
@override
|
|
void visitFunctionType(FunctionType node) {
|
|
if (node.requiredParameterCount == node.positionalParameters.length &&
|
|
node.typeParameters.isEmpty &&
|
|
node.namedParameters.isEmpty &&
|
|
node.typedefReference == null) {
|
|
writeByte(Tag.SimpleFunctionType);
|
|
writeNodeList(node.positionalParameters);
|
|
writeStringReferenceList(node.positionalParameterNames);
|
|
writeNode(node.returnType);
|
|
} else {
|
|
writeByte(Tag.FunctionType);
|
|
_typeParameterIndexer.enter(node.typeParameters);
|
|
writeNodeList(node.typeParameters);
|
|
writeUInt30(node.requiredParameterCount);
|
|
writeUInt30(
|
|
node.positionalParameters.length + node.namedParameters.length);
|
|
writeNodeList(node.positionalParameters);
|
|
writeNodeList(node.namedParameters);
|
|
writeStringReferenceList(node.positionalParameterNames);
|
|
writeReference(node.typedefReference);
|
|
writeNode(node.returnType);
|
|
_typeParameterIndexer.exit(node.typeParameters);
|
|
}
|
|
}
|
|
|
|
@override
|
|
void visitNamedType(NamedType node) {
|
|
writeStringReference(node.name);
|
|
writeNode(node.type);
|
|
}
|
|
|
|
@override
|
|
void visitTypeParameterType(TypeParameterType node) {
|
|
writeByte(Tag.TypeParameterType);
|
|
writeUInt30(_typeParameterIndexer[node.parameter]);
|
|
writeOptionalNode(node.promotedBound);
|
|
}
|
|
|
|
@override
|
|
void visitVectorType(VectorType node) {
|
|
writeByte(Tag.VectorType);
|
|
}
|
|
|
|
@override
|
|
void visitTypedefType(TypedefType node) {
|
|
writeByte(Tag.TypedefType);
|
|
writeReference(node.typedefReference);
|
|
writeNodeList(node.typeArguments);
|
|
}
|
|
|
|
@override
|
|
void visitTypeParameter(TypeParameter node) {
|
|
writeByte(node.flags);
|
|
writeAnnotationList(node.annotations);
|
|
writeStringReference(node.name ?? '');
|
|
writeNode(node.bound);
|
|
writeOptionalNode(node.defaultType);
|
|
}
|
|
|
|
// ================================================================
|
|
// 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 Nodes');
|
|
}
|
|
|
|
@override
|
|
void defaultConstant(Constant node) {
|
|
throw new UnsupportedError('serialization of generic Constants');
|
|
}
|
|
|
|
@override
|
|
void defaultBasicLiteral(BasicLiteral node) {
|
|
throw new UnsupportedError('serialization of generic BasicLiterals');
|
|
}
|
|
|
|
@override
|
|
void defaultConstantReference(Constant node) {
|
|
throw new UnsupportedError('serialization of generic Constant references');
|
|
}
|
|
|
|
@override
|
|
void defaultDartType(DartType node) {
|
|
throw new UnsupportedError('serialization of generic DartTypes');
|
|
}
|
|
|
|
@override
|
|
void defaultExpression(Expression node) {
|
|
throw new UnsupportedError('serialization of generic Expressions');
|
|
}
|
|
|
|
@override
|
|
void defaultInitializer(Initializer node) {
|
|
throw new UnsupportedError('serialization of generic Initializers');
|
|
}
|
|
|
|
@override
|
|
void defaultMember(Member node) {
|
|
throw new UnsupportedError('serialization of generic Members');
|
|
}
|
|
|
|
@override
|
|
void defaultMemberReference(Member node) {
|
|
throw new UnsupportedError('serialization of generic Member references');
|
|
}
|
|
|
|
@override
|
|
void defaultStatement(Statement node) {
|
|
throw new UnsupportedError('serialization of generic Statements');
|
|
}
|
|
|
|
@override
|
|
void defaultTreeNode(TreeNode node) {
|
|
throw new UnsupportedError('serialization of generic TreeNodes');
|
|
}
|
|
|
|
@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 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 LibraryDependencys');
|
|
}
|
|
|
|
@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 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 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');
|
|
}
|
|
}
|
|
|
|
typedef bool LibraryFilter(Library _);
|
|
|
|
class VariableIndexer {
|
|
final Map<VariableDeclaration, int> index = <VariableDeclaration, int>{};
|
|
final List<int> scopes = <int>[];
|
|
int stackHeight = 0;
|
|
|
|
void declare(VariableDeclaration node) {
|
|
index[node] = stackHeight++;
|
|
}
|
|
|
|
void pushScope() {
|
|
scopes.add(stackHeight);
|
|
}
|
|
|
|
void popScope() {
|
|
stackHeight = scopes.removeLast();
|
|
}
|
|
|
|
void restoreScope(int numberOfVariables) {
|
|
stackHeight += numberOfVariables;
|
|
}
|
|
|
|
int operator [](VariableDeclaration node) {
|
|
return 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 (var caseNode in node.cases) {
|
|
index[caseNode] = stackHeight++;
|
|
}
|
|
}
|
|
|
|
void exit(SwitchStatement node) {
|
|
stackHeight -= node.cases.length;
|
|
}
|
|
|
|
int operator [](SwitchCase node) => index[node];
|
|
}
|
|
|
|
class ConstantIndexer extends RecursiveVisitor {
|
|
final StringIndexer stringIndexer;
|
|
|
|
final List<Constant> entries = <Constant>[];
|
|
final Map<Constant, int> index = <Constant, int>{};
|
|
|
|
ConstantIndexer(this.stringIndexer);
|
|
|
|
defaultConstantReference(Constant node) {
|
|
put(node);
|
|
}
|
|
|
|
int put(Constant constant) {
|
|
final int value = index[constant];
|
|
if (value != null) return value;
|
|
|
|
// Traverse DAG in post-order to ensure children have their id's assigned
|
|
// before the parent.
|
|
return constant.accept(this);
|
|
}
|
|
|
|
defaultConstant(Constant node) {
|
|
final int oldIndex = index[node];
|
|
if (oldIndex != null) return oldIndex;
|
|
|
|
if (node is StringConstant) {
|
|
stringIndexer.put(node.value);
|
|
} else if (node is DoubleConstant) {
|
|
stringIndexer.put('${node.value}');
|
|
} else if (node is IntConstant) {
|
|
final int value = node.value;
|
|
if ((value.abs() >> 30) != 0) {
|
|
stringIndexer.put('$value');
|
|
}
|
|
}
|
|
|
|
final int newIndex = entries.length;
|
|
entries.add(node);
|
|
return index[node] = newIndex;
|
|
}
|
|
|
|
visitMapConstant(MapConstant node) {
|
|
for (final ConstantMapEntry entry in node.entries) {
|
|
put(entry.key);
|
|
put(entry.value);
|
|
}
|
|
return defaultConstant(node);
|
|
}
|
|
|
|
visitListConstant(ListConstant node) {
|
|
for (final Constant entry in node.entries) {
|
|
put(entry);
|
|
}
|
|
return defaultConstant(node);
|
|
}
|
|
|
|
visitInstanceConstant(InstanceConstant node) {
|
|
for (final Constant entry in node.fieldValues.values) {
|
|
put(entry);
|
|
}
|
|
return defaultConstant(node);
|
|
}
|
|
|
|
int operator [](Constant node) => index[node];
|
|
}
|
|
|
|
class TypeParameterIndexer {
|
|
final Map<TypeParameter, int> index = <TypeParameter, int>{};
|
|
int stackHeight = 0;
|
|
|
|
void enter(List<TypeParameter> typeParameters) {
|
|
for (var parameter in typeParameters) {
|
|
index[parameter] = stackHeight;
|
|
++stackHeight;
|
|
}
|
|
}
|
|
|
|
void exit(List<TypeParameter> typeParameters) {
|
|
stackHeight -= typeParameters.length;
|
|
}
|
|
|
|
int operator [](TypeParameter parameter) => index[parameter];
|
|
}
|
|
|
|
class StringTableEntry {
|
|
final String value;
|
|
final List<int> utf8Bytes;
|
|
|
|
StringTableEntry(String value)
|
|
: value = value,
|
|
utf8Bytes = const Utf8Encoder().convert(value);
|
|
}
|
|
|
|
class StringIndexer {
|
|
final List<StringTableEntry> entries = <StringTableEntry>[];
|
|
final LinkedHashMap<String, int> index = new LinkedHashMap<String, int>();
|
|
|
|
StringIndexer() {
|
|
put('');
|
|
}
|
|
|
|
int get numberOfStrings => index.length;
|
|
|
|
int put(String string) {
|
|
var result = index[string];
|
|
if (result == null) {
|
|
entries.add(new StringTableEntry(string));
|
|
result = index.length;
|
|
index[string] = result;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int operator [](String string) => index[string];
|
|
}
|
|
|
|
/// Computes and stores the index of a library, class, or member within its
|
|
/// parent list.
|
|
class GlobalIndexer extends TreeVisitor {
|
|
final Map<TreeNode, int> indices = <TreeNode, int>{};
|
|
|
|
void buildIndexForContainer(TreeNode libraryOrClass) {
|
|
libraryOrClass.accept(this);
|
|
}
|
|
|
|
void buildIndexForList(List<TreeNode> list) {
|
|
for (int i = 0; i < list.length; ++i) {
|
|
TreeNode child = list[i];
|
|
if (child != null) {
|
|
indices[child] = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
visitComponent(Component node) {
|
|
buildIndexForList(node.libraries);
|
|
}
|
|
|
|
visitLibrary(Library node) {
|
|
buildIndexForList(node.classes);
|
|
buildIndexForList(node.fields);
|
|
buildIndexForList(node.procedures);
|
|
}
|
|
|
|
visitClass(Class node) {
|
|
buildIndexForList(node.fields);
|
|
buildIndexForList(node.constructors);
|
|
buildIndexForList(node.procedures);
|
|
}
|
|
|
|
int operator [](TreeNode memberOrLibraryOrClass) {
|
|
var node = memberOrLibraryOrClass;
|
|
assert(node is Member || node is Library || node is Class);
|
|
int index = indices[node];
|
|
if (index == null) {
|
|
buildIndexForContainer(node.parent);
|
|
return indices[node];
|
|
} else {
|
|
return index;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 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;
|
|
|
|
BufferedSink(this._sink);
|
|
|
|
void addByte(int byte) {
|
|
_buffer[length++] = byte;
|
|
if (length == SIZE) {
|
|
_sink.add(_buffer);
|
|
_buffer = new Uint8List(SIZE);
|
|
length = 0;
|
|
flushedLength += SIZE;
|
|
}
|
|
}
|
|
|
|
void addByte2(int byte1, int byte2) {
|
|
if (length < SAFE_SIZE) {
|
|
_buffer[length++] = byte1;
|
|
_buffer[length++] = byte2;
|
|
} else {
|
|
addByte(byte1);
|
|
addByte(byte2);
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
/// Offsets of metadata payloads associated with the nodes.
|
|
final Map<Node, int> metadataOffsets;
|
|
|
|
/// 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>[];
|
|
|
|
/// Mapping between nodes that are referenced from inside metadata payloads
|
|
/// and their ids.
|
|
final Map<Node, int> nodeToReferenceId;
|
|
|
|
/// Mapping between reference ids and offsets of referenced nodes.
|
|
/// Gradually filled by the writer as writing progresses but is not
|
|
/// guaranteed to be sorted.
|
|
final List<int> offsetsOfReferencedNodes;
|
|
|
|
_MetadataSubsection(
|
|
this.repository, this.metadataOffsets, Map<Node, int> nodeToReferenceId)
|
|
: nodeToReferenceId =
|
|
nodeToReferenceId.isNotEmpty ? nodeToReferenceId : null,
|
|
offsetsOfReferencedNodes = nodeToReferenceId.isNotEmpty
|
|
? new List<int>.filled(nodeToReferenceId.length, 0)
|
|
: null;
|
|
}
|