blob: c35b0121e16208d8a6a5aabc6c3b616d96ce240c [file] [log] [blame]
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library kernel.ast_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 _ProgramIndex {
int binaryOffsetForSourceTable;
int binaryOffsetForStringTable;
int binaryOffsetForCanonicalNames;
int mainMethodReference;
List<int> libraryOffsets;
int libraryCount;
int programFileSizeInBytes;
}
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 _byteOffset = 0;
final List<String> _stringTable = <String>[];
final List<String> _sourceUriTable = <String>[];
List<CanonicalName> _linkTable;
int _transformerFlags = 0;
Library _currentLibrary;
int _programStartOffset = 0;
// 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: _byteOffset, filename: filename, path: debugPath.join('::'));
}
int readByte() => _bytes[_byteOffset++];
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, _byteOffset);
_byteOffset += 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 (_byteOffset + 2 < _bytes.length &&
_bytes[_byteOffset] == 0xef &&
_bytes[_byteOffset + 1] == 0xbb &&
_bytes[_byteOffset + 2] == 0xbf) {
++numByteOrderMarks;
_byteOffset += 3;
numBytes -= 3;
}
String string = const Utf8Decoder()
.convert(_bytes, _byteOffset, _byteOffset + numBytes);
_byteOffset += 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(int index), TreeNode parent) {
var length = readUInt();
list.length = length;
for (int i = 0; i < length; ++i) {
TreeNode object = buildObject(i);
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(int index), 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(i);
// 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);
}
}
List<int> _indexPrograms() {
int savedByteOffset = _byteOffset;
_byteOffset = _bytes.length - 4;
List<int> index = <int>[];
while (_byteOffset > 0) {
int size = readUint32();
int start = _byteOffset - size;
if (start < 0) throw "Invalid program file: Indicated size is invalid.";
index.add(size);
_byteOffset = start - 4;
}
_byteOffset = savedByteOffset;
return new List.from(index.reversed);
}
/// 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) {
List<int> programFileSizes = _indexPrograms();
int programFileIndex = 0;
while (_byteOffset < _bytes.length) {
_readOneProgram(program, programFileSizes[programFileIndex]);
++programFileIndex;
}
}
/// 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) {
List<int> programFileSizes = _indexPrograms();
if (programFileSizes.isEmpty) throw "Invalid program data.";
_readOneProgram(program, programFileSizes[0]);
if (_byteOffset < _bytes.length) {
if (_byteOffset + 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';
}
}
_ProgramIndex _readProgramIndex(int programFileSize) {
int savedByteIndex = _byteOffset;
_ProgramIndex result = new _ProgramIndex();
// There are two fields: file size and library count.
_byteOffset = _programStartOffset + programFileSize - (2) * 4;
result.libraryCount = readUint32();
// Library offsets are used for start and end offsets, so there is one extra
// element that this the end offset of the last library
result.libraryOffsets = new List<int>(result.libraryCount + 1);
result.programFileSizeInBytes = readUint32();
if (result.programFileSizeInBytes != programFileSize) {
throw 'Malformed binary: This program files program index indicates that'
' the filesize should be $programFileSize but other program indexes'
' has indicated that the size should be '
'${result.programFileSizeInBytes}.';
}
// Skip to the start of the index.
// There are these fields: file size, library count, library count + 1
// offsets, main reference, string table offset, canonical name offset and
// source table offset. That's 6 fields + number of libraries.
_byteOffset -= (result.libraryCount + 7) * 4;
// Now read the program index.
result.binaryOffsetForSourceTable = _programStartOffset + readUint32();
result.binaryOffsetForCanonicalNames = _programStartOffset + readUint32();
result.binaryOffsetForStringTable = _programStartOffset + readUint32();
result.mainMethodReference = readUint32();
for (int i = 0; i < result.libraryCount + 1; ++i) {
result.libraryOffsets[i] = _programStartOffset + readUint32();
}
_byteOffset = savedByteIndex;
return result;
}
void _readOneProgram(Program program, int programFileSize) {
_programStartOffset = _byteOffset;
int magic = readUint32();
if (magic != Tag.ProgramFile) {
throw fail('This is not a binary dart file. '
'Magic number was: ${magic.toRadixString(16)}');
}
// Read program index from the end of this ProgramFiles serialized data.
_ProgramIndex index = _readProgramIndex(programFileSize);
_byteOffset = index.binaryOffsetForStringTable;
readStringTable(_stringTable);
_byteOffset = index.binaryOffsetForCanonicalNames;
readLinkTable(program.root);
_byteOffset = index.binaryOffsetForSourceTable;
Map<String, Source> uriToSource = readUriToSource();
program.uriToSource.addAll(uriToSource);
int numberOfLibraries = index.libraryCount;
List<Library> libraries = new List<Library>(numberOfLibraries);
for (int i = 0; i < numberOfLibraries; ++i) {
_byteOffset = index.libraryOffsets[i];
libraries[i] = readLibrary(program, index.libraryOffsets[i + 1]);
}
var mainMethod =
getMemberReferenceFromInt(index.mainMethodReference, allowNull: true);
program.mainMethodName ??= mainMethod;
_byteOffset = _programStartOffset + programFileSize;
}
Map<String, Source> readUriToSource() {
int length = readUint32();
// Read data.
_sourceUriTable.length = length;
Map<String, Source> uriToSource = <String, Source>{};
for (int i = 0; i < length; ++i) {
List<int> uriBytes = readUtf8Bytes();
String uri = const Utf8Decoder().convert(uriBytes);
_sourceUriTable[i] = uri;
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);
}
// Read index.
for (int i = 0; i < length; ++i) {
readUint32();
}
return uriToSource;
}
CanonicalName readCanonicalNameReference() {
var index = readUInt();
if (index == 0) return null;
return _linkTable[index - 1];
}
CanonicalName getCanonicalNameReferenceFromInt(int index) {
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 getMemberReferenceFromInt(int index, {bool allowNull: false}) {
var name = getCanonicalNameReferenceFromInt(index);
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 endOffset) {
// Read index.
int savedByteOffset = _byteOffset;
// There is a field for the procedure count.
_byteOffset = endOffset - (1) * 4;
int procedureCount = readUint32();
List<int> procedureOffsets = new List<int>(procedureCount + 1);
// There is a field for the procedure count, that number + 1 (for the end)
// offsets, and then the class count (i.e. procedure count + 3 fields).
_byteOffset = endOffset - (procedureCount + 3) * 4;
int classCount = readUint32();
for (int i = 0; i < procedureCount + 1; i++) {
procedureOffsets[i] = _programStartOffset + readUint32();
}
List<int> classOffsets = new List<int>(classCount + 1);
// There is a field for the procedure count, that number + 1 (for the end)
// offsets, then the class count and that number + 1 (for the end) offsets.
// (i.e. procedure count + class count + 4 fields).
_byteOffset = endOffset - (procedureCount + classCount + 4) * 4;
for (int i = 0; i < classCount + 1; i++) {
classOffsets[i] = _programStartOffset + readUint32();
}
_byteOffset = savedByteOffset;
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();
String documentationComment = readStringOrNullIfEmpty();
// TODO(jensj): We currently save (almost the same) uri twice.
String fileUri = readUriReference();
if (shouldWriteData) {
library.isExternal = isExternal;
library.name = name;
library.documentationComment = documentationComment;
library.fileUri = fileUri;
}
debugPath.add(library.name ?? library.importUri?.toString() ?? 'library');
if (shouldWriteData) {
_fillTreeNodeList(
library.annotations, (index) => readExpression(), library);
} else {
_skipNodeList(readExpression);
}
_readLibraryDependencies(library);
_readAdditionalExports(library);
_readLibraryParts(library);
_mergeNamedNodeList(library.typedefs, (index) => readTypedef(), library);
_mergeNamedNodeList(library.classes, (index) {
_byteOffset = classOffsets[index];
return readClass(classOffsets[index + 1]);
}, library);
_byteOffset = classOffsets.last;
_mergeNamedNodeList(library.fields, (index) => readField(), library);
_mergeNamedNodeList(library.procedures, (index) {
_byteOffset = procedureOffsets[index];
return readProcedure(procedureOffsets[index + 1]);
}, library);
_byteOffset = procedureOffsets.last;
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 fileOffset = readOffset();
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)
..fileOffset = fileOffset
..parent = library;
}
}
void _readAdditionalExports(Library library) {
int numExportedReference = readUInt();
if (numExportedReference != 0) {
for (int i = 0; i < numExportedReference; i++) {
CanonicalName exportedName = readCanonicalNameReference();
Reference reference = exportedName.getReference();
library.additionalExports.add(reference);
}
}
}
Combinator readCombinator() {
var isShow = readByte() == 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();
node.annotations = readAnnotationList(node);
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 endOffset) {
int tag = readByte();
assert(tag == Tag.Class);
// Read index.
int savedByteOffset = _byteOffset;
// There is a field for the procedure count.
_byteOffset = endOffset - (1) * 4;
int procedureCount = readUint32();
List<int> procedureOffsets = new List<int>(procedureCount + 1);
// There is a field for the procedure count, that number + 1 (for the end)
// offsets (i.e. procedure count + 2 fields).
_byteOffset = endOffset - (procedureCount + 2) * 4;
for (int i = 0; i < procedureCount + 1; i++) {
procedureOffsets[i] = _programStartOffset + readUint32();
}
_byteOffset = savedByteOffset;
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, (index) => readField(), node);
_mergeNamedNodeList(node.constructors, (index) => readConstructor(), node);
_mergeNamedNodeList(node.procedures, (index) {
_byteOffset = procedureOffsets[index];
return readProcedure(procedureOffsets[index + 1]);
}, node);
_byteOffset = procedureOffsets.last;
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;
}
_byteOffset = endOffset;
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();
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();
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, (index) => 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 endOffset) {
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();
var name = readName();
var fileUri = readUriReference();
var documentationComment = readStringOrNullIfEmpty();
var annotations = readAnnotationList(node);
debugPath.add(node.name?.name ?? 'procedure');
int functionNodeSize = endOffset - _byteOffset;
// Read small factories up front. Postpone everything else.
bool readFunctionNodeNow =
kind == ProcedureKind.Factory && functionNodeSize <= 50;
var function;
var transformerFlags;
if (readFunctionNodeNow) {
function = readFunctionNodeOption();
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;
if (readFunctionNodeNow) {
node.function = function;
function?.parent = node;
node.transformerFlags = transformerFlags;
} else {
int offset = _byteOffset;
int programStartOffset = _programStartOffset;
List<TypeParameter> typeParameters = typeParameterStack.toList();
node.lazyBuilder = () {
_byteOffset = offset;
typeParameterStack.clear();
typeParameterStack.addAll(typeParameters);
_programStartOffset = programStartOffset;
FunctionNode functionNode = readFunctionNodeOption();
node.function = functionNode;
functionNode?.parent = node;
node.transformerFlags = getAndResetTransformerFlags();
};
}
}
_byteOffset = endOffset;
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();
int flags = readByte();
return new PropertyGet.byReference(
readExpression(), readName(), readMemberReference(allowNull: true))
..fileOffset = offset
..flags = flags;
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();
int flags = readByte();
return new DirectPropertyGet.byReference(
readExpression(), readMemberReference())
..fileOffset = offset
..flags = flags;
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();
int flags = readByte();
return new MethodInvocation.byReference(readExpression(), readName(),
readArguments(), readMemberReference(allowNull: true))
..fileOffset = offset
..flags = flags;
case Tag.SuperMethodInvocation:
int offset = readOffset();
addTransformerFlag(TransformerFlag.superCalls);
return new SuperMethodInvocation.byReference(
readName(), readArguments(), readMemberReference(allowNull: true))
..fileOffset = offset;
case Tag.DirectMethodInvocation:
int flags = readByte();
return new DirectMethodInvocation.byReference(
readExpression(), readMemberReference(), readArguments())
..flags = flags;
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:
int offset = readOffset();
return new FunctionExpression(readFunctionNode())..fileOffset = offset;
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 offset = readOffset();
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)..fileOffset = offset;
case Tag.ContinueSwitchStatement:
int offset = readOffset();
int index = readUInt();
return new ContinueSwitchStatement(switchCaseStack[index])
..fileOffset = offset;
case Tag.IfStatement:
int offset = readOffset();
return new IfStatement(
readExpression(), readStatement(), readStatementOrNullIfEmpty())
..fileOffset = offset;
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.flags = readByte();
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(), flags: flags)
..fileOffset = offset
..fileEqualsOffset = fileEqualsOffset;
}
int readOffset() {
// Offset is saved as unsigned,
// but actually ranges from -1 and up (thus the -1)
return readUInt() - 1;
}
}