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