blob: ea6b55c12d8e959a640fb7ccdb95a28a71c841d6 [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_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(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));
}
}