Files
sdk/pkg/kernel/lib/binary/ast_from_binary.dart
T
Jens Johansen 414d66dce6 More offsets in kernel
Added offsets to more nodes.
Added end offsets to some nodes.
Added functionnode debuggability flag.

This changes the dill format.
The new offsets et al. are read on the C++ side, but not used
for anything usefull yet.

This is step #2 in introducing these things, next step(s) will be
using it on the C++ side.

R=asgerf@google.com

Review-Url: https://codereview.chromium.org/2626613002 .
2017-01-11 09:59:52 +01:00

978 lines
33 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 '../ast.dart';
import 'tag.dart';
import 'loader.dart';
import 'dart:convert';
import 'package:kernel/transformations/flags.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 BinaryReferenceLoader loader;
final List<Library> importTable = <Library>[];
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;
Library _currentLibrary;
List<String> _stringTable;
List<String> _sourceUriTable;
int _transformerFlags = 0;
// If something goes wrong, this list should indicate what library,
// class, and member was being built.
List<String> debugPath = <String>[];
BinaryBuilder(this.loader, 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 readMagicWord() {
return (readByte() << 24) |
(readByte() << 16) |
(readByte() << 8) |
readByte();
}
String readStringEntry() {
int numBytes = readUInt();
// 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() {
int length = readUInt();
_stringTable = new List<String>(length);
for (int i = 0; i < length; ++i) {
_stringTable[i] = readStringEntry();
}
}
String readUriReference() {
return _sourceUriTable[readUInt()];
}
void readSourceUriTable() {
int length = readUInt();
_sourceUriTable = new List<String>(length);
for (int i = 0; i < length; ++i) {
_sourceUriTable[i] = readStringEntry();
}
}
String readStringReference() {
return _stringTable[readUInt()];
}
String readStringOrNullIfEmpty() {
var string = readStringReference();
return string.isEmpty ? null : string;
}
InferredValue readOptionalInferredValue() {
if (readAndCheckOptionTag()) {
Class baseClass = readClassReference(allowNull: true);
BaseClassKind baseClassKind = BaseClassKind.values[readByte()];
int valueBits = readByte();
return new InferredValue(baseClass, baseClassKind, valueBits);
}
return null;
}
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) {
list.length = readUInt();
for (int i = 0; i < list.length; ++i) {
list[i] = buildObject()..parent = parent;
}
}
void _fillNonTreeNodeList(List<Node> list, Node buildObject()) {
list.length = readUInt();
for (int i = 0; i < list.length; ++i) {
list[i] = buildObject();
}
}
Program readProgramFile() {
int magic = readMagicWord();
if (magic != Tag.ProgramFile) {
throw fail('This is not a binary dart file. '
'Magic number was: ${magic.toRadixString(16)}');
}
readStringTable();
Map<String, Source> uriToSource = readUriToSource();
importTable.length = readUInt();
for (int i = 0; i < importTable.length; ++i) {
importTable[i] = new Library(null);
}
for (int i = 0; i < importTable.length; ++i) {
_currentLibrary = importTable[i];
readLibrary();
}
var mainMethod = readMemberReference(allowNull: true);
return new Program(importTable, uriToSource)
..mainMethod = mainMethod;
}
Map<String, Source> readUriToSource() {
readSourceUriTable();
int length = _sourceUriTable.length;
Map<String, Source> uriToLineStarts = <String, Source>{};
for (int i = 0; i < length; ++i) {
String uri = _sourceUriTable[i];
String sourceCode = readStringEntry();
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;
}
uriToLineStarts[uri] = new Source(lineStarts, sourceCode);
}
return uriToLineStarts;
}
void _fillLazilyLoadedList(
List<TreeNode> list, void buildObject(int tag, int index)) {
int length = readUInt();
list.length = length;
for (int i = 0; i < length; ++i) {
int tag = readByte();
buildObject(tag, i);
}
}
Library readLibraryReference() {
int index = readUInt();
return importTable[index];
}
Class readClassReference({bool allowNull: false}) {
int tag = readByte();
if (tag == Tag.NullReference) {
if (!allowNull) {
throw 'Expected a class reference to be valid but was `null`.';
}
return null;
} else {
var library = readLibraryReference();
int index = readUInt();
return loader.getClassReference(library, tag, index);
}
}
Member readMemberReference({bool allowNull: false}) {
int tag = readByte();
switch (tag) {
case Tag.LibraryFieldReference:
case Tag.LibraryProcedureReference:
var library = readLibraryReference();
var index = readUInt();
return loader.getLibraryMemberReference(library, tag, index);
case Tag.ClassFieldReference:
case Tag.ClassConstructorReference:
case Tag.ClassProcedureReference:
var classNode = readClassReference();
var index = readUInt();
return loader.getClassMemberReference(classNode, tag, index);
case Tag.NullReference:
if (!allowNull) {
throw 'Expected a member reference to be valid but was `null`.';
}
return null;
default:
throw fail('Invalid member reference tag: $tag');
}
}
Name readName() {
String text = readStringReference();
if (text.isNotEmpty && text[0] == '_') {
return new Name(text, readLibraryReference());
} else {
return new Name(text);
}
}
Uri readImportUri() {
return Uri.parse(readStringReference());
}
void readLibrary() {
int flags = readByte();
_currentLibrary.isExternal = (flags & 0x1) != 0;
_currentLibrary.name = readStringOrNullIfEmpty();
_currentLibrary.importUri = readImportUri();
debugPath.add(_currentLibrary.name ??
_currentLibrary.importUri?.toString() ??
'library');
// TODO(jensj): We currently save (almost the same) uri twice.
_currentLibrary.fileUri = readUriReference();
_fillLazilyLoadedList(_currentLibrary.classes, (int tag, int index) {
readClass(loader.getClassReference(_currentLibrary, tag, index), tag);
});
_fillLazilyLoadedList(_currentLibrary.fields, (int tag, int index) {
readField(
loader.getLibraryMemberReference(_currentLibrary, tag, index), tag);
});
_fillLazilyLoadedList(_currentLibrary.procedures, (int tag, int index) {
readProcedure(
loader.getLibraryMemberReference(_currentLibrary, tag, index), tag);
});
debugPath.removeLast();
}
void readClass(Class node, int tag) {
assert(node != null);
switch (tag) {
case Tag.NormalClass:
readNormalClass(node);
break;
case Tag.MixinClass:
readMixinClass(node);
break;
default:
throw fail('Invalid class tag: $tag');
}
}
void readNormalClass(Class node) {
node.fileOffset = readOffset();
int flags = readByte();
node.isAbstract = flags & 0x1 != 0;
node.level = _currentLibrary.isExternal
? (flags & 0x2 != 0) ? ClassLevel.Type : ClassLevel.Hierarchy
: ClassLevel.Body;
node.name = readStringOrNullIfEmpty();
node.fileUri = readUriReference();
node.annotations = readAnnotationList(node);
debugPath.add(node.name ?? 'normal-class');
readAndPushTypeParameterList(node.typeParameters, node);
node.supertype = readSupertypeOption();
_fillNonTreeNodeList(node.implementedTypes, readSupertype);
_fillLazilyLoadedList(node.fields, (int tag, int index) {
readField(loader.getClassMemberReference(node, tag, index), tag);
});
_fillLazilyLoadedList(node.constructors, (int tag, int index) {
readConstructor(loader.getClassMemberReference(node, tag, index), tag);
});
_fillLazilyLoadedList(node.procedures, (int tag, int index) {
readProcedure(loader.getClassMemberReference(node, tag, index), tag);
});
typeParameterStack.length = 0;
debugPath.removeLast();
}
void readMixinClass(Class node) {
node.fileOffset = readOffset();
int flags = readByte();
node.isAbstract = flags & 0x1 != 0;
node.level = _currentLibrary.isExternal
? (flags & 0x2 != 0) ? ClassLevel.Type : ClassLevel.Hierarchy
: ClassLevel.Body;
node.name = readStringOrNullIfEmpty();
node.fileUri = readUriReference();
node.annotations = readAnnotationList(node);
debugPath.add(node.name ?? 'mixin-class');
readAndPushTypeParameterList(node.typeParameters, node);
node.supertype = readSupertype();
node.mixedInType = readSupertype();
_fillNonTreeNodeList(node.implementedTypes, readDartType);
_fillLazilyLoadedList(node.constructors, (int tag, int index) {
readConstructor(loader.getClassMemberReference(node, tag, index), tag);
});
typeParameterStack.length = 0;
debugPath.removeLast();
}
int getAndResetTransformerFlags() {
int flags = _transformerFlags;
_transformerFlags = 0;
return flags;
}
/// Adds the given flag to the current [Member.transformerFlags].
void addTransformerFlag(int flags) {
_transformerFlags |= flags;
}
void readField(Field node, int tag) {
// Note: as with readProcedure and readConstructor, the tag parameter
// is unused, but we pass it in to clarify that the tag has already been
// consumed from the input.
assert(tag == Tag.Field);
node.fileOffset = readOffset();
node.fileEndOffset = readOffset();
node.flags = readByte();
node.name = readName();
node.fileUri = readUriReference();
node.annotations = readAnnotationList(node);
debugPath.add(node.name?.name ?? 'field');
node.type = readDartType();
node.inferredValue = readOptionalInferredValue();
node.initializer = readExpressionOption();
node.initializer?.parent = node;
node.transformerFlags = getAndResetTransformerFlags();
debugPath.removeLast();
}
void readConstructor(Constructor node, int tag) {
assert(tag == Tag.Constructor);
node.fileOffset = readOffset();
node.fileEndOffset = readOffset();
node.flags = readByte();
node.name = readName();
node.annotations = readAnnotationList(node);
debugPath.add(node.name?.name ?? 'constructor');
node.function = readFunctionNode()..parent = node;
pushVariableDeclarations(node.function.positionalParameters);
pushVariableDeclarations(node.function.namedParameters);
_fillTreeNodeList(node.initializers, readInitializer, node);
variableStack.length = 0;
node.transformerFlags = getAndResetTransformerFlags();
debugPath.removeLast();
}
void readProcedure(Procedure node, int tag) {
assert(tag == Tag.Procedure);
node.fileOffset = readOffset();
node.fileEndOffset = readOffset();
int kindIndex = readByte();
node.kind = ProcedureKind.values[kindIndex];
node.flags = readByte();
node.name = readName();
node.fileUri = readUriReference();
node.annotations = readAnnotationList(node);
debugPath.add(node.name?.name ?? 'procedure');
node.function = readFunctionNodeOption();
node.function?.parent = node;
node.transformerFlags = getAndResetTransformerFlags();
debugPath.removeLast();
}
Initializer readInitializer() {
int tag = readByte();
switch (tag) {
case Tag.InvalidInitializer:
return new InvalidInitializer();
case Tag.FieldInitializer:
return new FieldInitializer(readMemberReference(), readExpression());
case Tag.SuperInitializer:
return new SuperInitializer(readMemberReference(), readArguments());
case Tag.RedirectingInitializer:
return new RedirectingInitializer(
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 offset = readOffset();
int endOffset = readOffset();
AsyncMarker asyncMarker = AsyncMarker.values[readByte()];
bool debuggable = readByte() == 1 ? true : false;
int typeParameterStackHeight = typeParameterStack.length;
var typeParameters = readAndPushTypeParameterList();
var requiredParameterCount = readUInt();
int variableStackHeight = variableStack.length;
var positional = readAndPushVariableDeclarationList();
var named = readAndPushVariableDeclarationList();
var returnType = readDartType();
var inferredReturnValue = readOptionalInferredValue();
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,
inferredReturnValue: inferredReturnValue,
asyncMarker: asyncMarker)
..fileOffset = offset
..fileEndOffset = endOffset
..debuggable = debuggable;
}
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.InvalidExpression:
return new InvalidExpression();
case Tag.VariableGet:
int offset = readOffset();
return new VariableGet(readVariableReference(), readDartTypeOption())
..fileOffset = offset;
case Tag.SpecializedVariableGet:
int index = tagByte & Tag.SpecializedPayloadMask;
int offset = readOffset();
return new VariableGet(variableStack[index])..fileOffset = offset;
case Tag.VariableSet:
int offset = readOffset();
return new VariableSet(readVariableReference(), readExpression())
..fileOffset = offset;
case Tag.SpecializedVariableSet:
int index = tagByte & Tag.SpecializedPayloadMask;
int offset = readOffset();
return new VariableSet(variableStack[index], readExpression())
..fileOffset = offset;
case Tag.PropertyGet:
int offset = readOffset();
return new PropertyGet(
readExpression(), readName(), readMemberReference(allowNull: true))
..fileOffset = offset;
case Tag.PropertySet:
int offset = readOffset();
return new PropertySet(readExpression(), readName(), readExpression(),
readMemberReference(allowNull: true))..fileOffset = offset;
case Tag.SuperPropertyGet:
addTransformerFlag(TransformerFlag.superCalls);
return new SuperPropertyGet(
readName(), readMemberReference(allowNull: true));
case Tag.SuperPropertySet:
addTransformerFlag(TransformerFlag.superCalls);
return new SuperPropertySet(
readName(), readExpression(), readMemberReference(allowNull: true));
case Tag.DirectPropertyGet:
return new DirectPropertyGet(readExpression(), readMemberReference());
case Tag.DirectPropertySet:
return new DirectPropertySet(
readExpression(), readMemberReference(), readExpression());
case Tag.StaticGet:
int offset = readOffset();
return new StaticGet(readMemberReference())..fileOffset = offset;
case Tag.StaticSet:
return new StaticSet(readMemberReference(), readExpression());
case Tag.MethodInvocation:
int offset = readOffset();
return new MethodInvocation(
readExpression(),
readName(),
readArguments(),
readMemberReference(allowNull: true))..fileOffset = offset;
case Tag.SuperMethodInvocation:
int offset = readOffset();
addTransformerFlag(TransformerFlag.superCalls);
return new SuperMethodInvocation(
readName(), readArguments(), readMemberReference(allowNull: true))
..fileOffset = offset;
case Tag.DirectMethodInvocation:
return new DirectMethodInvocation(
readExpression(), readMemberReference(), readArguments());
case Tag.StaticInvocation:
int offset = readOffset();
return new StaticInvocation(readMemberReference(), readArguments(),
isConst: false)..fileOffset = offset;
case Tag.ConstStaticInvocation:
int offset = readOffset();
return new StaticInvocation(readMemberReference(), readArguments(),
isConst: true)..fileOffset = offset;
case Tag.ConstructorInvocation:
int offset = readOffset();
return new ConstructorInvocation(readMemberReference(), readArguments(),
isConst: false)..fileOffset = offset;
case Tag.ConstConstructorInvocation:
int offset = readOffset();
return new ConstructorInvocation(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:
return new AsExpression(readExpression(), readDartType());
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:
return new Rethrow();
case Tag.Throw:
int offset = readOffset();
return new Throw(readExpression())..fileOffset = offset;
case Tag.ListLiteral:
var typeArgument = readDartType();
return new ListLiteral(readExpressionList(),
typeArgument: typeArgument, isConst: false);
case Tag.ConstListLiteral:
var typeArgument = readDartType();
return new ListLiteral(readExpressionList(),
typeArgument: typeArgument, isConst: true);
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);
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(), 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 index = readUInt();
return new BreakStatement(labelStack[labelStackBase + index]);
case Tag.WhileStatement:
return new WhileStatement(readExpression(), readStatement());
case Tag.DoStatement:
return new DoStatement(readStatement(), readExpression());
case Tag.ForStatement:
int variableStackHeight = variableStack.length;
var variables = readAndPushVariableDeclarationList();
var condition = readExpressionOption();
var updates = readExpressionList();
var body = readStatement();
variableStack.length = variableStackHeight;
return new ForStatement(variables, condition, updates, body);
case Tag.ForInStatement:
case Tag.AsyncForInStatement:
bool isAsync = tag == Tag.AsyncForInStatement;
int variableStackHeight = variableStack.length;
var variable = readAndPushVariableDeclaration();
var iterable = readExpression();
var body = readStatement();
variableStack.length = variableStackHeight;
return new ForInStatement(variable, iterable, body, isAsync: isAsync);
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];
_fillTreeNodeList(caseNode.expressions, readExpression, 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:
return new TryCatch(readStatement(), 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(type.classNode, 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.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(readClassReference(), readDartTypeList());
case Tag.SimpleInterfaceType:
return new InterfaceType(readClassReference(), const <DartType>[]);
case Tag.FunctionType:
int typeParameterStackHeight = typeParameterStack.length;
var typeParameters = readAndPushTypeParameterList();
var requiredParameterCount = readUInt();
var positional = readDartTypeList();
var named = readNamedTypeList();
var returnType = readDartType();
typeParameterStack.length = typeParameterStackHeight;
return new FunctionType(positional, returnType,
typeParameters: typeParameters,
requiredParameterCount: requiredParameterCount,
namedParameters: named);
case Tag.SimpleFunctionType:
var positional = readDartTypeList();
var returnType = readDartType();
return new FunctionType(positional, returnType);
case Tag.TypeParameterType:
int index = readUInt();
return new TypeParameterType(typeParameterStack[index]);
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 {
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 typeArguments = readDartTypeList();
var positional = readExpressionList();
var named = readNamedExpressionList();
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 flags = readByte();
return new VariableDeclaration(readStringOrNullIfEmpty(),
type: readDartType(),
inferredValue: readOptionalInferredValue(),
initializer: readExpressionOption(),
isFinal: flags & 0x1 != 0,
isConst: flags & 0x2 != 0)..fileOffset = offset;
}
int readOffset() {
// Offset is saved as unsigned,
// but actually ranges from -1 and up (thus the -1)
return readUInt() - 1;
}
}