a117d3f8f7
This is to distinguish constant calls to an external constant factory. This changes the binary format.
1142 lines
30 KiB
Dart
1142 lines
30 KiB
Dart
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
library kernel.ast_to_binary;
|
|
|
|
import '../ast.dart';
|
|
import '../import_table.dart';
|
|
import 'tag.dart';
|
|
import 'dart:io';
|
|
import 'dart:convert';
|
|
import 'dart:typed_data';
|
|
import 'dart:collection';
|
|
|
|
/// Writes to a binary file.
|
|
///
|
|
/// A [BinaryPrinter] can be used to write one file and must then be
|
|
/// discarded.
|
|
class BinaryPrinter extends Visitor {
|
|
ImportTable _importTable;
|
|
|
|
// TODO: We can do the indexing on-the-fly, but for now just keep it simple.
|
|
VariableIndexer _variableIndexer;
|
|
LabelIndexer _labelIndexer;
|
|
SwitchCaseIndexer _switchCaseIndexer;
|
|
final TypeParameterIndexer _typeParameterIndexer = new TypeParameterIndexer();
|
|
final GlobalIndexer _globalIndexer;
|
|
final StringIndexer _stringIndexer = new StringIndexer();
|
|
|
|
final BufferedSink _sink;
|
|
|
|
/// Create a printer that writes to the given [sink].
|
|
///
|
|
/// The BinaryPrinter will use its own buffer, so the [sink] does not need
|
|
/// one.
|
|
///
|
|
/// If multiple binaries are to be written based on the same IR, a shared
|
|
/// [globalIndexer] may be passed in to avoid rebuilding the same indices
|
|
/// in every printer.
|
|
BinaryPrinter(IOSink sink, {GlobalIndexer globalIndexer})
|
|
: _sink = new BufferedSink(sink),
|
|
_globalIndexer = globalIndexer ?? new GlobalIndexer();
|
|
|
|
void _flush() {
|
|
_sink.flushAndDestroy();
|
|
}
|
|
|
|
void writeByte(int byte) {
|
|
_sink.addByte(byte);
|
|
}
|
|
|
|
void writeBytes(List<int> bytes) {
|
|
_sink.addBytes(bytes);
|
|
}
|
|
|
|
void writeUInt30(int value) {
|
|
assert(value >= 0 && value >> 30 == 0);
|
|
if (value < 0x80) {
|
|
writeByte(value);
|
|
} else if (value < 0x4000) {
|
|
writeByte((value >> 8) | 0x80);
|
|
writeByte(value & 0xFF);
|
|
} else {
|
|
writeByte((value >> 24) | 0xC0);
|
|
writeByte((value >> 16) & 0xFF);
|
|
writeByte((value >> 8) & 0xFF);
|
|
writeByte(value & 0xFF);
|
|
}
|
|
}
|
|
|
|
void writeMagicWord(int value) {
|
|
writeByte((value >> 24) & 0xFF);
|
|
writeByte((value >> 16) & 0xFF);
|
|
writeByte((value >> 8) & 0xFF);
|
|
writeByte(value & 0xFF);
|
|
}
|
|
|
|
void writeStringTableEntry(String string) {
|
|
List<int> utf8Bytes = const Utf8Encoder().convert(string);
|
|
writeUInt30(utf8Bytes.length);
|
|
writeBytes(utf8Bytes);
|
|
}
|
|
|
|
void writeStringTable(StringIndexer indexer) {
|
|
writeUInt30(indexer.numberOfStrings);
|
|
for (var entry in indexer.entries) {
|
|
writeStringTableEntry(entry.value);
|
|
}
|
|
}
|
|
|
|
void writeStringReference(String string) {
|
|
writeUInt30(_stringIndexer[string]);
|
|
}
|
|
|
|
void writeList(List items, writeItem(x)) {
|
|
writeUInt30(items.length);
|
|
items.forEach(writeItem);
|
|
}
|
|
|
|
void writeNodeList(List<Node> nodes) {
|
|
writeList(nodes, writeNode);
|
|
}
|
|
|
|
void writeNode(Node node) {
|
|
node.accept(this);
|
|
}
|
|
|
|
void writeOptionalNode(Node node) {
|
|
if (node == null) {
|
|
writeByte(Tag.Nothing);
|
|
} else {
|
|
writeByte(Tag.Something);
|
|
writeNode(node);
|
|
}
|
|
}
|
|
|
|
void writeLibraryFile(Library library) {
|
|
writeMagicWord(Tag.LibraryFile);
|
|
_importTable = new LibraryImportTable(library);
|
|
_stringIndexer.addLibraryImports(_importTable);
|
|
_stringIndexer.build(library);
|
|
writeStringTable(_stringIndexer);
|
|
writeLibraryImportTable(_importTable);
|
|
writeNode(library);
|
|
_flush();
|
|
}
|
|
|
|
void writeProgramFile(Program program) {
|
|
writeMagicWord(Tag.ProgramFile);
|
|
_importTable = new ProgramImportTable(program);
|
|
_stringIndexer.build(program);
|
|
writeStringTable(_stringIndexer);
|
|
writeList(program.libraries, writeNode);
|
|
if (program.mainMethod == null) {
|
|
throw 'Cannot emit program without a main method';
|
|
}
|
|
writeMemberReference(program.mainMethod);
|
|
_flush();
|
|
}
|
|
|
|
void writeLibraryImportTable(LibraryImportTable imports) {
|
|
writeList(imports.importPaths, writeStringReference);
|
|
}
|
|
|
|
void writeLibraryReference(Library node) {
|
|
int index = _importTable.getImportIndex(node);
|
|
if (index == -1) {
|
|
throw 'Missing import for library: ${node.importUri}';
|
|
}
|
|
writeUInt30(index);
|
|
}
|
|
|
|
void writeClassIndex(Class node) {
|
|
writeUInt30(_globalIndexer[node]);
|
|
}
|
|
|
|
void writeClassReference(Class node) {
|
|
if (node == null) {
|
|
throw 'I found an unresolved class reference. '
|
|
'The binary format does not support these yet.';
|
|
}
|
|
node.acceptReference(this);
|
|
}
|
|
|
|
void writeMemberReference(Member node) {
|
|
if (node == null) {
|
|
throw 'I found an unresolved member reference. '
|
|
'The binary format does not support these yet.';
|
|
}
|
|
node.acceptReference(this);
|
|
}
|
|
|
|
void visitNormalClassReference(NormalClass node) {
|
|
var library = node.enclosingLibrary;
|
|
writeByte(Tag.NormalClassReference);
|
|
writeLibraryReference(library);
|
|
writeClassIndex(node);
|
|
}
|
|
|
|
void visitMixinClassReference(MixinClass node) {
|
|
var library = node.enclosingLibrary;
|
|
writeByte(Tag.MixinClassReference);
|
|
writeLibraryReference(library);
|
|
writeClassIndex(node);
|
|
}
|
|
|
|
void visitFieldReference(Field node) {
|
|
if (node.enclosingClass != null) {
|
|
writeByte(Tag.ClassFieldReference);
|
|
NormalClass classNode = node.enclosingClass;
|
|
writeClassReference(classNode);
|
|
writeUInt30(_globalIndexer[node]);
|
|
} else {
|
|
writeByte(Tag.LibraryFieldReference);
|
|
writeLibraryReference(node.enclosingLibrary);
|
|
writeUInt30(_globalIndexer[node]);
|
|
}
|
|
}
|
|
|
|
void visitConstructorReference(Constructor node) {
|
|
writeByte(Tag.ClassConstructorReference);
|
|
writeClassReference(node.enclosingClass);
|
|
writeUInt30(_globalIndexer[node]);
|
|
}
|
|
|
|
void visitProcedureReference(Procedure node) {
|
|
if (node.enclosingClass != null) {
|
|
writeByte(Tag.ClassProcedureReference);
|
|
NormalClass classNode = node.enclosingClass;
|
|
writeClassReference(classNode);
|
|
writeUInt30(_globalIndexer[node]);
|
|
} else {
|
|
writeByte(Tag.LibraryProcedureReference);
|
|
writeLibraryReference(node.enclosingLibrary);
|
|
writeUInt30(_globalIndexer[node]);
|
|
}
|
|
}
|
|
|
|
void writeName(Name node) {
|
|
writeStringReference(node.name);
|
|
// TODO: Consider a more compressed format for private names within the
|
|
// enclosing library.
|
|
if (node.isPrivate) {
|
|
writeLibraryReference(node.library);
|
|
}
|
|
}
|
|
|
|
visitLibrary(Library node) {
|
|
writeStringReference(node.name ?? '');
|
|
writeStringReference('${node.importUri}');
|
|
writeNodeList(node.classes);
|
|
writeNodeList(node.fields);
|
|
writeNodeList(node.procedures);
|
|
}
|
|
|
|
visitNormalClass(NormalClass node) {
|
|
writeByte(Tag.NormalClass);
|
|
writeByte(node.isAbstract ? 1 : 0);
|
|
writeStringReference(node.name ?? '');
|
|
_typeParameterIndexer.push(node.typeParameters);
|
|
writeNodeList(node.typeParameters);
|
|
writeOptionalNode(node.superType);
|
|
writeNodeList(node.implementedTypes);
|
|
writeNodeList(node.fields);
|
|
writeNodeList(node.constructors);
|
|
writeNodeList(node.procedures);
|
|
_typeParameterIndexer.pop(node.typeParameters);
|
|
}
|
|
|
|
visitMixinClass(MixinClass node) {
|
|
writeByte(Tag.MixinClass);
|
|
writeByte(node.isAbstract ? 1 : 0);
|
|
writeStringReference(node.name ?? '');
|
|
_typeParameterIndexer.push(node.typeParameters);
|
|
writeNodeList(node.typeParameters);
|
|
writeNode(node.superType);
|
|
writeNode(node.mixedInType);
|
|
writeNodeList(node.implementedTypes);
|
|
writeNodeList(node.constructors);
|
|
_typeParameterIndexer.pop(node.typeParameters);
|
|
}
|
|
|
|
visitConstructor(Constructor node) {
|
|
_variableIndexer = new VariableIndexer()..build(node);
|
|
writeByte(Tag.Constructor);
|
|
writeByte(node.flags);
|
|
writeName(node.name ?? '');
|
|
assert(node.function.typeParameters.isEmpty);
|
|
writeNode(node.function);
|
|
writeNodeList(node.initializers);
|
|
}
|
|
|
|
visitProcedure(Procedure node) {
|
|
_variableIndexer = new VariableIndexer()..build(node);
|
|
writeByte(Tag.Procedure);
|
|
writeByte(node.kind.index);
|
|
writeByte(node.flags);
|
|
writeName(node.name ?? '');
|
|
writeOptionalNode(node.function);
|
|
}
|
|
|
|
visitField(Field node) {
|
|
_variableIndexer = new VariableIndexer()..build(node);
|
|
writeByte(Tag.Field);
|
|
writeByte(node.flags);
|
|
writeName(node.name ?? '');
|
|
writeNode(node.type);
|
|
writeOptionalNode(node.initializer);
|
|
}
|
|
|
|
visitInvalidInitializer(InvalidInitializer node) {
|
|
writeByte(Tag.InvalidInitializer);
|
|
}
|
|
|
|
visitFieldInitializer(FieldInitializer node) {
|
|
writeByte(Tag.FieldInitializer);
|
|
writeMemberReference(node.field);
|
|
writeNode(node.value);
|
|
}
|
|
|
|
visitSuperInitializer(SuperInitializer node) {
|
|
writeByte(Tag.SuperInitializer);
|
|
writeMemberReference(node.target);
|
|
writeNode(node.arguments);
|
|
}
|
|
|
|
visitRedirectingInitializer(RedirectingInitializer node) {
|
|
writeByte(Tag.RedirectingInitializer);
|
|
writeMemberReference(node.target);
|
|
writeNode(node.arguments);
|
|
}
|
|
|
|
visitFunctionNode(FunctionNode node) {
|
|
assert(_variableIndexer != null);
|
|
var oldLabels = _labelIndexer;
|
|
_labelIndexer = new LabelIndexer()..build(node);
|
|
var oldCases = _switchCaseIndexer;
|
|
_switchCaseIndexer = new SwitchCaseIndexer()..build(node);
|
|
// Note: FunctionNode has no tag.
|
|
_typeParameterIndexer.push(node.typeParameters);
|
|
writeByte(node.asyncMarker.index);
|
|
writeNodeList(node.typeParameters);
|
|
writeUInt30(node.requiredParameterCount);
|
|
writeVariableDeclarationList(node.positionalParameters);
|
|
writeVariableDeclarationList(node.namedParameters);
|
|
writeOptionalNode(node.returnType);
|
|
writeOptionalNode(node.body);
|
|
_labelIndexer = oldLabels;
|
|
_switchCaseIndexer = oldCases;
|
|
_typeParameterIndexer.pop(node.typeParameters);
|
|
}
|
|
|
|
visitInvalidExpression(InvalidExpression node) {
|
|
writeByte(Tag.InvalidExpression);
|
|
}
|
|
|
|
visitVariableGet(VariableGet node) {
|
|
assert(_variableIndexer != null);
|
|
int index = _variableIndexer[node.variable];
|
|
if (index & Tag.SpecializedPayloadMask == index) {
|
|
writeByte(Tag.SpecializedVariableGet + index);
|
|
} else {
|
|
writeByte(Tag.VariableGet);
|
|
writeUInt30(_variableIndexer[node.variable]);
|
|
}
|
|
}
|
|
|
|
visitVariableSet(VariableSet node) {
|
|
assert(_variableIndexer != null);
|
|
int index = _variableIndexer[node.variable];
|
|
if (index & Tag.SpecializedPayloadMask == index) {
|
|
writeByte(Tag.SpecializedVariableSet + index);
|
|
writeNode(node.value);
|
|
} else {
|
|
writeByte(Tag.VariableSet);
|
|
writeUInt30(_variableIndexer[node.variable]);
|
|
writeNode(node.value);
|
|
}
|
|
}
|
|
|
|
visitPropertyGet(PropertyGet node) {
|
|
writeByte(Tag.PropertyGet);
|
|
writeNode(node.receiver);
|
|
writeName(node.name);
|
|
}
|
|
|
|
visitPropertySet(PropertySet node) {
|
|
writeByte(Tag.PropertySet);
|
|
writeNode(node.receiver);
|
|
writeName(node.name);
|
|
writeNode(node.value);
|
|
}
|
|
|
|
visitSuperPropertyGet(SuperPropertyGet node) {
|
|
writeByte(Tag.SuperPropertyGet);
|
|
writeMemberReference(node.target);
|
|
}
|
|
|
|
visitSuperPropertySet(SuperPropertySet node) {
|
|
writeByte(Tag.SuperPropertySet);
|
|
writeMemberReference(node.target);
|
|
writeNode(node.value);
|
|
}
|
|
|
|
visitStaticGet(StaticGet node) {
|
|
writeByte(Tag.StaticGet);
|
|
writeMemberReference(node.target);
|
|
}
|
|
|
|
visitStaticSet(StaticSet node) {
|
|
writeByte(Tag.StaticSet);
|
|
writeMemberReference(node.target);
|
|
writeNode(node.value);
|
|
}
|
|
|
|
visitMethodInvocation(MethodInvocation node) {
|
|
writeByte(Tag.MethodInvocation);
|
|
writeNode(node.receiver);
|
|
writeName(node.name);
|
|
writeNode(node.arguments);
|
|
}
|
|
|
|
visitSuperMethodInvocation(SuperMethodInvocation node) {
|
|
writeByte(Tag.SuperMethodInvocation);
|
|
writeMemberReference(node.target);
|
|
writeNode(node.arguments);
|
|
}
|
|
|
|
visitStaticInvocation(StaticInvocation node) {
|
|
writeByte(node.isConst ? Tag.ConstStaticInvocation : Tag.StaticInvocation);
|
|
writeMemberReference(node.target);
|
|
writeNode(node.arguments);
|
|
}
|
|
|
|
visitConstructorInvocation(ConstructorInvocation node) {
|
|
writeByte(node.isConst
|
|
? Tag.ConstConstructorInvocation
|
|
: Tag.ConstructorInvocation);
|
|
writeMemberReference(node.target);
|
|
writeNode(node.arguments);
|
|
}
|
|
|
|
visitArguments(Arguments node) {
|
|
writeNodeList(node.types);
|
|
writeNodeList(node.positional);
|
|
writeNodeList(node.named);
|
|
}
|
|
|
|
visitNamedExpression(NamedExpression node) {
|
|
writeStringReference(node.name);
|
|
writeNode(node.value);
|
|
}
|
|
|
|
visitNot(Not node) {
|
|
writeByte(Tag.Not);
|
|
writeNode(node.operand);
|
|
}
|
|
|
|
int logicalOperatorIndex(String operator) {
|
|
switch (operator) {
|
|
case '&&':
|
|
return 0;
|
|
case '||':
|
|
return 1;
|
|
case '??':
|
|
return 2;
|
|
}
|
|
throw 'Not a logical operator: $operator';
|
|
}
|
|
|
|
visitLogicalExpression(LogicalExpression node) {
|
|
writeByte(Tag.LogicalExpression);
|
|
writeNode(node.left);
|
|
writeByte(logicalOperatorIndex(node.operator));
|
|
writeNode(node.right);
|
|
}
|
|
|
|
visitConditionalExpression(ConditionalExpression node) {
|
|
writeByte(Tag.ConditionalExpression);
|
|
writeNode(node.condition);
|
|
writeNode(node.then);
|
|
writeNode(node.otherwise);
|
|
}
|
|
|
|
visitStringConcatenation(StringConcatenation node) {
|
|
writeByte(Tag.StringConcatenation);
|
|
writeNodeList(node.expressions);
|
|
}
|
|
|
|
visitIsExpression(IsExpression node) {
|
|
writeByte(Tag.IsExpression);
|
|
writeNode(node.operand);
|
|
writeNode(node.type);
|
|
}
|
|
|
|
visitAsExpression(AsExpression node) {
|
|
writeByte(Tag.AsExpression);
|
|
writeNode(node.operand);
|
|
writeNode(node.type);
|
|
}
|
|
|
|
visitStringLiteral(StringLiteral node) {
|
|
writeByte(Tag.StringLiteral);
|
|
writeStringReference(node.value);
|
|
}
|
|
|
|
visitIntLiteral(IntLiteral node) {
|
|
int value = node.value;
|
|
int biasedValue = value + Tag.SpecializedIntLiteralBias;
|
|
if (biasedValue >= 0 &&
|
|
biasedValue & Tag.SpecializedPayloadMask == biasedValue) {
|
|
writeByte(Tag.SpecializedIntLiteral + biasedValue);
|
|
} else if (value.abs() >> 30 == 0) {
|
|
if (value < 0) {
|
|
writeByte(Tag.NegativeIntLiteral);
|
|
writeUInt30(-value);
|
|
} else {
|
|
writeByte(Tag.PositiveIntLiteral);
|
|
writeUInt30(value);
|
|
}
|
|
} else {
|
|
// TODO: Pick a better format for big int literals.
|
|
writeByte(Tag.BigIntLiteral);
|
|
writeStringReference('${node.value}');
|
|
}
|
|
}
|
|
|
|
visitDoubleLiteral(DoubleLiteral node) {
|
|
// TODO: Pick a better format for double literals.
|
|
writeByte(Tag.DoubleLiteral);
|
|
writeStringReference('${node.value}');
|
|
}
|
|
|
|
visitBoolLiteral(BoolLiteral node) {
|
|
writeByte(node.value ? Tag.TrueLiteral : Tag.FalseLiteral);
|
|
}
|
|
|
|
visitNullLiteral(NullLiteral node) {
|
|
writeByte(Tag.NullLiteral);
|
|
}
|
|
|
|
visitSymbolLiteral(SymbolLiteral node) {
|
|
writeByte(Tag.SymbolLiteral);
|
|
writeStringReference(node.value);
|
|
}
|
|
|
|
visitTypeLiteral(TypeLiteral node) {
|
|
writeByte(Tag.TypeLiteral);
|
|
writeNode(node.type);
|
|
}
|
|
|
|
visitThisExpression(ThisExpression node) {
|
|
writeByte(Tag.ThisExpression);
|
|
}
|
|
|
|
visitRethrow(Rethrow node) {
|
|
writeByte(Tag.Rethrow);
|
|
}
|
|
|
|
visitThrow(Throw node) {
|
|
writeByte(Tag.Throw);
|
|
writeNode(node.expression);
|
|
}
|
|
|
|
visitListLiteral(ListLiteral node) {
|
|
writeByte(node.isConst ? Tag.ConstListLiteral : Tag.ListLiteral);
|
|
writeNode(node.typeArgument);
|
|
writeNodeList(node.expressions);
|
|
}
|
|
|
|
visitMapLiteral(MapLiteral node) {
|
|
writeByte(node.isConst ? Tag.ConstMapLiteral : Tag.MapLiteral);
|
|
writeNode(node.keyType);
|
|
writeNode(node.valueType);
|
|
writeNodeList(node.entries);
|
|
}
|
|
|
|
visitMapEntry(MapEntry node) {
|
|
// Note: there is no tag on MapEntry
|
|
writeNode(node.key);
|
|
writeNode(node.value);
|
|
}
|
|
|
|
visitAwaitExpression(AwaitExpression node) {
|
|
writeByte(Tag.AwaitExpression);
|
|
writeNode(node.operand);
|
|
}
|
|
|
|
visitFunctionExpression(FunctionExpression node) {
|
|
writeByte(Tag.FunctionExpression);
|
|
writeNode(node.function);
|
|
}
|
|
|
|
visitLet(Let node) {
|
|
writeByte(Tag.Let);
|
|
writeVariableDeclaration(node.variable);
|
|
writeNode(node.body);
|
|
}
|
|
|
|
writeStatementOrEmpty(Statement node) {
|
|
if (node == null) {
|
|
writeByte(Tag.EmptyStatement);
|
|
} else {
|
|
writeNode(node);
|
|
}
|
|
}
|
|
|
|
visitInvalidStatement(InvalidStatement node) {
|
|
writeByte(Tag.InvalidStatement);
|
|
}
|
|
|
|
visitExpressionStatement(ExpressionStatement node) {
|
|
writeByte(Tag.ExpressionStatement);
|
|
writeNode(node.expression);
|
|
}
|
|
|
|
visitBlock(Block node) {
|
|
writeByte(Tag.Block);
|
|
writeNodeList(node.statements);
|
|
}
|
|
|
|
visitEmptyStatement(EmptyStatement node) {
|
|
writeByte(Tag.EmptyStatement);
|
|
}
|
|
|
|
visitAssertStatement(AssertStatement node) {
|
|
writeByte(Tag.AssertStatement);
|
|
writeNode(node.condition);
|
|
writeOptionalNode(node.message);
|
|
}
|
|
|
|
visitLabeledStatement(LabeledStatement node) {
|
|
writeByte(Tag.LabeledStatement);
|
|
writeNode(node.body);
|
|
}
|
|
|
|
visitBreakStatement(BreakStatement node) {
|
|
writeByte(Tag.BreakStatement);
|
|
writeUInt30(_labelIndexer[node.target]);
|
|
}
|
|
|
|
visitWhileStatement(WhileStatement node) {
|
|
writeByte(Tag.WhileStatement);
|
|
writeNode(node.condition);
|
|
writeNode(node.body);
|
|
}
|
|
|
|
visitDoStatement(DoStatement node) {
|
|
writeByte(Tag.DoStatement);
|
|
writeNode(node.body);
|
|
writeNode(node.condition);
|
|
}
|
|
|
|
visitForStatement(ForStatement node) {
|
|
writeByte(Tag.ForStatement);
|
|
writeVariableDeclarationList(node.variables);
|
|
writeOptionalNode(node.condition);
|
|
writeNodeList(node.updates);
|
|
writeNode(node.body);
|
|
}
|
|
|
|
visitForInStatement(ForInStatement node) {
|
|
writeByte(node.isAsync ? Tag.AsyncForInStatement : Tag.ForInStatement);
|
|
writeVariableDeclaration(node.variable);
|
|
writeNode(node.iterable);
|
|
writeNode(node.body);
|
|
}
|
|
|
|
visitSwitchStatement(SwitchStatement node) {
|
|
writeByte(Tag.SwitchStatement);
|
|
writeNode(node.expression);
|
|
writeNodeList(node.cases);
|
|
}
|
|
|
|
visitSwitchCase(SwitchCase node) {
|
|
// Note: there is no tag on SwitchCase.
|
|
writeNodeList(node.expressions);
|
|
writeByte(node.isDefault ? 1 : 0);
|
|
writeNode(node.body);
|
|
}
|
|
|
|
visitContinueSwitchStatement(ContinueSwitchStatement node) {
|
|
writeByte(Tag.ContinueSwitchStatement);
|
|
writeUInt30(_switchCaseIndexer[node.target]);
|
|
}
|
|
|
|
visitIfStatement(IfStatement node) {
|
|
writeByte(Tag.IfStatement);
|
|
writeNode(node.condition);
|
|
writeNode(node.then);
|
|
writeStatementOrEmpty(node.otherwise);
|
|
}
|
|
|
|
visitReturnStatement(ReturnStatement node) {
|
|
writeByte(Tag.ReturnStatement);
|
|
writeOptionalNode(node.expression);
|
|
}
|
|
|
|
visitTryCatch(TryCatch node) {
|
|
writeByte(Tag.TryCatch);
|
|
writeNode(node.body);
|
|
writeNodeList(node.catches);
|
|
}
|
|
|
|
visitCatch(Catch node) {
|
|
// Note: there is no tag on Catch.
|
|
writeOptionalNode(node.guard);
|
|
writeOptionalVariableDeclaration(node.exception);
|
|
writeOptionalVariableDeclaration(node.stackTrace);
|
|
writeNode(node.body);
|
|
}
|
|
|
|
visitTryFinally(TryFinally node) {
|
|
writeByte(Tag.TryFinally);
|
|
writeNode(node.body);
|
|
writeNode(node.finalizer);
|
|
}
|
|
|
|
visitYieldStatement(YieldStatement node) {
|
|
writeByte(Tag.YieldStatement);
|
|
writeByte(node.isYieldStar ? 1 : 0);
|
|
writeNode(node.expression);
|
|
}
|
|
|
|
visitVariableDeclaration(VariableDeclaration node) {
|
|
writeByte(Tag.VariableDeclaration);
|
|
writeVariableDeclaration(node);
|
|
}
|
|
|
|
void writeVariableDeclaration(VariableDeclaration node) {
|
|
writeByte(node.flags);
|
|
writeStringReference(node.name ?? '');
|
|
writeOptionalNode(node.type);
|
|
writeOptionalNode(node.initializer);
|
|
}
|
|
|
|
void writeVariableDeclarationList(List<VariableDeclaration> nodes) {
|
|
writeList(nodes, writeVariableDeclaration);
|
|
}
|
|
|
|
void writeOptionalVariableDeclaration(VariableDeclaration node) {
|
|
if (node == null) {
|
|
writeByte(Tag.Nothing);
|
|
} else {
|
|
writeByte(Tag.Something);
|
|
writeVariableDeclaration(node);
|
|
}
|
|
}
|
|
|
|
visitFunctionDeclaration(FunctionDeclaration node) {
|
|
writeByte(Tag.FunctionDeclaration);
|
|
writeVariableDeclaration(node.variable);
|
|
writeNode(node.function);
|
|
}
|
|
|
|
visitInvalidType(InvalidType node) {
|
|
writeByte(Tag.InvalidType);
|
|
}
|
|
|
|
visitDynamicType(DynamicType node) {
|
|
writeByte(Tag.DynamicType);
|
|
}
|
|
|
|
visitVoidType(VoidType node) {
|
|
writeByte(Tag.VoidType);
|
|
}
|
|
|
|
visitInterfaceType(InterfaceType node) {
|
|
if (node.typeArguments.isEmpty) {
|
|
writeByte(Tag.SimpleInterfaceType);
|
|
writeClassReference(node.classNode);
|
|
} else {
|
|
writeByte(Tag.InterfaceType);
|
|
writeClassReference(node.classNode);
|
|
writeNodeList(node.typeArguments);
|
|
}
|
|
}
|
|
|
|
visitFunctionType(FunctionType node) {
|
|
if (node.requiredParameterCount == node.positionalParameters.length &&
|
|
node.typeParameters.isEmpty &&
|
|
node.namedParameters.isEmpty) {
|
|
writeByte(Tag.SimpleFunctionType);
|
|
writeNodeList(node.positionalParameters);
|
|
writeNode(node.returnType);
|
|
} else {
|
|
writeByte(Tag.FunctionType);
|
|
_typeParameterIndexer.push(node.typeParameters);
|
|
writeNodeList(node.typeParameters);
|
|
writeUInt30(node.requiredParameterCount);
|
|
writeNodeList(node.positionalParameters);
|
|
writeList(node.namedParameters.keys.toList(), (String name) {
|
|
writeStringReference(name);
|
|
writeNode(node.namedParameters[name]);
|
|
});
|
|
writeNode(node.returnType);
|
|
_typeParameterIndexer.pop(node.typeParameters);
|
|
}
|
|
}
|
|
|
|
visitTypeParameterType(TypeParameterType node) {
|
|
writeByte(Tag.TypeParameterType);
|
|
writeUInt30(_typeParameterIndexer[node.parameter]);
|
|
}
|
|
|
|
visitTypeParameter(TypeParameter node) {
|
|
writeStringReference(node.name ?? '');
|
|
writeNode(node.bound);
|
|
}
|
|
|
|
defaultNode(Node node) {
|
|
throw 'Unsupported node: $node';
|
|
}
|
|
}
|
|
|
|
class VariableIndexer extends RecursiveVisitor {
|
|
final Map<VariableDeclaration, int> index = <VariableDeclaration, int>{};
|
|
int stackHeight = 0;
|
|
|
|
void build(Member node) => node.accept(this);
|
|
|
|
visitConstructor(Constructor node) {
|
|
node.function.accept(this);
|
|
// Keep parameters in scope when traversing initializers.
|
|
stackHeight = node.function.positionalParameters.length +
|
|
node.function.namedParameters.length;
|
|
for (var init in node.initializers) {
|
|
init.accept(this);
|
|
}
|
|
}
|
|
|
|
visitFunctionNode(FunctionNode node) {
|
|
int frame = stackHeight;
|
|
node.visitChildren(this);
|
|
stackHeight = frame;
|
|
}
|
|
|
|
visitBlock(Block node) {
|
|
int frame = stackHeight;
|
|
node.visitChildren(this);
|
|
stackHeight = frame;
|
|
}
|
|
|
|
visitLet(Let node) {
|
|
int frame = stackHeight;
|
|
node.visitChildren(this);
|
|
stackHeight = frame;
|
|
}
|
|
|
|
visitForInStatement(ForInStatement node) {
|
|
int frame = stackHeight;
|
|
node.visitChildren(this);
|
|
stackHeight = frame;
|
|
}
|
|
|
|
visitForStatement(ForStatement node) {
|
|
int frame = stackHeight;
|
|
node.visitChildren(this);
|
|
stackHeight = frame;
|
|
}
|
|
|
|
visitCatch(Catch node) {
|
|
int frame = stackHeight;
|
|
node.visitChildren(this);
|
|
stackHeight = frame;
|
|
}
|
|
|
|
visitVariableDeclaration(VariableDeclaration node) {
|
|
node.visitChildren(this);
|
|
assert(!index.containsKey(node));
|
|
index[node] = stackHeight;
|
|
++stackHeight;
|
|
}
|
|
|
|
int operator [](VariableDeclaration node) => index[node];
|
|
}
|
|
|
|
class LabelIndexer extends RecursiveVisitor {
|
|
final Map<LabeledStatement, int> index = <LabeledStatement, int>{};
|
|
int stackHeight = 0;
|
|
|
|
void build(FunctionNode node) => node.visitChildren(this);
|
|
|
|
visitFunctionNode(FunctionNode node) {
|
|
// Inhibit traversal into nested functions.
|
|
// The client must create a separate label indexer for the
|
|
// nested function.
|
|
}
|
|
|
|
visitLabeledStatement(LabeledStatement node) {
|
|
index[node] = stackHeight;
|
|
++stackHeight;
|
|
node.visitChildren(this);
|
|
--stackHeight;
|
|
}
|
|
|
|
int operator [](LabeledStatement node) => index[node];
|
|
}
|
|
|
|
class SwitchCaseIndexer extends RecursiveVisitor {
|
|
final Map<SwitchCase, int> index = <SwitchCase, int>{};
|
|
int stackHeight = 0;
|
|
|
|
void build(FunctionNode node) => node.visitChildren(this);
|
|
|
|
visitFunctionNode(FunctionNode node) {
|
|
// Inhibit traversal into nested functions.
|
|
// The client must create a separate case indexer for the
|
|
// nested function.
|
|
}
|
|
|
|
visitSwitchStatement(SwitchStatement node) {
|
|
int oldHeight = stackHeight;
|
|
for (var caseNode in node.cases) {
|
|
index[caseNode] = stackHeight;
|
|
++stackHeight;
|
|
}
|
|
node.visitChildren(this);
|
|
stackHeight = oldHeight;
|
|
}
|
|
|
|
int operator [](SwitchCase node) => index[node];
|
|
}
|
|
|
|
/// The type parameter indexer works differently from the other indexers because
|
|
/// type parameters can be bound inside DartTypes, which can be shared by the
|
|
/// in-memory representation (but not the binary form) and the index depends on
|
|
/// the use site.
|
|
class TypeParameterIndexer {
|
|
final Map<TypeParameter, int> index = <TypeParameter, int>{};
|
|
int stackHeight = 0;
|
|
|
|
void push(List<TypeParameter> typeParameters) {
|
|
for (var parameter in typeParameters) {
|
|
index[parameter] = stackHeight;
|
|
++stackHeight;
|
|
}
|
|
}
|
|
|
|
void pop(List<TypeParameter> typeParameters) {
|
|
stackHeight -= typeParameters.length;
|
|
}
|
|
|
|
int operator [](TypeParameter parameter) => index[parameter];
|
|
}
|
|
|
|
class StringTableEntry implements Comparable<StringTableEntry> {
|
|
final String value;
|
|
int frequency = 0;
|
|
|
|
StringTableEntry(this.value);
|
|
|
|
int compareTo(StringTableEntry other) => other.frequency - frequency;
|
|
}
|
|
|
|
class StringIndexer extends RecursiveVisitor<Null> {
|
|
final List<StringTableEntry> entries = <StringTableEntry>[];
|
|
final LinkedHashMap<String, int> index = new LinkedHashMap<String, int>();
|
|
|
|
StringIndexer() {
|
|
put('');
|
|
}
|
|
|
|
int get numberOfStrings => index.length;
|
|
|
|
void build(Node node) {
|
|
node.accept(this);
|
|
entries.sort();
|
|
for (int i = 0; i < entries.length; ++i) {
|
|
index[entries[i].value] = i;
|
|
}
|
|
}
|
|
|
|
void put(String string) {
|
|
int i = index.putIfAbsent(string, () {
|
|
entries.add(new StringTableEntry(string));
|
|
return index.length;
|
|
});
|
|
++entries[i].frequency;
|
|
}
|
|
|
|
void putOptional(String string) {
|
|
if (string != null) {
|
|
put(string);
|
|
}
|
|
}
|
|
|
|
int operator [](String string) => index[string];
|
|
|
|
void addLibraryImports(LibraryImportTable imports) {
|
|
imports.importPaths.forEach(put);
|
|
}
|
|
|
|
visitName(Name node) {
|
|
put(node.name);
|
|
}
|
|
|
|
visitLibrary(Library node) {
|
|
putOptional(node.name);
|
|
put('${node.importUri}');
|
|
node.visitChildren(this);
|
|
}
|
|
|
|
visitNormalClass(NormalClass node) {
|
|
putOptional(node.name);
|
|
node.visitChildren(this);
|
|
}
|
|
|
|
visitMixinClass(MixinClass node) {
|
|
putOptional(node.name);
|
|
node.visitChildren(this);
|
|
}
|
|
|
|
visitNamedExpression(NamedExpression node) {
|
|
put(node.name);
|
|
node.visitChildren(this);
|
|
}
|
|
|
|
visitStringLiteral(StringLiteral node) {
|
|
put(node.value);
|
|
}
|
|
|
|
visitIntLiteral(IntLiteral node) {
|
|
if (node.value.abs() >> 30 != 0) {
|
|
put('${node.value}');
|
|
}
|
|
}
|
|
|
|
visitDoubleLiteral(DoubleLiteral node) {
|
|
put('${node.value}');
|
|
}
|
|
|
|
visitSymbolLiteral(SymbolLiteral node) {
|
|
put(node.value);
|
|
}
|
|
|
|
visitVariableDeclaration(VariableDeclaration node) {
|
|
putOptional(node.name);
|
|
node.visitChildren(this);
|
|
}
|
|
|
|
visitFunctionType(FunctionType node) {
|
|
node.namedParameters.keys.forEach(put);
|
|
node.visitChildren(this);
|
|
}
|
|
|
|
visitTypeParameter(TypeParameter node) {
|
|
putOptional(node.name);
|
|
node.visitChildren(this);
|
|
}
|
|
}
|
|
|
|
/// Computes and stores the index of a library, class, or member within its
|
|
/// parent list.
|
|
class GlobalIndexer extends TreeVisitor {
|
|
final Map<TreeNode, int> indices = <TreeNode, int>{};
|
|
|
|
void buildIndexForContainer(TreeNode libraryOrClass) {
|
|
libraryOrClass.accept(this);
|
|
}
|
|
|
|
void buildIndexForList(List<TreeNode> list) {
|
|
for (int i = 0; i < list.length; ++i) {
|
|
TreeNode child = list[i];
|
|
if (child != null) {
|
|
indices[child] = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
visitProgram(Program node) {
|
|
buildIndexForList(node.libraries);
|
|
}
|
|
|
|
visitLibrary(Library node) {
|
|
buildIndexForList(node.classes);
|
|
buildIndexForList(node.fields);
|
|
buildIndexForList(node.procedures);
|
|
}
|
|
|
|
visitNormalClass(NormalClass node) {
|
|
buildIndexForList(node.fields);
|
|
buildIndexForList(node.constructors);
|
|
buildIndexForList(node.procedures);
|
|
}
|
|
|
|
visitMixinClass(MixinClass node) {
|
|
buildIndexForList(node.constructors);
|
|
}
|
|
|
|
int operator [](TreeNode memberOrLibraryOrClass) {
|
|
var node = memberOrLibraryOrClass;
|
|
assert(node is Member || node is Library || node is Class);
|
|
int index = indices[node];
|
|
if (index == null) {
|
|
buildIndexForContainer(node.parent);
|
|
return indices[node];
|
|
} else {
|
|
return index;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Puts a buffer in front of an [IOSink].
|
|
class BufferedSink {
|
|
static const int SIZE = 100000;
|
|
static const int SMALL = 10000;
|
|
final IOSink _sink;
|
|
Uint8List _buffer = new Uint8List(SIZE);
|
|
int length = 0;
|
|
|
|
BufferedSink(this._sink);
|
|
|
|
void addByte(int byte) {
|
|
_buffer[length++] = byte;
|
|
if (length == SIZE) {
|
|
_sink.add(_buffer);
|
|
_buffer = new Uint8List(SIZE);
|
|
length = 0;
|
|
}
|
|
}
|
|
|
|
void addBytes(List<int> bytes) {
|
|
// Avoid copying a large buffer into the another large buffer. Also, if
|
|
// the bytes buffer is too large to fit in our own buffer, just emit both.
|
|
if (length + bytes.length < SIZE &&
|
|
(bytes.length < SMALL || length < SMALL)) {
|
|
if (length == 0) {
|
|
_sink.add(bytes);
|
|
} else {
|
|
_buffer.setRange(length, length + bytes.length, bytes);
|
|
length += bytes.length;
|
|
}
|
|
} else if (bytes.length < SMALL) {
|
|
// Flush as much as we can in the current buffer.
|
|
_buffer.setRange(length, SIZE, bytes);
|
|
_sink.add(_buffer);
|
|
// Copy over the remainder into a new buffer. It is guaranteed to fit
|
|
// because the input byte array is small.
|
|
int alreadyEmitted = SIZE - length;
|
|
int remainder = bytes.length - alreadyEmitted;
|
|
_buffer = new Uint8List(SIZE);
|
|
_buffer.setRange(0, remainder, bytes, alreadyEmitted);
|
|
length = remainder;
|
|
} else {
|
|
_sink.add(_buffer.sublist(0, length));
|
|
_sink.add(bytes);
|
|
_buffer = new Uint8List(SIZE);
|
|
length = 0;
|
|
}
|
|
}
|
|
|
|
void flush() {
|
|
_sink.add(_buffer.sublist(0, length));
|
|
_buffer = new Uint8List(SIZE);
|
|
length = 0;
|
|
}
|
|
|
|
void flushAndDestroy() {
|
|
_sink.add(_buffer.sublist(0, length));
|
|
}
|
|
}
|