blob: fb199941e58bdacffd2cef26b50c1c6fbc87fb95 [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: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 extends Visitor implements 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);
}
void writeUriReference(Uri uri) {
int index = 0; // equivalent to index = _sourceUriIndexer[""];
if (_knownSourceUri.contains(uri)) {
index = _sourceUriIndexer.put(uri == null ? "" : "$uri");
}
writeUInt30(index);
}
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(Program program) {
_binaryOffsetForLinkTable = getBufferOffset();
writeList(_canonicalNameList, writeCanonicalNameEntry);
}
void indexLinkTable(Program program) {
_canonicalNameList = <CanonicalName>[];
void visitCanonicalName(CanonicalName node) {
node.index = _canonicalNameList.length;
_canonicalNameList.add(node);
node.children.forEach(visitCanonicalName);
}
for (var library in program.libraries) {
if (!shouldWriteLibraryCanonicalNames(library)) continue;
visitCanonicalName(library.canonicalName);
_knownCanonicalNameNonRootTops.add(library.canonicalName);
}
}
/// Compute canonical names for the whole program or parts of it.
void computeCanonicalNames(Program program) {
program.computeCanonicalNames();
}
/// Return `true` if all canonical names of the [library] should be written
/// into the link table. If some libraries of the program 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 writeProgramFile(Program program) {
computeCanonicalNames(program);
final programOffset = getBufferOffset();
writeUInt32(Tag.ProgramFile);
writeUInt32(Tag.BinaryFormatVersion);
indexLinkTable(program);
indexUris(program);
// Note: must write metadata payloads before any other node in the program
// to collect references to nodes contained within metadata payloads.
_writeMetadataPayloads(program);
if (_metadataSubsections != null) {
_recordNodeOffsetForMetadataMappingImpl(program, programOffset);
}
libraryOffsets = <int>[];
CanonicalName main = getCanonicalNameOfMember(program.mainMethod);
if (main != null) {
checkCanonicalName(main);
}
writeLibraries(program);
writeUriToSource(program.uriToSource);
writeLinkTable(program);
_writeMetadataMappingSection(program);
writeStringTable(stringIndexer);
writeConstantTable(_constantIndexer);
writeProgramIndex(program, program.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 program.
///
/// Non-empty metadata subsections will be collected in [_metadataSubsections]
/// and used to generate metadata mappings after all nodes in the program
/// are written and all node offsets are known.
///
/// Note: must write metadata payloads before any other node in the program
/// to collect references to nodes contained within metadata payloads.
void _writeMetadataPayloads(Program program) {
program.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(Program program) {
if (_metadataSubsections == null) {
writeUInt32(0); // Empty section.
return;
}
_recordNodeOffsetForMetadataMappingImpl(program, 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 [program].
void writeLibraries(Program program) {
program.libraries.forEach(writeNode);
}
void writeProgramIndex(Program program, 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(program.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(Program program) {
_knownSourceUri.addAll(program.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) {
// 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;
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.
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());
// 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);
writeUriReference(node.fileUri);
}
void visitTypedef(Typedef node) {
writeCanonicalNameReference(getCanonicalNameOfTypedef(node));
writeOffset(node.fileOffset);
writeStringReference(node.name);
writeUriReference(node.fileUri);
writeAnnotationList(node.annotations);
_typeParameterIndexer.enter(node.typeParameters);
writeNodeList(node.typeParameters);
writeNode(node.type);
_typeParameterIndexer.exit(node.typeParameters);
}
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;
}
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));
writeOffset(node.fileOffset);
writeOffset(node.fileEndOffset);
writeByte(flags);
writeStringReference(node.name ?? '');
writeUriReference(node.fileUri);
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);
assert(procedureOffsets.length > 0);
for (int offset in procedureOffsets) {
writeUInt32(offset);
}
writeUInt32(procedureOffsets.length - 1);
}
static final Name _emptyName = new Name('');
visitConstructor(Constructor node) {
if (node.canonicalName == null) {
throw 'Missing canonical name for $node';
}
_variableIndexer = new VariableIndexer();
writeByte(Tag.Constructor);
writeCanonicalNameReference(getCanonicalNameOfMember(node));
writeOffset(node.fileOffset);
writeOffset(node.fileEndOffset);
writeByte(node.flags);
writeName(node.name ?? _emptyName);
writeUriReference(node.fileUri);
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);
_variableIndexer = null;
}
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));
writeOffset(node.fileOffset);
writeOffset(node.fileEndOffset);
writeByte(node.kind.index);
writeByte(node.flags);
writeName(node.name ?? '');
writeUriReference(node.fileUri);
writeAnnotationList(node.annotations);
writeOptionalReference(node.forwardingStubSuperTargetReference);
writeOptionalReference(node.forwardingStubInterfaceTargetReference);
writeOptionalNode(node.function);
_variableIndexer = null;
assert((node.forwardingStubSuperTarget != null) ||
!(node.isForwardingStub && node.function.body != null));
}
visitField(Field node) {
if (node.canonicalName == null) {
throw 'Missing canonical name for $node';
}
_variableIndexer = new VariableIndexer();
writeByte(Tag.Field);
writeCanonicalNameReference(getCanonicalNameOfMember(node));
writeOffset(node.fileOffset);
writeOffset(node.fileEndOffset);
writeByte(node.flags);
writeByte(node.flags2);
writeName(node.name);
writeUriReference(node.fileUri);
writeAnnotationList(node.annotations);
writeNode(node.type);
writeOptionalNode(node.initializer);
_variableIndexer = null;
}
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));
writeOffset(node.fileOffset);
writeOffset(node.fileEndOffset);
writeByte(node.flags);
writeName(node.name);
writeUriReference(node.fileUri);
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);
_variableIndexer.popScope();
_variableIndexer = null;
}
visitInvalidInitializer(InvalidInitializer node) {
writeByte(Tag.InvalidInitializer);
writeByte(node.isSynthetic ? 1 : 0);
}
visitFieldInitializer(FieldInitializer node) {
writeByte(Tag.FieldInitializer);
writeByte(node.isSynthetic ? 1 : 0);
writeReference(node.fieldReference);
writeNode(node.value);
}
visitSuperInitializer(SuperInitializer node) {
writeByte(Tag.SuperInitializer);
writeByte(node.isSynthetic ? 1 : 0);
writeReference(node.targetReference);
writeNode(node.arguments);
}
visitRedirectingInitializer(RedirectingInitializer node) {
writeByte(Tag.RedirectingInitializer);
writeByte(node.isSynthetic ? 1 : 0);
writeReference(node.targetReference);
writeNode(node.arguments);
}
visitLocalInitializer(LocalInitializer node) {
writeByte(Tag.LocalInitializer);
writeByte(node.isSynthetic ? 1 : 0);
writeVariableDeclaration(node.variable);
}
visitAssertInitializer(AssertInitializer node) {
writeByte(Tag.AssertInitializer);
writeByte(node.isSynthetic ? 1 : 0);
writeNode(node.statement);
}
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();
}
visitInvalidExpression(InvalidExpression node) {
writeByte(Tag.InvalidExpression);
writeOffset(node.fileOffset);
writeStringReference(node.message ?? '');
}
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);
}
}
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);
}
}
visitPropertyGet(PropertyGet node) {
writeByte(Tag.PropertyGet);
writeOffset(node.fileOffset);
writeByte(node.flags);
writeNode(node.receiver);
writeName(node.name);
writeReference(node.interfaceTargetReference);
}
visitPropertySet(PropertySet node) {
writeByte(Tag.PropertySet);
writeOffset(node.fileOffset);
writeByte(node.flags);
writeNode(node.receiver);
writeName(node.name);
writeNode(node.value);
writeReference(node.interfaceTargetReference);
}
visitSuperPropertyGet(SuperPropertyGet node) {
writeByte(Tag.SuperPropertyGet);
writeOffset(node.fileOffset);
writeName(node.name);
writeReference(node.interfaceTargetReference);
}
visitSuperPropertySet(SuperPropertySet node) {
writeByte(Tag.SuperPropertySet);
writeOffset(node.fileOffset);
writeName(node.name);
writeNode(node.value);
writeReference(node.interfaceTargetReference);
}
visitDirectPropertyGet(DirectPropertyGet node) {
writeByte(Tag.DirectPropertyGet);
writeOffset(node.fileOffset);
writeByte(node.flags);
writeNode(node.receiver);
writeReference(node.targetReference);
}
visitDirectPropertySet(DirectPropertySet node) {
writeByte(Tag.DirectPropertySet);
writeOffset(node.fileOffset);
writeByte(node.flags);
writeNode(node.receiver);
writeReference(node.targetReference);
writeNode(node.value);
}
visitStaticGet(StaticGet node) {
writeByte(Tag.StaticGet);
writeOffset(node.fileOffset);
writeReference(node.targetReference);
}
visitStaticSet(StaticSet node) {
writeByte(Tag.StaticSet);
writeOffset(node.fileOffset);
writeReference(node.targetReference);
writeNode(node.value);
}
visitMethodInvocation(MethodInvocation node) {
writeByte(Tag.MethodInvocation);
writeOffset(node.fileOffset);
writeByte(node.flags);
writeNode(node.receiver);
writeName(node.name);
writeNode(node.arguments);
writeReference(node.interfaceTargetReference);
}
visitSuperMethodInvocation(SuperMethodInvocation node) {
writeByte(Tag.SuperMethodInvocation);
writeOffset(node.fileOffset);
writeName(node.name);
writeNode(node.arguments);
writeReference(node.interfaceTargetReference);
}
visitDirectMethodInvocation(DirectMethodInvocation node) {
writeByte(Tag.DirectMethodInvocation);
writeOffset(node.fileOffset);
writeByte(node.flags);
writeNode(node.receiver);
writeReference(node.targetReference);
writeNode(node.arguments);
}
visitStaticInvocation(StaticInvocation node) {
writeByte(node.isConst ? Tag.ConstStaticInvocation : Tag.StaticInvocation);
writeOffset(node.fileOffset);
writeReference(node.targetReference);
writeNode(node.arguments);
}
visitConstructorInvocation(ConstructorInvocation node) {
writeByte(node.isConst
? Tag.ConstConstructorInvocation
: Tag.ConstructorInvocation);
writeOffset(node.fileOffset);
writeReference(node.targetReference);
writeNode(node.arguments);
}
visitArguments(Arguments node) {
writeUInt30(node.positional.length + node.named.length);
writeNodeList(node.types);
writeNodeList(node.positional);
writeNodeList(node.named);
}
visitNamedExpression(NamedExpression node) {
writeStringReference(node.name);
writeNode(node.value);
}
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';
}
visitLogicalExpression(LogicalExpression node) {
writeByte(Tag.LogicalExpression);
writeNode(node.left);
writeByte(logicalOperatorIndex(node.operator));
writeNode(node.right);
}
visitConditionalExpression(ConditionalExpression node) {
writeByte(Tag.ConditionalExpression);
writeNode(node.condition);
writeNode(node.then);
writeNode(node.otherwise);
writeOptionalNode(node.staticType);
}
visitStringConcatenation(StringConcatenation node) {
writeByte(Tag.StringConcatenation);
writeOffset(node.fileOffset);
writeNodeList(node.expressions);
}
visitIsExpression(IsExpression node) {
writeByte(Tag.IsExpression);
writeOffset(node.fileOffset);
writeNode(node.operand);
writeNode(node.type);
}
visitAsExpression(AsExpression node) {
writeByte(Tag.AsExpression);
writeOffset(node.fileOffset);
writeByte(node.flags);
writeNode(node.operand);
writeNode(node.type);
}
visitStringLiteral(StringLiteral node) {
writeByte(Tag.StringLiteral);
writeStringReference(node.value);
}
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');
}
}
visitDoubleLiteral(DoubleLiteral node) {
writeDouble(node.value);
}
writeDouble(double value) {
// TODO: Pick a better format for double literals.
writeByte(Tag.DoubleLiteral);
writeStringReference('$value');
}
visitBoolLiteral(BoolLiteral node) {
writeByte(node.value ? Tag.TrueLiteral : Tag.FalseLiteral);
}
visitNullLiteral(NullLiteral node) {
writeByte(Tag.NullLiteral);
}
visitSymbolLiteral(SymbolLiteral node) {
writeByte(Tag.SymbolLiteral);
writeStringReference(node.value);
}
visitTypeLiteral(TypeLiteral node) {
writeByte(Tag.TypeLiteral);
writeNode(node.type);
}
visitThisExpression(ThisExpression node) {
writeByte(Tag.ThisExpression);
}
visitRethrow(Rethrow node) {
writeByte(Tag.Rethrow);
writeOffset(node.fileOffset);
}
visitThrow(Throw node) {
writeByte(Tag.Throw);
writeOffset(node.fileOffset);
writeNode(node.expression);
}
visitListLiteral(ListLiteral node) {
writeByte(node.isConst ? Tag.ConstListLiteral : Tag.ListLiteral);
writeOffset(node.fileOffset);
writeNode(node.typeArgument);
writeNodeList(node.expressions);
}
visitMapLiteral(MapLiteral node) {
writeByte(node.isConst ? Tag.ConstMapLiteral : Tag.MapLiteral);
writeOffset(node.fileOffset);
writeNode(node.keyType);
writeNode(node.valueType);
writeNodeList(node.entries);
}
visitMapEntry(MapEntry node) {
// Note: there is no tag on MapEntry
writeNode(node.key);
writeNode(node.value);
}
visitAwaitExpression(AwaitExpression node) {
writeByte(Tag.AwaitExpression);
writeNode(node.operand);
}
visitFunctionExpression(FunctionExpression node) {
writeByte(Tag.FunctionExpression);
writeOffset(node.fileOffset);
writeNode(node.function);
}
visitLet(Let node) {
writeByte(Tag.Let);
writeVariableDeclaration(node.variable);
writeNode(node.body);
--_variableIndexer.stackHeight;
}
visitInstantiation(Instantiation node) {
writeByte(Tag.Instantiation);
writeNode(node.expression);
writeNodeList(node.typeArguments);
}
visitLoadLibrary(LoadLibrary node) {
writeByte(Tag.LoadLibrary);
writeLibraryDependencyReference(node.import);
}
visitCheckLibraryIsLoaded(CheckLibraryIsLoaded node) {
writeByte(Tag.CheckLibraryIsLoaded);
writeLibraryDependencyReference(node.import);
}
visitVectorCreation(VectorCreation node) {
writeByte(Tag.VectorCreation);
writeUInt30(node.length);
}
visitVectorGet(VectorGet node) {
writeByte(Tag.VectorGet);
writeNode(node.vectorExpression);
writeUInt30(node.index);
}
visitVectorSet(VectorSet node) {
writeByte(Tag.VectorSet);
writeNode(node.vectorExpression);
writeUInt30(node.index);
writeNode(node.value);
}
visitVectorCopy(VectorCopy node) {
writeByte(Tag.VectorCopy);
writeNode(node.vectorExpression);
}
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);
}
}
visitExpressionStatement(ExpressionStatement node) {
writeByte(Tag.ExpressionStatement);
writeNode(node.expression);
}
visitBlock(Block node) {
_variableIndexer.pushScope();
writeByte(Tag.Block);
writeNodeList(node.statements);
_variableIndexer.popScope();
}
visitEmptyStatement(EmptyStatement node) {
writeByte(Tag.EmptyStatement);
}
visitAssertStatement(AssertStatement node) {
writeByte(Tag.AssertStatement);
writeNode(node.condition);
writeOffset(node.conditionStartOffset);
writeOffset(node.conditionEndOffset);
writeOptionalNode(node.message);
}
visitLabeledStatement(LabeledStatement node) {
if (_labelIndexer == null) {
_labelIndexer = new LabelIndexer();
}
_labelIndexer.enter(node);
writeByte(Tag.LabeledStatement);
writeNode(node.body);
_labelIndexer.exit();
}
visitConstantExpression(ConstantExpression node) {
writeByte(Tag.ConstantExpression);
writeConstantReference(node.constant);
}
visitBreakStatement(BreakStatement node) {
writeByte(Tag.BreakStatement);
writeOffset(node.fileOffset);
writeUInt30(_labelIndexer[node.target]);
}
visitWhileStatement(WhileStatement node) {
writeByte(Tag.WhileStatement);
writeOffset(node.fileOffset);
writeNode(node.condition);
writeNode(node.body);
}
visitDoStatement(DoStatement node) {
writeByte(Tag.DoStatement);
writeOffset(node.fileOffset);
writeNode(node.body);
writeNode(node.condition);
}
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();
}
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();
}
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);
}
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);
}
visitContinueSwitchStatement(ContinueSwitchStatement node) {
writeByte(Tag.ContinueSwitchStatement);
writeOffset(node.fileOffset);
writeUInt30(_switchCaseIndexer[node.target]);
}
visitIfStatement(IfStatement node) {
writeByte(Tag.IfStatement);
writeOffset(node.fileOffset);
writeNode(node.condition);
writeNode(node.then);
writeStatementOrEmpty(node.otherwise);
}
visitReturnStatement(ReturnStatement node) {
writeByte(Tag.ReturnStatement);
writeOffset(node.fileOffset);
writeOptionalNode(node.expression);
}
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);
}
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();
}
visitTryFinally(TryFinally node) {
writeByte(Tag.TryFinally);
writeNode(node.body);
writeNode(node.finalizer);
}
visitYieldStatement(YieldStatement node) {
writeByte(Tag.YieldStatement);
writeOffset(node.fileOffset);
writeByte(node.flags);
writeNode(node.expression);
}
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);
}
}
visitFunctionDeclaration(FunctionDeclaration node) {
writeByte(Tag.FunctionDeclaration);
writeOffset(node.fileOffset);
writeVariableDeclaration(node.variable);
writeNode(node.function);
}
visitBottomType(BottomType node) {
writeByte(Tag.BottomType);
}
visitInvalidType(InvalidType node) {
writeByte(Tag.InvalidType);
}
visitDynamicType(DynamicType node) {
writeByte(Tag.DynamicType);
}
visitVoidType(VoidType node) {
writeByte(Tag.VoidType);
}
visitInterfaceType(InterfaceType node) {
if (node.typeArguments.isEmpty) {
writeByte(Tag.SimpleInterfaceType);
writeReference(node.className);
} else {
writeByte(Tag.InterfaceType);
writeReference(node.className);
writeNodeList(node.typeArguments);
}
}
visitSupertype(Supertype node) {
if (node.typeArguments.isEmpty) {
writeByte(Tag.SimpleInterfaceType);
writeReference(node.className);
} else {
writeByte(Tag.InterfaceType);
writeReference(node.className);
writeNodeList(node.typeArguments);
}
}
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);
}
}
visitNamedType(NamedType node) {
writeStringReference(node.name);
writeNode(node.type);
}
visitTypeParameterType(TypeParameterType node) {
writeByte(Tag.TypeParameterType);
writeUInt30(_typeParameterIndexer[node.parameter]);
writeOptionalNode(node.promotedBound);
}
visitVectorType(VectorType node) {
writeByte(Tag.VectorType);
}
visitTypedefType(TypedefType node) {
writeByte(Tag.TypedefType);
writeReference(node.typedefReference);
writeNodeList(node.typeArguments);
}
visitTypeParameter(TypeParameter node) {
writeByte(node.flags);
writeAnnotationList(node.annotations);
writeStringReference(node.name ?? '');
writeNode(node.bound);
}
defaultConstant(Constant node) {
throw 'Implement handling of ${node.runtimeType}';
}
defaultNode(Node node) {
throw 'Unsupported node: $node';
}
}
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;
}
}
}
visitProgram(Program 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;
}