// 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 importTable = []; final List variableStack = []; final List labelStack = []; int labelStackBase = 0; final List switchCaseStack = []; final List typeParameterStack = []; final String filename; final List _bytes; int _byteIndex = 0; Library _currentLibrary; List _stringTable; List _sourceUriTable; int _transformerFlags = 0; // If something goes wrong, this list should indicate what library, // class, and member was being built. List debugPath = []; 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(length); for (int i = 0; i < length; ++i) { _stringTable[i] = readStringEntry(); } } String readUriReference() { return _sourceUriTable[readUInt()]; } void readSourceUriTable() { int length = readUInt(); _sourceUriTable = new List(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 readAnnotationList(TreeNode parent) { int length = readUInt(); if (length == 0) return const []; List list = new List(length); for (int i = 0; i < length; ++i) { list[i] = readExpression()..parent = parent; } return list; } void _fillTreeNodeList( List list, TreeNode buildObject(), TreeNode parent) { list.length = readUInt(); for (int i = 0; i < list.length; ++i) { list[i] = buildObject()..parent = parent; } } void _fillNonTreeNodeList(List 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 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 readUriToSource() { readSourceUriTable(); int length = _sourceUriTable.length; Map uriToLineStarts = {}; for (int i = 0; i < length; ++i) { String uri = _sourceUriTable[i]; String sourceCode = readStringEntry(); int lineCount = readUInt(); List lineStarts = new List(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 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 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 readExpressionList() { return new List.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 readMapEntryList() { return new List.generate(readUInt(), (i) => readMapEntry()); } MapEntry readMapEntry() { return new MapEntry(readExpression(), readExpression()); } List readStatementList() { return new List.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 cases = new List.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 readCatchList() { return new List.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 readSupertypeList() { return new List.generate(readUInt(), (i) => readSupertype()); } List readDartTypeList() { return new List.generate(readUInt(), (i) => readDartType()); } List readNamedTypeList() { return new List.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 []); 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 readAndPushTypeParameterList( [List list, TreeNode parent]) { int length = readUInt(); if (length == 0) return list ?? []; if (list == null) { list = new List.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 readNamedExpressionList() { return new List.generate( readUInt(), (i) => readNamedExpression()); } NamedExpression readNamedExpression() { return new NamedExpression(readStringReference(), readExpression()); } List readAndPushVariableDeclarationList() { return new List.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; } }