fb745e6e1b
Prior to this CL we carried around information about the containing class and member, both of which was fetched by reading out-of-line in the binary (i.e. while reading the current member, start reading something from the parent member etc). It had also required the introduction of extra fields in the kernel binary file (dill file). This CL cleans that up, by a) Setting type parameters on functions as needed (in kernel_reader.cc) b) Using the VM Class and VM Function to get the required information (with a above the information is all available). (in kernel_binary_flowgraph.cc.) This means that c) We don't have to read the binary out-of-line (for TypeParameterType to work at least), and that d) We can remove the previously introduced extra fields from the kernel binary file (dill file). R=dmitryas@google.com, kmillikin@google.com Review-Url: https://codereview.chromium.org/2973633002 .
1289 lines
45 KiB
Dart
1289 lines
45 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_from_binary;
|
|
|
|
import 'dart:convert';
|
|
import 'dart:typed_data';
|
|
|
|
import '../ast.dart';
|
|
import '../transformations/flags.dart';
|
|
import 'tag.dart';
|
|
|
|
class ParseError {
|
|
String filename;
|
|
int byteIndex;
|
|
String message;
|
|
String path;
|
|
|
|
ParseError(this.message, {this.filename, this.byteIndex, this.path});
|
|
|
|
String toString() => '$filename:$byteIndex: $message at $path';
|
|
}
|
|
|
|
class BinaryBuilder {
|
|
final List<VariableDeclaration> variableStack = <VariableDeclaration>[];
|
|
final List<LabeledStatement> labelStack = <LabeledStatement>[];
|
|
int labelStackBase = 0;
|
|
final List<SwitchCase> switchCaseStack = <SwitchCase>[];
|
|
final List<TypeParameter> typeParameterStack = <TypeParameter>[];
|
|
final String filename;
|
|
final List<int> _bytes;
|
|
int _byteIndex = 0;
|
|
final List<String> _stringTable = <String>[];
|
|
final List<String> _sourceUriTable = <String>[];
|
|
List<CanonicalName> _linkTable;
|
|
int _transformerFlags = 0;
|
|
Library _currentLibrary;
|
|
|
|
// If something goes wrong, this list should indicate what library,
|
|
// class, and member was being built.
|
|
List<String> debugPath = <String>[];
|
|
|
|
bool _isReadingLibraryImplementation = false;
|
|
|
|
BinaryBuilder(this._bytes, [this.filename]);
|
|
|
|
fail(String message) {
|
|
throw new ParseError(message,
|
|
byteIndex: _byteIndex, filename: filename, path: debugPath.join('::'));
|
|
}
|
|
|
|
int readByte() => _bytes[_byteIndex++];
|
|
|
|
int readUInt() {
|
|
var byte = readByte();
|
|
if (byte & 0x80 == 0) {
|
|
// 0xxxxxxx
|
|
return byte;
|
|
} else if (byte & 0x40 == 0) {
|
|
// 10xxxxxx
|
|
return ((byte & 0x3F) << 8) | readByte();
|
|
} else {
|
|
// 11xxxxxx
|
|
return ((byte & 0x3F) << 24) |
|
|
(readByte() << 16) |
|
|
(readByte() << 8) |
|
|
readByte();
|
|
}
|
|
}
|
|
|
|
int readUint32() {
|
|
return (readByte() << 24) |
|
|
(readByte() << 16) |
|
|
(readByte() << 8) |
|
|
readByte();
|
|
}
|
|
|
|
List<int> readUtf8Bytes() {
|
|
List<int> bytes = new Uint8List(readUInt());
|
|
bytes.setRange(0, bytes.length, _bytes, _byteIndex);
|
|
_byteIndex += bytes.length;
|
|
return bytes;
|
|
}
|
|
|
|
String readStringEntry(int numBytes) {
|
|
// Utf8Decoder will skip leading BOM characters, but we must preserve them.
|
|
// Collect leading BOMs before passing the bytes onto Utf8Decoder.
|
|
int numByteOrderMarks = 0;
|
|
while (_byteIndex + 2 < _bytes.length &&
|
|
_bytes[_byteIndex] == 0xef &&
|
|
_bytes[_byteIndex + 1] == 0xbb &&
|
|
_bytes[_byteIndex + 2] == 0xbf) {
|
|
++numByteOrderMarks;
|
|
_byteIndex += 3;
|
|
numBytes -= 3;
|
|
}
|
|
String string =
|
|
const Utf8Decoder().convert(_bytes, _byteIndex, _byteIndex + numBytes);
|
|
_byteIndex += numBytes;
|
|
if (numByteOrderMarks > 0) {
|
|
return '\ufeff' * numByteOrderMarks + string;
|
|
}
|
|
return string;
|
|
}
|
|
|
|
void readStringTable(List<String> table) {
|
|
// Read the table of end offsets.
|
|
int length = readUInt();
|
|
List<int> endOffsets = new List<int>(length);
|
|
for (int i = 0; i < length; ++i) {
|
|
endOffsets[i] = readUInt();
|
|
}
|
|
// Read the UTF-8 encoded strings.
|
|
table.length = length;
|
|
int startOffset = 0;
|
|
for (int i = 0; i < length; ++i) {
|
|
table[i] = readStringEntry(endOffsets[i] - startOffset);
|
|
startOffset = endOffsets[i];
|
|
}
|
|
}
|
|
|
|
String readUriReference() {
|
|
return _sourceUriTable[readUInt()];
|
|
}
|
|
|
|
String readStringReference() {
|
|
return _stringTable[readUInt()];
|
|
}
|
|
|
|
List<String> readStringReferenceList() {
|
|
return new List<String>.generate(readUInt(), (i) => readStringReference());
|
|
}
|
|
|
|
String readStringOrNullIfEmpty() {
|
|
var string = readStringReference();
|
|
return string.isEmpty ? null : string;
|
|
}
|
|
|
|
bool readAndCheckOptionTag() {
|
|
int tag = readByte();
|
|
if (tag == Tag.Nothing) {
|
|
return false;
|
|
} else if (tag == Tag.Something) {
|
|
return true;
|
|
} else {
|
|
throw fail('Invalid Option tag: $tag');
|
|
}
|
|
}
|
|
|
|
List<Expression> readAnnotationList(TreeNode parent) {
|
|
int length = readUInt();
|
|
if (length == 0) return const <Expression>[];
|
|
List<Expression> list = new List<Expression>(length);
|
|
for (int i = 0; i < length; ++i) {
|
|
list[i] = readExpression()..parent = parent;
|
|
}
|
|
return list;
|
|
}
|
|
|
|
void _fillTreeNodeList(
|
|
List<TreeNode> list, TreeNode buildObject(), TreeNode parent) {
|
|
var length = readUInt();
|
|
list.length = length;
|
|
for (int i = 0; i < length; ++i) {
|
|
TreeNode object = buildObject();
|
|
list[i] = object..parent = parent;
|
|
}
|
|
}
|
|
|
|
void _fillNonTreeNodeList(List<Node> list, Node buildObject()) {
|
|
var length = readUInt();
|
|
list.length = length;
|
|
for (int i = 0; i < length; ++i) {
|
|
Node object = buildObject();
|
|
list[i] = object;
|
|
}
|
|
}
|
|
|
|
void _skipNodeList(Node skipObject()) {
|
|
var length = readUInt();
|
|
for (int i = 0; i < length; ++i) {
|
|
skipObject();
|
|
}
|
|
}
|
|
|
|
/// Reads a list of named nodes, reusing any existing objects already in the
|
|
/// linking tree. The nodes are merged into [list], and if reading the library
|
|
/// implementation, the order is corrected.
|
|
///
|
|
/// [readObject] should read the object definition and its canonical name.
|
|
/// If an existing object is bound to the canonical name, the existing object
|
|
/// must be reused and returned.
|
|
void _mergeNamedNodeList(
|
|
List<NamedNode> list, NamedNode readObject(), TreeNode parent) {
|
|
if (_isReadingLibraryImplementation) {
|
|
// When reading the library implementation, overwrite the whole list
|
|
// with the new one.
|
|
_fillTreeNodeList(list, readObject, parent);
|
|
} else {
|
|
// When reading an external library, the results should either be:
|
|
// - merged with the existing external library definition (if any)
|
|
// - ignored if the library implementation is already in memory
|
|
int numberOfNodes = readUInt();
|
|
for (int i = 0; i < numberOfNodes; ++i) {
|
|
var value = readObject();
|
|
// We use the parent pointer of a node to determine if it already is in
|
|
// the AST and hence should not be added again.
|
|
if (value.parent == null) {
|
|
list.add(value..parent = parent);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void readLinkTable(CanonicalName linkRoot) {
|
|
int length = readUInt();
|
|
_linkTable = new List<CanonicalName>(length);
|
|
for (int i = 0; i < length; ++i) {
|
|
int biasedParentIndex = readUInt();
|
|
String name = readStringReference();
|
|
var parent =
|
|
biasedParentIndex == 0 ? linkRoot : _linkTable[biasedParentIndex - 1];
|
|
_linkTable[i] = parent.getChild(name);
|
|
}
|
|
}
|
|
|
|
/// Deserializes a kernel program and stores it in [program].
|
|
///
|
|
/// When linking with a non-empty program, canonical names must have been
|
|
/// computed ahead of time.
|
|
///
|
|
/// The input bytes may contain multiple files concatenated.
|
|
void readProgram(Program program) {
|
|
while (_byteIndex < _bytes.length) {
|
|
_readOneProgram(program);
|
|
}
|
|
}
|
|
|
|
/// Reads a single program file from the input and loads it into [program],
|
|
/// overwriting and reusing any existing data in the program.
|
|
///
|
|
/// When linking with a non-empty program, canonical names must have been
|
|
/// computed ahead of time.
|
|
///
|
|
/// This should *only* be used when there is a reason to not allow
|
|
/// concatenated files.
|
|
void readSingleFileProgram(Program program) {
|
|
_readOneProgram(program);
|
|
if (_byteIndex < _bytes.length) {
|
|
if (_byteIndex + 3 < _bytes.length) {
|
|
int magic = readUint32();
|
|
if (magic == Tag.ProgramFile) {
|
|
throw 'Concatenated program file given when a single program '
|
|
'was expected.';
|
|
}
|
|
}
|
|
throw 'Unrecognized bytes following program data';
|
|
}
|
|
}
|
|
|
|
void _readOneProgram(Program program) {
|
|
int magic = readUint32();
|
|
if (magic != Tag.ProgramFile) {
|
|
throw fail('This is not a binary dart file. '
|
|
'Magic number was: ${magic.toRadixString(16)}');
|
|
}
|
|
readStringTable(_stringTable);
|
|
Map<String, Source> uriToSource = readUriToSource();
|
|
program.uriToSource.addAll(uriToSource);
|
|
readLinkTable(program.root);
|
|
int numberOfLibraries = readUInt();
|
|
List<Library> libraries = new List<Library>(numberOfLibraries);
|
|
for (int i = 0; i < numberOfLibraries; ++i) {
|
|
libraries[i] = readLibrary(program);
|
|
}
|
|
var mainMethod = readMemberReference(allowNull: true);
|
|
program.mainMethodName ??= mainMethod;
|
|
|
|
// Read the program index.
|
|
readUint32(); // binary offset for source table.
|
|
readUint32(); // binary offset for link table.
|
|
readUint32(); // main
|
|
for (int i = 0; i < numberOfLibraries; i++) {
|
|
readUint32(); // binary offset for library #i.
|
|
}
|
|
int numberOfLibrariesCheck = readUint32();
|
|
if (numberOfLibraries != numberOfLibrariesCheck) {
|
|
throw 'Malformed binary: the program index indicates there are '
|
|
'$numberOfLibrariesCheck libraries but the binary contains '
|
|
'$numberOfLibraries.';
|
|
}
|
|
}
|
|
|
|
Map<String, Source> readUriToSource() {
|
|
readStringTable(_sourceUriTable);
|
|
int length = _sourceUriTable.length;
|
|
Map<String, Source> uriToSource = <String, Source>{};
|
|
for (int i = 0; i < length; ++i) {
|
|
String uri = _sourceUriTable[i];
|
|
List<int> sourceCode = readUtf8Bytes();
|
|
int lineCount = readUInt();
|
|
List<int> lineStarts = new List<int>(lineCount);
|
|
int previousLineStart = 0;
|
|
for (int j = 0; j < lineCount; ++j) {
|
|
int lineStart = readUInt() + previousLineStart;
|
|
lineStarts[j] = lineStart;
|
|
previousLineStart = lineStart;
|
|
}
|
|
uriToSource[uri] = new Source(lineStarts, sourceCode);
|
|
}
|
|
return uriToSource;
|
|
}
|
|
|
|
CanonicalName readCanonicalNameReference() {
|
|
var index = readUInt();
|
|
if (index == 0) return null;
|
|
return _linkTable[index - 1];
|
|
}
|
|
|
|
Reference readLibraryReference() {
|
|
return readCanonicalNameReference().getReference();
|
|
}
|
|
|
|
LibraryDependency readLibraryDependencyReference() {
|
|
int index = readUInt();
|
|
return _currentLibrary.dependencies[index];
|
|
}
|
|
|
|
Reference readClassReference({bool allowNull: false}) {
|
|
var name = readCanonicalNameReference();
|
|
if (name == null && !allowNull) {
|
|
throw 'Expected a class reference to be valid but was `null`.';
|
|
}
|
|
return name?.getReference();
|
|
}
|
|
|
|
Reference readMemberReference({bool allowNull: false}) {
|
|
var name = readCanonicalNameReference();
|
|
if (name == null && !allowNull) {
|
|
throw 'Expected a member reference to be valid but was `null`.';
|
|
}
|
|
return name?.getReference();
|
|
}
|
|
|
|
Reference readTypedefReference() {
|
|
return readCanonicalNameReference()?.getReference();
|
|
}
|
|
|
|
Name readName() {
|
|
String text = readStringReference();
|
|
if (text.isNotEmpty && text[0] == '_') {
|
|
return new Name.byReference(text, readLibraryReference());
|
|
} else {
|
|
return new Name(text);
|
|
}
|
|
}
|
|
|
|
Library readLibrary(Program program) {
|
|
int flags = readByte();
|
|
bool isExternal = (flags & 0x1) != 0;
|
|
_isReadingLibraryImplementation = !isExternal;
|
|
var canonicalName = readCanonicalNameReference();
|
|
Reference reference = canonicalName.getReference();
|
|
Library library = reference.node;
|
|
bool shouldWriteData = library == null || _isReadingLibraryImplementation;
|
|
if (library == null) {
|
|
library =
|
|
new Library(Uri.parse(canonicalName.name), reference: reference);
|
|
program.libraries.add(library..parent = program);
|
|
}
|
|
_currentLibrary = library;
|
|
String name = readStringOrNullIfEmpty();
|
|
// TODO(jensj): We currently save (almost the same) uri twice.
|
|
String fileUri = readUriReference();
|
|
|
|
if (shouldWriteData) {
|
|
library.isExternal = isExternal;
|
|
library.name = name;
|
|
library.fileUri = fileUri;
|
|
}
|
|
|
|
debugPath.add(library.name ?? library.importUri?.toString() ?? 'library');
|
|
|
|
if (shouldWriteData) {
|
|
_fillTreeNodeList(library.annotations, readExpression, library);
|
|
} else {
|
|
_skipNodeList(readExpression);
|
|
}
|
|
_readLibraryDependencies(library);
|
|
_readLibraryParts(library);
|
|
_mergeNamedNodeList(library.typedefs, readTypedef, library);
|
|
_mergeNamedNodeList(library.classes, readClass, library);
|
|
_mergeNamedNodeList(library.fields, readField, library);
|
|
_mergeNamedNodeList(library.procedures, readProcedure, library);
|
|
|
|
debugPath.removeLast();
|
|
_currentLibrary = null;
|
|
return library;
|
|
}
|
|
|
|
void _readLibraryDependencies(Library library) {
|
|
int length = readUInt();
|
|
if (library.isExternal) {
|
|
assert(length == 0);
|
|
return;
|
|
}
|
|
library.dependencies.length = length;
|
|
for (int i = 0; i < length; ++i) {
|
|
var flags = readByte();
|
|
var annotations = readExpressionList();
|
|
var targetLibrary = readLibraryReference();
|
|
var prefixName = readStringOrNullIfEmpty();
|
|
var names = readCombinatorList();
|
|
library.dependencies[i] = new LibraryDependency.byReference(
|
|
flags, annotations, targetLibrary, prefixName, names)
|
|
..parent = library;
|
|
}
|
|
}
|
|
|
|
Combinator readCombinator() {
|
|
var isShow = readUInt() == 1;
|
|
var names = readStringReferenceList();
|
|
return new Combinator(isShow, names);
|
|
}
|
|
|
|
List<Combinator> readCombinatorList() {
|
|
return new List<Combinator>.generate(readUInt(), (i) => readCombinator());
|
|
}
|
|
|
|
void _readLibraryParts(Library library) {
|
|
int length = readUInt();
|
|
library.parts.length = length;
|
|
for (int i = 0; i < length; ++i) {
|
|
var annotations = readExpressionList();
|
|
var fileUri = readStringOrNullIfEmpty();
|
|
library.parts[i] = new LibraryPart(annotations, fileUri)
|
|
..parent = library;
|
|
}
|
|
}
|
|
|
|
Typedef readTypedef() {
|
|
var canonicalName = readCanonicalNameReference();
|
|
var reference = canonicalName.getReference();
|
|
Typedef node = reference.node;
|
|
bool shouldWriteData = node == null || _isReadingLibraryImplementation;
|
|
if (node == null) {
|
|
node = new Typedef(null, null, reference: reference);
|
|
}
|
|
int fileOffset = readOffset();
|
|
String name = readStringReference();
|
|
String fileUri = readUriReference();
|
|
readAndPushTypeParameterList(node.typeParameters, node);
|
|
var type = readDartType();
|
|
typeParameterStack.length = 0;
|
|
if (shouldWriteData) {
|
|
node.fileOffset = fileOffset;
|
|
node.name = name;
|
|
node.fileUri = fileUri;
|
|
node.type = type;
|
|
}
|
|
return node;
|
|
}
|
|
|
|
Class readClass() {
|
|
int tag = readByte();
|
|
assert(tag == Tag.Class);
|
|
var canonicalName = readCanonicalNameReference();
|
|
var reference = canonicalName.getReference();
|
|
Class node = reference.node;
|
|
bool shouldWriteData = node == null || _isReadingLibraryImplementation;
|
|
if (node == null) {
|
|
node = new Class(reference: reference)..level = ClassLevel.Temporary;
|
|
}
|
|
node.fileOffset = readOffset();
|
|
node.fileEndOffset = readOffset();
|
|
int flags = readByte();
|
|
node.isAbstract = flags & 0x1 != 0;
|
|
node.isEnum = flags & 0x2 != 0;
|
|
node.isSyntheticMixinImplementation = flags & 0x4 != 0;
|
|
int levelIndex = (flags >> 3) & 0x3;
|
|
var level = ClassLevel.values[levelIndex + 1];
|
|
if (level.index >= node.level.index) {
|
|
node.level = level;
|
|
}
|
|
var name = readStringOrNullIfEmpty();
|
|
var fileUri = readUriReference();
|
|
var documentationComment = readStringOrNullIfEmpty();
|
|
var annotations = readAnnotationList(node);
|
|
debugPath.add(node.name ?? 'normal-class');
|
|
readAndPushTypeParameterList(node.typeParameters, node);
|
|
var supertype = readSupertypeOption();
|
|
var mixedInType = readSupertypeOption();
|
|
if (shouldWriteData) {
|
|
_fillNonTreeNodeList(node.implementedTypes, readSupertype);
|
|
} else {
|
|
_skipNodeList(readSupertype);
|
|
}
|
|
_mergeNamedNodeList(node.fields, readField, node);
|
|
_mergeNamedNodeList(node.constructors, readConstructor, node);
|
|
_mergeNamedNodeList(node.procedures, readProcedure, node);
|
|
typeParameterStack.length = 0;
|
|
debugPath.removeLast();
|
|
if (shouldWriteData) {
|
|
node.name = name;
|
|
node.fileUri = fileUri;
|
|
node.documentationComment = documentationComment;
|
|
node.annotations = annotations;
|
|
node.supertype = supertype;
|
|
node.mixedInType = mixedInType;
|
|
}
|
|
return node;
|
|
}
|
|
|
|
int getAndResetTransformerFlags() {
|
|
int flags = _transformerFlags;
|
|
_transformerFlags = 0;
|
|
return flags;
|
|
}
|
|
|
|
/// Adds the given flag to the current [Member.transformerFlags].
|
|
void addTransformerFlag(int flags) {
|
|
_transformerFlags |= flags;
|
|
}
|
|
|
|
Field readField() {
|
|
int tag = readByte();
|
|
assert(tag == Tag.Field);
|
|
var canonicalName = readCanonicalNameReference();
|
|
var reference = canonicalName.getReference();
|
|
Field node = reference.node;
|
|
bool shouldWriteData = node == null || _isReadingLibraryImplementation;
|
|
if (node == null) {
|
|
node = new Field(null, reference: reference);
|
|
}
|
|
int fileOffset = readOffset();
|
|
int fileEndOffset = readOffset();
|
|
int flags = readByte();
|
|
readUInt(); // parent class binary offset.
|
|
var name = readName();
|
|
var fileUri = readUriReference();
|
|
var documentationComment = readStringOrNullIfEmpty();
|
|
var annotations = readAnnotationList(node);
|
|
debugPath.add(node.name?.name ?? 'field');
|
|
var type = readDartType();
|
|
var initializer = readExpressionOption();
|
|
int transformerFlags = getAndResetTransformerFlags();
|
|
debugPath.removeLast();
|
|
if (shouldWriteData) {
|
|
node.fileOffset = fileOffset;
|
|
node.fileEndOffset = fileEndOffset;
|
|
node.flags = flags;
|
|
node.name = name;
|
|
node.fileUri = fileUri;
|
|
node.documentationComment = documentationComment;
|
|
node.annotations = annotations;
|
|
node.type = type;
|
|
node.initializer = initializer;
|
|
node.initializer?.parent = node;
|
|
node.transformerFlags = transformerFlags;
|
|
}
|
|
return node;
|
|
}
|
|
|
|
Constructor readConstructor() {
|
|
int tag = readByte();
|
|
assert(tag == Tag.Constructor);
|
|
var canonicalName = readCanonicalNameReference();
|
|
var reference = canonicalName.getReference();
|
|
Constructor node = reference.node;
|
|
bool shouldWriteData = node == null || _isReadingLibraryImplementation;
|
|
if (node == null) {
|
|
node = new Constructor(null, reference: reference);
|
|
}
|
|
var fileOffset = readOffset();
|
|
var fileEndOffset = readOffset();
|
|
var flags = readByte();
|
|
readUInt(); // parent class binary offset.
|
|
var name = readName();
|
|
var documentationComment = readStringOrNullIfEmpty();
|
|
var annotations = readAnnotationList(node);
|
|
debugPath.add(node.name?.name ?? 'constructor');
|
|
var function = readFunctionNode();
|
|
pushVariableDeclarations(function.positionalParameters);
|
|
pushVariableDeclarations(function.namedParameters);
|
|
if (shouldWriteData) {
|
|
_fillTreeNodeList(node.initializers, readInitializer, node);
|
|
} else {
|
|
_skipNodeList(readInitializer);
|
|
}
|
|
variableStack.length = 0;
|
|
var transformerFlags = getAndResetTransformerFlags();
|
|
debugPath.removeLast();
|
|
if (shouldWriteData) {
|
|
node.fileOffset = fileOffset;
|
|
node.fileEndOffset = fileEndOffset;
|
|
node.flags = flags;
|
|
node.name = name;
|
|
node.documentationComment = documentationComment;
|
|
node.annotations = annotations;
|
|
node.function = function..parent = node;
|
|
node.transformerFlags = transformerFlags;
|
|
}
|
|
return node;
|
|
}
|
|
|
|
Procedure readProcedure() {
|
|
int tag = readByte();
|
|
assert(tag == Tag.Procedure);
|
|
var canonicalName = readCanonicalNameReference();
|
|
var reference = canonicalName.getReference();
|
|
Procedure node = reference.node;
|
|
bool shouldWriteData = node == null || _isReadingLibraryImplementation;
|
|
if (node == null) {
|
|
node = new Procedure(null, null, null, reference: reference);
|
|
}
|
|
var fileOffset = readOffset();
|
|
var fileEndOffset = readOffset();
|
|
int kindIndex = readByte();
|
|
var kind = ProcedureKind.values[kindIndex];
|
|
var flags = readByte();
|
|
readUInt(); // parent class binary offset.
|
|
var name = readName();
|
|
var fileUri = readUriReference();
|
|
var documentationComment = readStringOrNullIfEmpty();
|
|
var annotations = readAnnotationList(node);
|
|
debugPath.add(node.name?.name ?? 'procedure');
|
|
var function = readFunctionNodeOption();
|
|
var transformerFlags = getAndResetTransformerFlags();
|
|
debugPath.removeLast();
|
|
if (shouldWriteData) {
|
|
node.fileOffset = fileOffset;
|
|
node.fileEndOffset = fileEndOffset;
|
|
node.kind = kind;
|
|
node.flags = flags;
|
|
node.name = name;
|
|
node.fileUri = fileUri;
|
|
node.documentationComment = documentationComment;
|
|
node.annotations = annotations;
|
|
node.function = function;
|
|
node.function?.parent = node;
|
|
node.transformerFlags = transformerFlags;
|
|
}
|
|
return node;
|
|
}
|
|
|
|
Initializer readInitializer() {
|
|
int tag = readByte();
|
|
bool isSynthetic = readByte() == 1;
|
|
switch (tag) {
|
|
case Tag.InvalidInitializer:
|
|
return new InvalidInitializer();
|
|
case Tag.FieldInitializer:
|
|
var reference = readMemberReference();
|
|
var value = readExpression();
|
|
return new FieldInitializer.byReference(reference, value)
|
|
..isSynthetic = isSynthetic;
|
|
case Tag.SuperInitializer:
|
|
var reference = readMemberReference();
|
|
var arguments = readArguments();
|
|
return new SuperInitializer.byReference(reference, arguments)
|
|
..isSynthetic = isSynthetic;
|
|
case Tag.RedirectingInitializer:
|
|
return new RedirectingInitializer.byReference(
|
|
readMemberReference(), readArguments());
|
|
case Tag.LocalInitializer:
|
|
return new LocalInitializer(readAndPushVariableDeclaration());
|
|
default:
|
|
throw fail('Invalid initializer tag: $tag');
|
|
}
|
|
}
|
|
|
|
FunctionNode readFunctionNodeOption() {
|
|
return readAndCheckOptionTag() ? readFunctionNode() : null;
|
|
}
|
|
|
|
FunctionNode readFunctionNode() {
|
|
int tag = readByte();
|
|
assert(tag == Tag.FunctionNode);
|
|
int offset = readOffset();
|
|
int endOffset = readOffset();
|
|
AsyncMarker asyncMarker = AsyncMarker.values[readByte()];
|
|
AsyncMarker dartAsyncMarker = AsyncMarker.values[readByte()];
|
|
int typeParameterStackHeight = typeParameterStack.length;
|
|
var typeParameters = readAndPushTypeParameterList();
|
|
readUInt(); // total parameter count.
|
|
var requiredParameterCount = readUInt();
|
|
int variableStackHeight = variableStack.length;
|
|
var positional = readAndPushVariableDeclarationList();
|
|
var named = readAndPushVariableDeclarationList();
|
|
var returnType = readDartType();
|
|
int oldLabelStackBase = labelStackBase;
|
|
labelStackBase = labelStack.length;
|
|
var body = readStatementOption();
|
|
labelStackBase = oldLabelStackBase;
|
|
variableStack.length = variableStackHeight;
|
|
typeParameterStack.length = typeParameterStackHeight;
|
|
return new FunctionNode(body,
|
|
typeParameters: typeParameters,
|
|
requiredParameterCount: requiredParameterCount,
|
|
positionalParameters: positional,
|
|
namedParameters: named,
|
|
returnType: returnType,
|
|
asyncMarker: asyncMarker,
|
|
dartAsyncMarker: dartAsyncMarker)
|
|
..fileOffset = offset
|
|
..fileEndOffset = endOffset;
|
|
}
|
|
|
|
void pushVariableDeclaration(VariableDeclaration variable) {
|
|
variableStack.add(variable);
|
|
}
|
|
|
|
void pushVariableDeclarations(List<VariableDeclaration> variables) {
|
|
variableStack.addAll(variables);
|
|
}
|
|
|
|
VariableDeclaration readVariableReference() {
|
|
int index = readUInt();
|
|
if (index >= variableStack.length) {
|
|
throw fail('Invalid variable index: $index');
|
|
}
|
|
return variableStack[index];
|
|
}
|
|
|
|
String logicalOperatorToString(int index) {
|
|
switch (index) {
|
|
case 0:
|
|
return '&&';
|
|
case 1:
|
|
return '||';
|
|
default:
|
|
throw fail('Invalid logical operator index: $index');
|
|
}
|
|
}
|
|
|
|
List<Expression> readExpressionList() {
|
|
return new List<Expression>.generate(readUInt(), (i) => readExpression());
|
|
}
|
|
|
|
Expression readExpressionOption() {
|
|
return readAndCheckOptionTag() ? readExpression() : null;
|
|
}
|
|
|
|
Expression readExpression() {
|
|
int tagByte = readByte();
|
|
int tag = tagByte & Tag.SpecializedTagHighBit == 0
|
|
? tagByte
|
|
: (tagByte & Tag.SpecializedTagMask);
|
|
switch (tag) {
|
|
case Tag.LoadLibrary:
|
|
return new LoadLibrary(readLibraryDependencyReference());
|
|
case Tag.CheckLibraryIsLoaded:
|
|
return new CheckLibraryIsLoaded(readLibraryDependencyReference());
|
|
case Tag.InvalidExpression:
|
|
return new InvalidExpression();
|
|
case Tag.VariableGet:
|
|
int offset = readOffset();
|
|
readUInt(); // offset of the variable declaration in the binary.
|
|
return new VariableGet(readVariableReference(), readDartTypeOption())
|
|
..fileOffset = offset;
|
|
case Tag.SpecializedVariableGet:
|
|
int index = tagByte & Tag.SpecializedPayloadMask;
|
|
int offset = readOffset();
|
|
readUInt(); // offset of the variable declaration in the binary.
|
|
return new VariableGet(variableStack[index])..fileOffset = offset;
|
|
case Tag.VariableSet:
|
|
int offset = readOffset();
|
|
readUInt(); // offset of the variable declaration in the binary.
|
|
return new VariableSet(readVariableReference(), readExpression())
|
|
..fileOffset = offset;
|
|
case Tag.SpecializedVariableSet:
|
|
int index = tagByte & Tag.SpecializedPayloadMask;
|
|
int offset = readOffset();
|
|
readUInt(); // offset of the variable declaration in the binary.
|
|
return new VariableSet(variableStack[index], readExpression())
|
|
..fileOffset = offset;
|
|
case Tag.PropertyGet:
|
|
int offset = readOffset();
|
|
return new PropertyGet.byReference(
|
|
readExpression(), readName(), readMemberReference(allowNull: true))
|
|
..fileOffset = offset;
|
|
case Tag.PropertySet:
|
|
int offset = readOffset();
|
|
return new PropertySet.byReference(readExpression(), readName(),
|
|
readExpression(), readMemberReference(allowNull: true))
|
|
..fileOffset = offset;
|
|
case Tag.SuperPropertyGet:
|
|
addTransformerFlag(TransformerFlag.superCalls);
|
|
return new SuperPropertyGet.byReference(
|
|
readName(), readMemberReference(allowNull: true));
|
|
case Tag.SuperPropertySet:
|
|
addTransformerFlag(TransformerFlag.superCalls);
|
|
return new SuperPropertySet.byReference(
|
|
readName(), readExpression(), readMemberReference(allowNull: true));
|
|
case Tag.DirectPropertyGet:
|
|
int offset = readOffset();
|
|
return new DirectPropertyGet.byReference(
|
|
readExpression(), readMemberReference())
|
|
..fileOffset = offset;
|
|
case Tag.DirectPropertySet:
|
|
int offset = readOffset();
|
|
return new DirectPropertySet.byReference(
|
|
readExpression(), readMemberReference(), readExpression())
|
|
..fileOffset = offset;
|
|
case Tag.StaticGet:
|
|
int offset = readOffset();
|
|
return new StaticGet.byReference(readMemberReference())
|
|
..fileOffset = offset;
|
|
case Tag.StaticSet:
|
|
int offset = readOffset();
|
|
return new StaticSet.byReference(
|
|
readMemberReference(), readExpression())
|
|
..fileOffset = offset;
|
|
case Tag.MethodInvocation:
|
|
int offset = readOffset();
|
|
return new MethodInvocation.byReference(readExpression(), readName(),
|
|
readArguments(), readMemberReference(allowNull: true))
|
|
..fileOffset = offset;
|
|
case Tag.SuperMethodInvocation:
|
|
int offset = readOffset();
|
|
addTransformerFlag(TransformerFlag.superCalls);
|
|
return new SuperMethodInvocation.byReference(
|
|
readName(), readArguments(), readMemberReference(allowNull: true))
|
|
..fileOffset = offset;
|
|
case Tag.DirectMethodInvocation:
|
|
return new DirectMethodInvocation.byReference(
|
|
readExpression(), readMemberReference(), readArguments());
|
|
case Tag.StaticInvocation:
|
|
int offset = readOffset();
|
|
return new StaticInvocation.byReference(
|
|
readMemberReference(), readArguments(),
|
|
isConst: false)
|
|
..fileOffset = offset;
|
|
case Tag.ConstStaticInvocation:
|
|
int offset = readOffset();
|
|
return new StaticInvocation.byReference(
|
|
readMemberReference(), readArguments(),
|
|
isConst: true)
|
|
..fileOffset = offset;
|
|
case Tag.ConstructorInvocation:
|
|
int offset = readOffset();
|
|
return new ConstructorInvocation.byReference(
|
|
readMemberReference(), readArguments(),
|
|
isConst: false)
|
|
..fileOffset = offset;
|
|
case Tag.ConstConstructorInvocation:
|
|
int offset = readOffset();
|
|
return new ConstructorInvocation.byReference(
|
|
readMemberReference(), readArguments(),
|
|
isConst: true)
|
|
..fileOffset = offset;
|
|
case Tag.Not:
|
|
return new Not(readExpression());
|
|
case Tag.LogicalExpression:
|
|
return new LogicalExpression(readExpression(),
|
|
logicalOperatorToString(readByte()), readExpression());
|
|
case Tag.ConditionalExpression:
|
|
return new ConditionalExpression(readExpression(), readExpression(),
|
|
readExpression(), readDartTypeOption());
|
|
case Tag.StringConcatenation:
|
|
int offset = readOffset();
|
|
return new StringConcatenation(readExpressionList())
|
|
..fileOffset = offset;
|
|
case Tag.IsExpression:
|
|
int offset = readOffset();
|
|
return new IsExpression(readExpression(), readDartType())
|
|
..fileOffset = offset;
|
|
case Tag.AsExpression:
|
|
int offset = readOffset();
|
|
return new AsExpression(readExpression(), readDartType())
|
|
..fileOffset = offset;
|
|
case Tag.StringLiteral:
|
|
return new StringLiteral(readStringReference());
|
|
case Tag.SpecializedIntLiteral:
|
|
int biasedValue = tagByte & Tag.SpecializedPayloadMask;
|
|
return new IntLiteral(biasedValue - Tag.SpecializedIntLiteralBias);
|
|
case Tag.PositiveIntLiteral:
|
|
return new IntLiteral(readUInt());
|
|
case Tag.NegativeIntLiteral:
|
|
return new IntLiteral(-readUInt());
|
|
case Tag.BigIntLiteral:
|
|
return new IntLiteral(int.parse(readStringReference()));
|
|
case Tag.DoubleLiteral:
|
|
return new DoubleLiteral(double.parse(readStringReference()));
|
|
case Tag.TrueLiteral:
|
|
return new BoolLiteral(true);
|
|
case Tag.FalseLiteral:
|
|
return new BoolLiteral(false);
|
|
case Tag.NullLiteral:
|
|
return new NullLiteral();
|
|
case Tag.SymbolLiteral:
|
|
return new SymbolLiteral(readStringReference());
|
|
case Tag.TypeLiteral:
|
|
return new TypeLiteral(readDartType());
|
|
case Tag.ThisExpression:
|
|
return new ThisExpression();
|
|
case Tag.Rethrow:
|
|
int offset = readOffset();
|
|
return new Rethrow()..fileOffset = offset;
|
|
case Tag.Throw:
|
|
int offset = readOffset();
|
|
return new Throw(readExpression())..fileOffset = offset;
|
|
case Tag.ListLiteral:
|
|
int offset = readOffset();
|
|
var typeArgument = readDartType();
|
|
return new ListLiteral(readExpressionList(),
|
|
typeArgument: typeArgument, isConst: false)
|
|
..fileOffset = offset;
|
|
case Tag.ConstListLiteral:
|
|
int offset = readOffset();
|
|
var typeArgument = readDartType();
|
|
return new ListLiteral(readExpressionList(),
|
|
typeArgument: typeArgument, isConst: true)
|
|
..fileOffset = offset;
|
|
case Tag.MapLiteral:
|
|
int offset = readOffset();
|
|
var keyType = readDartType();
|
|
var valueType = readDartType();
|
|
return new MapLiteral(readMapEntryList(),
|
|
keyType: keyType, valueType: valueType, isConst: false)
|
|
..fileOffset = offset;
|
|
case Tag.ConstMapLiteral:
|
|
int offset = readOffset();
|
|
var keyType = readDartType();
|
|
var valueType = readDartType();
|
|
return new MapLiteral(readMapEntryList(),
|
|
keyType: keyType, valueType: valueType, isConst: true)
|
|
..fileOffset = offset;
|
|
case Tag.AwaitExpression:
|
|
return new AwaitExpression(readExpression());
|
|
case Tag.FunctionExpression:
|
|
return new FunctionExpression(readFunctionNode());
|
|
case Tag.Let:
|
|
var variable = readVariableDeclaration();
|
|
int stackHeight = variableStack.length;
|
|
pushVariableDeclaration(variable);
|
|
var body = readExpression();
|
|
variableStack.length = stackHeight;
|
|
return new Let(variable, body);
|
|
case Tag.VectorCreation:
|
|
var length = readUInt();
|
|
return new VectorCreation(length);
|
|
case Tag.VectorGet:
|
|
var vectorExpression = readExpression();
|
|
var index = readUInt();
|
|
return new VectorGet(vectorExpression, index);
|
|
case Tag.VectorSet:
|
|
var vectorExpression = readExpression();
|
|
var index = readUInt();
|
|
var value = readExpression();
|
|
return new VectorSet(vectorExpression, index, value);
|
|
case Tag.VectorCopy:
|
|
var vectorExpression = readExpression();
|
|
return new VectorCopy(vectorExpression);
|
|
case Tag.ClosureCreation:
|
|
var topLevelFunctionReference = readMemberReference();
|
|
var contextVector = readExpression();
|
|
var functionType = readDartType();
|
|
var typeArgs = readDartTypeList();
|
|
return new ClosureCreation.byReference(
|
|
topLevelFunctionReference, contextVector, functionType, typeArgs);
|
|
default:
|
|
throw fail('Invalid expression tag: $tag');
|
|
}
|
|
}
|
|
|
|
List<MapEntry> readMapEntryList() {
|
|
return new List<MapEntry>.generate(readUInt(), (i) => readMapEntry());
|
|
}
|
|
|
|
MapEntry readMapEntry() {
|
|
return new MapEntry(readExpression(), readExpression());
|
|
}
|
|
|
|
List<Statement> readStatementList() {
|
|
return new List<Statement>.generate(readUInt(), (i) => readStatement());
|
|
}
|
|
|
|
Statement readStatementOrNullIfEmpty() {
|
|
var node = readStatement();
|
|
if (node is EmptyStatement) {
|
|
return null;
|
|
} else {
|
|
return node;
|
|
}
|
|
}
|
|
|
|
Statement readStatementOption() {
|
|
return readAndCheckOptionTag() ? readStatement() : null;
|
|
}
|
|
|
|
Statement readStatement() {
|
|
int tag = readByte();
|
|
switch (tag) {
|
|
case Tag.InvalidStatement:
|
|
return new InvalidStatement();
|
|
case Tag.ExpressionStatement:
|
|
return new ExpressionStatement(readExpression());
|
|
case Tag.Block:
|
|
return readBlock();
|
|
case Tag.EmptyStatement:
|
|
return new EmptyStatement();
|
|
case Tag.AssertStatement:
|
|
return new AssertStatement(readExpression(),
|
|
conditionStartOffset: readOffset(),
|
|
conditionEndOffset: readOffset(),
|
|
message: readExpressionOption());
|
|
case Tag.LabeledStatement:
|
|
var label = new LabeledStatement(null);
|
|
labelStack.add(label);
|
|
label.body = readStatement()..parent = label;
|
|
labelStack.removeLast();
|
|
return label;
|
|
case Tag.BreakStatement:
|
|
int offset = readOffset();
|
|
int index = readUInt();
|
|
return new BreakStatement(labelStack[labelStackBase + index])
|
|
..fileOffset = offset;
|
|
case Tag.WhileStatement:
|
|
var offset = readOffset();
|
|
return new WhileStatement(readExpression(), readStatement())
|
|
..fileOffset = offset;
|
|
case Tag.DoStatement:
|
|
var offset = readOffset();
|
|
return new DoStatement(readStatement(), readExpression())
|
|
..fileOffset = offset;
|
|
case Tag.ForStatement:
|
|
int variableStackHeight = variableStack.length;
|
|
var offset = readOffset();
|
|
var variables = readAndPushVariableDeclarationList();
|
|
var condition = readExpressionOption();
|
|
var updates = readExpressionList();
|
|
var body = readStatement();
|
|
variableStack.length = variableStackHeight;
|
|
return new ForStatement(variables, condition, updates, body)
|
|
..fileOffset = offset;
|
|
case Tag.ForInStatement:
|
|
case Tag.AsyncForInStatement:
|
|
bool isAsync = tag == Tag.AsyncForInStatement;
|
|
int variableStackHeight = variableStack.length;
|
|
var offset = readOffset();
|
|
var bodyOffset = readOffset();
|
|
var variable = readAndPushVariableDeclaration();
|
|
var iterable = readExpression();
|
|
var body = readStatement();
|
|
variableStack.length = variableStackHeight;
|
|
return new ForInStatement(variable, iterable, body, isAsync: isAsync)
|
|
..fileOffset = offset
|
|
..bodyOffset = bodyOffset;
|
|
case Tag.SwitchStatement:
|
|
var expression = readExpression();
|
|
int count = readUInt();
|
|
List<SwitchCase> cases =
|
|
new List<SwitchCase>.generate(count, (i) => new SwitchCase.empty());
|
|
switchCaseStack.addAll(cases);
|
|
for (int i = 0; i < cases.length; ++i) {
|
|
var caseNode = cases[i];
|
|
int length = readUInt();
|
|
caseNode.expressions.length = length;
|
|
caseNode.expressionOffsets.length = length;
|
|
for (int i = 0; i < length; ++i) {
|
|
caseNode.expressionOffsets[i] = readOffset();
|
|
caseNode.expressions[i] = readExpression()..parent = caseNode;
|
|
}
|
|
caseNode.isDefault = readByte() == 1;
|
|
caseNode.body = readStatement()..parent = caseNode;
|
|
}
|
|
switchCaseStack.length -= count;
|
|
return new SwitchStatement(expression, cases);
|
|
case Tag.ContinueSwitchStatement:
|
|
int index = readUInt();
|
|
return new ContinueSwitchStatement(switchCaseStack[index]);
|
|
case Tag.IfStatement:
|
|
return new IfStatement(
|
|
readExpression(), readStatement(), readStatementOrNullIfEmpty());
|
|
case Tag.ReturnStatement:
|
|
int offset = readOffset();
|
|
return new ReturnStatement(readExpressionOption())..fileOffset = offset;
|
|
case Tag.TryCatch:
|
|
Statement body = readStatement();
|
|
readByte(); // whether any catch needs a stacktrace.
|
|
return new TryCatch(body, readCatchList());
|
|
case Tag.TryFinally:
|
|
return new TryFinally(readStatement(), readStatement());
|
|
case Tag.YieldStatement:
|
|
int offset = readOffset();
|
|
int flags = readByte();
|
|
return new YieldStatement(readExpression(),
|
|
isYieldStar: flags & YieldStatement.FlagYieldStar != 0,
|
|
isNative: flags & YieldStatement.FlagNative != 0)
|
|
..fileOffset = offset;
|
|
case Tag.VariableDeclaration:
|
|
var variable = readVariableDeclaration();
|
|
variableStack.add(variable); // Will be popped by the enclosing scope.
|
|
return variable;
|
|
case Tag.FunctionDeclaration:
|
|
int offset = readOffset();
|
|
var variable = readVariableDeclaration();
|
|
variableStack.add(variable); // Will be popped by the enclosing scope.
|
|
var function = readFunctionNode();
|
|
return new FunctionDeclaration(variable, function)..fileOffset = offset;
|
|
default:
|
|
throw fail('Invalid statement tag: $tag');
|
|
}
|
|
}
|
|
|
|
List<Catch> readCatchList() {
|
|
return new List<Catch>.generate(readUInt(), (i) => readCatch());
|
|
}
|
|
|
|
Catch readCatch() {
|
|
int variableStackHeight = variableStack.length;
|
|
var guard = readDartType();
|
|
var exception = readAndPushVariableDeclarationOption();
|
|
var stackTrace = readAndPushVariableDeclarationOption();
|
|
var body = readStatement();
|
|
variableStack.length = variableStackHeight;
|
|
return new Catch(exception, body, guard: guard, stackTrace: stackTrace);
|
|
}
|
|
|
|
Block readBlock() {
|
|
int stackHeight = variableStack.length;
|
|
var body = readStatementList();
|
|
variableStack.length = stackHeight;
|
|
return new Block(body);
|
|
}
|
|
|
|
Supertype readSupertype() {
|
|
InterfaceType type = readDartType();
|
|
return new Supertype.byReference(type.className, type.typeArguments);
|
|
}
|
|
|
|
Supertype readSupertypeOption() {
|
|
return readAndCheckOptionTag() ? readSupertype() : null;
|
|
}
|
|
|
|
List<Supertype> readSupertypeList() {
|
|
return new List<Supertype>.generate(readUInt(), (i) => readSupertype());
|
|
}
|
|
|
|
List<DartType> readDartTypeList() {
|
|
return new List<DartType>.generate(readUInt(), (i) => readDartType());
|
|
}
|
|
|
|
List<NamedType> readNamedTypeList() {
|
|
return new List<NamedType>.generate(readUInt(), (i) => readNamedType());
|
|
}
|
|
|
|
NamedType readNamedType() {
|
|
return new NamedType(readStringReference(), readDartType());
|
|
}
|
|
|
|
DartType readDartTypeOption() {
|
|
return readAndCheckOptionTag() ? readDartType() : null;
|
|
}
|
|
|
|
DartType readDartType() {
|
|
int tag = readByte();
|
|
switch (tag) {
|
|
case Tag.TypedefType:
|
|
return new TypedefType.byReference(
|
|
readTypedefReference(), readDartTypeList());
|
|
case Tag.VectorType:
|
|
return const VectorType();
|
|
case Tag.BottomType:
|
|
return const BottomType();
|
|
case Tag.InvalidType:
|
|
return const InvalidType();
|
|
case Tag.DynamicType:
|
|
return const DynamicType();
|
|
case Tag.VoidType:
|
|
return const VoidType();
|
|
case Tag.InterfaceType:
|
|
return new InterfaceType.byReference(
|
|
readClassReference(), readDartTypeList());
|
|
case Tag.SimpleInterfaceType:
|
|
return new InterfaceType.byReference(
|
|
readClassReference(), const <DartType>[]);
|
|
case Tag.FunctionType:
|
|
int typeParameterStackHeight = typeParameterStack.length;
|
|
var typeParameters = readAndPushTypeParameterList();
|
|
var requiredParameterCount = readUInt();
|
|
var totalParameterCount = readUInt();
|
|
var positional = readDartTypeList();
|
|
var named = readNamedTypeList();
|
|
var positionalNames = readStringReferenceList();
|
|
var typedefReference = readTypedefReference();
|
|
assert(positional.length + named.length == totalParameterCount);
|
|
var returnType = readDartType();
|
|
typeParameterStack.length = typeParameterStackHeight;
|
|
return new FunctionType(positional, returnType,
|
|
typeParameters: typeParameters,
|
|
requiredParameterCount: requiredParameterCount,
|
|
namedParameters: named,
|
|
positionalParameterNames: positionalNames,
|
|
typedefReference: typedefReference);
|
|
case Tag.SimpleFunctionType:
|
|
var positional = readDartTypeList();
|
|
var positionalNames = readStringReferenceList();
|
|
var returnType = readDartType();
|
|
return new FunctionType(positional, returnType,
|
|
positionalParameterNames: positionalNames);
|
|
case Tag.TypeParameterType:
|
|
int index = readUInt();
|
|
var bound = readDartTypeOption();
|
|
return new TypeParameterType(typeParameterStack[index], bound);
|
|
default:
|
|
throw fail('Invalid dart type tag: $tag');
|
|
}
|
|
}
|
|
|
|
List<TypeParameter> readAndPushTypeParameterList(
|
|
[List<TypeParameter> list, TreeNode parent]) {
|
|
int length = readUInt();
|
|
if (length == 0) return list ?? <TypeParameter>[];
|
|
if (list == null) {
|
|
list = new List<TypeParameter>.generate(
|
|
length, (i) => new TypeParameter(null, null)..parent = parent);
|
|
} else if (list.length != length) {
|
|
list.length = length;
|
|
for (int i = 0; i < length; ++i) {
|
|
list[i] = new TypeParameter(null, null)..parent = parent;
|
|
}
|
|
}
|
|
typeParameterStack.addAll(list);
|
|
for (int i = 0; i < list.length; ++i) {
|
|
readTypeParameter(list[i]);
|
|
}
|
|
return list;
|
|
}
|
|
|
|
void readTypeParameter(TypeParameter node) {
|
|
node.name = readStringOrNullIfEmpty();
|
|
node.bound = readDartType();
|
|
}
|
|
|
|
Arguments readArguments() {
|
|
var numArguments = readUInt();
|
|
var typeArguments = readDartTypeList();
|
|
var positional = readExpressionList();
|
|
var named = readNamedExpressionList();
|
|
assert(numArguments == positional.length + named.length);
|
|
return new Arguments(positional, types: typeArguments, named: named);
|
|
}
|
|
|
|
List<NamedExpression> readNamedExpressionList() {
|
|
return new List<NamedExpression>.generate(
|
|
readUInt(), (i) => readNamedExpression());
|
|
}
|
|
|
|
NamedExpression readNamedExpression() {
|
|
return new NamedExpression(readStringReference(), readExpression());
|
|
}
|
|
|
|
List<VariableDeclaration> readAndPushVariableDeclarationList() {
|
|
return new List<VariableDeclaration>.generate(
|
|
readUInt(), (i) => readAndPushVariableDeclaration());
|
|
}
|
|
|
|
VariableDeclaration readAndPushVariableDeclarationOption() {
|
|
return readAndCheckOptionTag() ? readAndPushVariableDeclaration() : null;
|
|
}
|
|
|
|
VariableDeclaration readAndPushVariableDeclaration() {
|
|
var variable = readVariableDeclaration();
|
|
variableStack.add(variable);
|
|
return variable;
|
|
}
|
|
|
|
VariableDeclaration readVariableDeclaration() {
|
|
int offset = readOffset();
|
|
int fileEqualsOffset = readOffset();
|
|
int flags = readByte();
|
|
return new VariableDeclaration(readStringOrNullIfEmpty(),
|
|
type: readDartType(),
|
|
initializer: readExpressionOption(),
|
|
isFinal: flags & 0x1 != 0,
|
|
isConst: flags & 0x2 != 0)
|
|
..fileOffset = offset
|
|
..fileEqualsOffset = fileEqualsOffset;
|
|
}
|
|
|
|
int readOffset() {
|
|
// Offset is saved as unsigned,
|
|
// but actually ranges from -1 and up (thus the -1)
|
|
return readUInt() - 1;
|
|
}
|
|
}
|