[kernel] Add kernel2kernel constant evaluation, binary format as well as vm support

The introduced "constants" transformation can evaluate constant expressions.  The
original use-sites of constant expressions are replaced by a new [ConstantExpression]
node, which points to a subclass of a new [Constant] class hierarchy.  Constant
[Field]s and [VariableDeclarations]s will be removed, since all use-sites are
re-written.

The [Constant] class hierarchy is, similarly to the [DartType] class hierarchy, not
part of the AST tree (also has no parent pointer).  The constants form a
DAG (directed acyclic graph).

There is no canonicalization requirement of the [Constant] objects referenced by the
AST (via [ConstantExpression]).  Although it is beneficial to canonicalize them during
construction, since it reduces time spent in operator==/hashCode.

This CL furthermore adds support for a constant table in the binary format.  Similarly
to [String]s, we canonicalize the constants before writing the table to the binary.
The constant table entries in the binary are written in a post-order way, to ensure
easy construction on the backend side.

The text format will be augmented with a "constants { ... }" section at the end,
which lists the constants in the same order as in the binary format.

The transformation can be used by those backends who choose to do so.  It is not
enabled by default atm.  It should therefore not affect analyzer, fasta or other
components.

Change-Id: I57cd9624fedcf537ab6870db76246149647bed21
Reviewed-on: https://dart-review.googlesource.com/14382
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Kevin Millikin <kmillikin@google.com>
This commit is contained in:
Martin Kustermann 2017-11-16 11:08:02 +00:00 committed by commit-bot@chromium.org
parent 02511ceeb4
commit d354a28cb2
29 changed files with 2466 additions and 116 deletions

View file

@ -0,0 +1,108 @@
// Copyright (c) 2017, 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.
import 'dart:async' show Future;
import 'dart:io' show Directory, File, Platform;
import 'package:async_helper/async_helper.dart' show asyncEnd, asyncStart;
import 'package:testing/testing.dart' show StdioProcess;
final Uri compiler = Uri.base.resolve('pkg/front_end/tool/_fasta/compile.dart');
final Uri transform = Uri.base.resolve('pkg/kernel/bin/transform.dart');
final Uri dump = Uri.base.resolve('pkg/kernel/bin/dump.dart');
final Uri packagesFile = Uri.base.resolve('.packages');
final Uri dartVm = Uri.base.resolve(Platform.resolvedExecutable);
Future main() async {
asyncStart();
final Directory tmp = await Directory.systemTemp.createTemp('whole_program');
final Uri dartFile = tmp.uri.resolve('hello.dart');
final Uri dillFile = tmp.uri.resolve('hello.dart.dill');
final Uri constantsDillFile = tmp.uri.resolve('hello.dart.constants.dill');
final Uri constantsDillTxtFile =
tmp.uri.resolve('hello.dart.constants.dill.txt');
// Write the hello world file.
await new File(dartFile.toFilePath()).writeAsString('''
// Ensure we import a big program!
import 'package:compiler/src/dart2js.dart';
import 'package:front_end/src/fasta/kernel/kernel_target.dart';
void main() => print('hello world!');
''');
try {
await runCompiler(dartFile, dillFile);
await transformDillFile(dillFile, constantsDillFile);
await dumpDillFile(constantsDillFile, constantsDillTxtFile);
await runHelloWorld(constantsDillFile);
} finally {
await tmp.delete(recursive: true);
}
asyncEnd();
}
Future runCompiler(Uri input, Uri output) async {
final buildDir = Uri.base.resolve(Platform.resolvedExecutable).resolve(".");
final platformDill = buildDir.resolve("vm_platform.dill").toFilePath();
final List<String> arguments = <String>[
'--packages=${packagesFile.toFilePath()}',
'-c',
compiler.toFilePath(),
'--platform=$platformDill',
'--output=${output.toFilePath()}',
'--packages=${packagesFile.toFilePath()}',
'--verify',
input.toFilePath(),
];
await run('Compilation of hello.dart', arguments);
}
Future transformDillFile(Uri from, Uri to) async {
final List<String> arguments = <String>[
transform.toFilePath(),
'-f',
'bin',
'-t',
'constants',
'-o',
to.toFilePath(),
from.toFilePath(),
];
await run('Transforming $from --to--> $to', arguments);
}
Future dumpDillFile(Uri dillFile, Uri txtFile) async {
final List<String> arguments = <String>[
dump.toFilePath(),
dillFile.toFilePath(),
txtFile.toFilePath(),
];
await run('Dumping $dillFile --to--> $txtFile', arguments);
}
Future runHelloWorld(Uri dillFile) async {
final List<String> arguments = <String>['-c', dillFile.toFilePath()];
await run('Running hello.dart', arguments, 'hello world!\n');
}
Future run(String message, List<String> arguments,
[String expectedOutput]) async {
final Stopwatch sw = new Stopwatch()..start();
print('Running:\n ${dartVm.toFilePath()} ${arguments.join(' ')}');
StdioProcess result = await StdioProcess.run(dartVm.toFilePath(), arguments,
timeout: const Duration(seconds: 120));
print('Output:\n ${result.output.replaceAll('\n', ' \n')}');
print('ExitCode: ${result.exitCode}');
print('Took: ${sw.elapsed}\n\n');
if ((expectedOutput != null && result.output != expectedOutput) ||
result.exitCode != 0) {
throw '$message failed.';
}
}

View file

@ -13,6 +13,7 @@ import 'package:kernel/kernel.dart';
import 'package:kernel/src/tool/batch_util.dart';
import 'package:kernel/target/targets.dart';
import 'package:kernel/transformations/closure_conversion.dart' as closures;
import 'package:kernel/transformations/constants.dart' as constants;
import 'package:kernel/transformations/continuation.dart' as cont;
import 'package:kernel/transformations/empty.dart' as empty;
import 'package:kernel/transformations/method_call.dart' as method_call;
@ -20,6 +21,7 @@ import 'package:kernel/transformations/mixin_full_resolution.dart' as mix;
import 'package:kernel/transformations/treeshaker.dart' as treeshaker;
// import 'package:kernel/verifier.dart';
import 'package:kernel/transformations/coq.dart' as coq;
import 'package:kernel/vm/constants_native_effects.dart';
import 'util.dart';
@ -78,8 +80,9 @@ Future<CompilerOutcome> runTransformation(List<String> arguments) async {
parseProgramRoots(embedderEntryPointManifests);
var program = loadProgramFromBinary(input);
var coreTypes = new CoreTypes(program);
var hierarchy = new ClosedWorldClassHierarchy(program);
final coreTypes = new CoreTypes(program);
final hierarchy = new ClosedWorldClassHierarchy(program);
switch (options['transformation']) {
case 'continuation':
program = cont.transformProgram(coreTypes, program);
@ -94,6 +97,10 @@ Future<CompilerOutcome> runTransformation(List<String> arguments) async {
case 'coq':
program = coq.transformProgram(coreTypes, program);
break;
case 'constants':
final VmConstantsBackend backend = new VmConstantsBackend(coreTypes);
program = constants.transformProgram(program, backend);
break;
case 'treeshake':
program = treeshaker.transformProgram(coreTypes, hierarchy, program,
programRoots: programRoots);

View file

@ -78,9 +78,14 @@ type StringReference {
UInt index; // Index into the Program's strings.
}
type ConstantReference {
UInt index; // Index into the Program's constants.
}
type SourceInfo {
List<Byte> uriUtf8Bytes;
List<Byte> sourceUtf8Bytes;
// Line starts are delta-encoded (they are encoded as line lengths). The list
// [0, 10, 25, 32, 42] is encoded as [0, 10, 15, 7, 10].
List<UInt> lineStarts;
@ -131,6 +136,7 @@ type ProgramFile {
List<CanonicalName> canonicalNames;
RList<MetadataMapping> metadataMappings;
StringTable strings;
List<Constant> constants;
ProgramIndex programIndex;
}
@ -155,6 +161,7 @@ type ProgramIndex {
UInt32 binaryOffsetForSourceTable;
UInt32 binaryOffsetForCanonicalNames;
UInt32 binaryOffsetForStringTable;
UInt32 binaryOffsetForConstantTable;
UInt32 mainMethodReference; // This is a ProcedureReference with a fixed-size integer.
UInt32[libraryCount + 1] libraryOffsets;
UInt32 libraryCount;
@ -810,6 +817,61 @@ type ClosureCreation extends Expression {
List<DartType> typeArguments;
}
type ConstantExpression extends Expression {
Byte tag = 107;
ConstantReference constantReference;
}
abstract type Constant extends Node {}
type NullConstant extends Constant {
Byte tag = 0;
}
type BoolConstant extends Constant {
Byte tag = 1;
Byte value;
}
type IntConstant extends Constant {
Byte tag = 2;
PositiveIntLiteral | NegativeIntLiteral | SpecializedIntLiteral | BigIntLiteral value;
}
type DoubleConstant extends Constant {
Byte tag = 3;
StringReference value;
}
type StringConstant extends Constant {
Byte tag = 4;
StringReference value;
}
type MapConstant extends Constant {
Byte tag = 5;
DartType keyType;
DartType valueType;
List<[ConstantReference, ConstantReference]> keyValueList;
}
type ListConstant extends Constant {
Byte tag = 6;
DartType type;
List<ConstantReference> values;
}
type InstanceConstant extends Constant {
Byte tag = 7;
List<DartType> typeArguments;
List<[FieldReference, ConstantReference]> values;
}
type TearOffConstant extends Constant {
Byte tag = 8;
CanonicalNameReference staticProcedureReference;
}
abstract type Statement extends Node {}
type InvalidStatement extends Statement {

View file

@ -3154,6 +3154,28 @@ class FunctionExpression extends Expression {
}
}
class ConstantExpression extends Expression {
Constant constant;
ConstantExpression(this.constant) {
assert(constant != null);
}
DartType getStaticType(TypeEnvironment types) =>
throw 'ConstantExpression.staticType() is unimplemented';
accept(ExpressionVisitor v) => v.visitConstantExpression(this);
accept1(ExpressionVisitor1 v, arg) => v.visitConstantExpression(this, arg);
visitChildren(Visitor v) {
constant?.acceptReference(v);
}
transformChildren(Transformer v) {
constant = v.visitConstant(constant);
}
}
/// Synthetic expression of form `let v = x in y`
class Let extends Expression {
VariableDeclaration variable; // Must have an initializer.
@ -3444,6 +3466,8 @@ class Block extends Statement {
Block(this.statements) {
setParents(statements, this);
statements.add(null);
statements.removeLast();
}
accept(StatementVisitor v) => v.visitBlock(this);
@ -4900,6 +4924,277 @@ class Supertype extends Node {
}
}
// ------------------------------------------------------------------------
// CONSTANTS
// ------------------------------------------------------------------------
abstract class Constant extends Node {
/// Calls the `visit*ConstantReference()` method on visitor [v] for all
/// constants referenced in this constant.
///
/// (Note that a constant can be seen as a DAG (directed acyclic graph) and
/// not a tree!)
visitChildren(Visitor v);
/// Calls the `visit*Constant()` method on the visitor [v].
accept(ConstantVisitor v);
/// Calls the `visit*ConstantReference()` method on the visitor [v].
acceptReference(Visitor v);
/// The Kernel AST will reference [Constant]s via [ConstantExpression]s. The
/// constants are not required to be canonicalized, but they have to be deeply
/// comparable via hashCode/==!
int get hashCode;
bool operator ==(Object other);
}
abstract class PrimitiveConstant<T> extends Constant {
final T value;
PrimitiveConstant(this.value);
String toString() => '${this.runtimeType}($value)';
int get hashCode => value.hashCode;
bool operator ==(Object other) =>
other is PrimitiveConstant<T> && other.value == value;
}
class NullConstant extends PrimitiveConstant<Null> {
NullConstant() : super(null);
visitChildren(Visitor v) {}
accept(ConstantVisitor v) => v.visitNullConstant(this);
acceptReference(Visitor v) => v.visitNullConstantReference(this);
}
class BoolConstant extends PrimitiveConstant<bool> {
BoolConstant(bool value) : super(value);
visitChildren(Visitor v) {}
accept(ConstantVisitor v) => v.visitBoolConstant(this);
acceptReference(Visitor v) => v.visitBoolConstantReference(this);
}
class IntConstant extends PrimitiveConstant<int> {
IntConstant(int value) : super(value);
visitChildren(Visitor v) {}
accept(ConstantVisitor v) => v.visitIntConstant(this);
acceptReference(Visitor v) => v.visitIntConstantReference(this);
}
class DoubleConstant extends PrimitiveConstant<double> {
DoubleConstant(double value) : super(value);
visitChildren(Visitor v) {}
accept(ConstantVisitor v) => v.visitDoubleConstant(this);
acceptReference(Visitor v) => v.visitDoubleConstantReference(this);
int get hashCode => value.isNaN ? 199 : super.hashCode;
bool operator ==(Object other) =>
other is DoubleConstant &&
(other.value == value || identical(value, other.value) /* For NaN */);
}
class StringConstant extends PrimitiveConstant<String> {
StringConstant(String value) : super(value) {
assert(value != null);
}
visitChildren(Visitor v) {}
accept(ConstantVisitor v) => v.visitStringConstant(this);
acceptReference(Visitor v) => v.visitStringConstantReference(this);
}
class MapConstant extends Constant {
final DartType keyType;
final DartType valueType;
final List<ConstantMapEntry> entries;
MapConstant(this.keyType, this.valueType, this.entries);
visitChildren(Visitor v) {
keyType.accept(v);
valueType.accept(v);
for (final ConstantMapEntry entry in entries) {
entry.key.acceptReference(v);
entry.value.acceptReference(v);
}
}
accept(ConstantVisitor v) => v.visitMapConstant(this);
acceptReference(Visitor v) => v.visitMapConstantReference(this);
String toString() => '${this.runtimeType}<$keyType, $valueType>($entries)';
// TODO(kustermann): Consider combining the hash codes in a better way (also
// below and in [listHashCode]/[mapHashCode].
int _cachedHashCode;
int get hashCode {
return _cachedHashCode ??=
keyType.hashCode ^ valueType.hashCode ^ listHashCode(entries);
}
bool operator ==(Object other) =>
identical(this, other) ||
(other is MapConstant &&
other.keyType == keyType &&
other.valueType == valueType &&
listEquals(other.entries, entries));
}
class ConstantMapEntry {
final Constant key;
final Constant value;
ConstantMapEntry(this.key, this.value);
String toString() => '$key: $value';
int get hashCode => key.hashCode ^ value.hashCode;
bool operator ==(Object other) =>
other is ConstantMapEntry && other.key == key && other.value == value;
}
class ListConstant extends Constant {
final DartType typeArgument;
final List<Constant> entries;
ListConstant(this.typeArgument, this.entries);
visitChildren(Visitor v) {
typeArgument.accept(v);
for (final Constant constant in entries) {
constant.acceptReference(v);
}
}
accept(ConstantVisitor v) => v.visitListConstant(this);
acceptReference(Visitor v) => v.visitListConstantReference(this);
String toString() => '${this.runtimeType}<$typeArgument>($entries)';
int _cachedHashCode;
int get hashCode {
return _cachedHashCode ??= typeArgument.hashCode ^ listHashCode(entries);
}
bool operator ==(Object other) =>
identical(this, other) ||
(other is ListConstant &&
other.typeArgument == typeArgument &&
listEquals(other.entries, entries));
}
class InstanceConstant extends Constant {
final Reference classReference;
final List<DartType> typeArguments;
final Map<Reference, Constant> fieldValues;
InstanceConstant(this.classReference, this.typeArguments, this.fieldValues);
Class get klass => classReference.asClass;
visitChildren(Visitor v) {
classReference.asClass.acceptReference(v);
visitList(typeArguments, v);
for (final Reference reference in fieldValues.keys) {
reference.asField.acceptReference(v);
}
for (final Constant constant in fieldValues.values) {
constant.acceptReference(v);
}
}
accept(ConstantVisitor v) => v.visitInstanceConstant(this);
acceptReference(Visitor v) => v.visitInstanceConstantReference(this);
String toString() {
final sb = new StringBuffer();
sb.write('${classReference.asClass}');
if (!classReference.asClass.typeParameters.isEmpty) {
sb.write('<');
sb.write(typeArguments.map((type) => type.toString()).join(', '));
sb.write('>');
}
sb.write(' {');
fieldValues.forEach((Reference fieldRef, Constant constant) {
sb.write('${fieldRef.asField.name}: $constant, ');
});
sb.write('}');
return sb.toString();
}
int _cachedHashCode;
int get hashCode {
return _cachedHashCode ??= classReference.hashCode ^
listHashCode(typeArguments) ^
mapHashCode(fieldValues);
}
bool operator ==(Object other) {
return identical(this, other) ||
(other is InstanceConstant &&
other.classReference == classReference &&
listEquals(other.typeArguments, typeArguments) &&
mapEquals(other.fieldValues, fieldValues));
}
}
class TearOffConstant extends Constant {
final Reference procedureReference;
TearOffConstant(Procedure procedure)
: procedureReference = procedure.reference {
assert(procedure.isStatic);
}
TearOffConstant.byReference(this.procedureReference);
Procedure get procedure => procedureReference?.asProcedure;
visitChildren(Visitor v) {
procedureReference.asProcedure.acceptReference(v);
}
accept(ConstantVisitor v) => v.visitTearOffConstant(this);
acceptReference(Visitor v) => v.visitTearOffConstantReference(this);
String toString() {
return '${runtimeType}(${procedure})';
}
int get hashCode => procedure.hashCode;
bool operator ==(Object other) {
return other is TearOffConstant && other.procedure == procedure;
}
}
class TypeLiteralConstant extends Constant {
final DartType type;
TypeLiteralConstant(this.type);
visitChildren(Visitor v) {
type.accept(v);
}
accept(ConstantVisitor v) => v.visitTypeLiteralConstant(this);
acceptReference(Visitor v) => v.visitTypeLiteralConstantReference(this);
String toString() => '${runtimeType}(${type})';
int get hashCode => type.hashCode;
bool operator ==(Object other) {
return other is TypeLiteralConstant && other.type == type;
}
}
// ------------------------------------------------------------------------
// PROGRAM
// ------------------------------------------------------------------------
@ -5251,6 +5546,33 @@ CanonicalName getCanonicalNameOfLibrary(Library library) {
return library.canonicalName;
}
int listHashCode(List list) {
return list.fold(0, (int value, Object item) => value ^ item.hashCode);
}
int mapHashCode(Map map) {
int value = 0;
for (final Object x in map.keys) value ^= x.hashCode;
for (final Object x in map.values) value ^= x.hashCode;
return value;
}
bool listEquals(List a, List b) {
if (a.length != b.length) return false;
for (int i = 0; i < a.length; i++) {
if (a[i] != b[i]) return false;
}
return true;
}
bool mapEquals(Map a, Map b) {
if (a.length != b.length) return false;
for (final Object key in a.keys) {
if (!b.containsKey(key) || a[key] != b[key]) return false;
}
return true;
}
/// Returns the canonical name of [typedef_], or throws an exception if the
/// typedef has not been assigned a canonical name yet.
///

View file

@ -25,6 +25,7 @@ class _ProgramIndex {
int binaryOffsetForSourceTable;
int binaryOffsetForStringTable;
int binaryOffsetForCanonicalNames;
int binaryOffsetForConstantTable;
int mainMethodReference;
List<int> libraryOffsets;
int libraryCount;
@ -42,6 +43,7 @@ class BinaryBuilder {
int _byteOffset = 0;
final List<String> _stringTable = <String>[];
final List<String> _sourceUriTable = <String>[];
List<Constant> _constantTable;
List<CanonicalName> _linkTable;
int _transformerFlags = 0;
Library _currentLibrary;
@ -150,6 +152,82 @@ class BinaryBuilder {
}
}
void readConstantTable() {
final int length = readUInt();
_constantTable = new List<Constant>(length);
for (int i = 0; i < length; i++) {
_constantTable[i] = readConstantTableEntry();
}
}
Constant readConstantTableEntry() {
final int constantTag = readByte();
switch (constantTag) {
case ConstantTag.NullConstant:
return new NullConstant();
case ConstantTag.BoolConstant:
return new BoolConstant(readByte() == 1);
case ConstantTag.IntConstant:
return new IntConstant((readExpression() as IntLiteral).value);
case ConstantTag.DoubleConstant:
return new DoubleConstant(double.parse(readStringReference()));
case ConstantTag.StringConstant:
return new StringConstant(readStringReference());
case ConstantTag.MapConstant:
final DartType keyType = readDartType();
final DartType valueType = readDartType();
final int length = readUInt();
final List<ConstantMapEntry> entries =
new List<ConstantMapEntry>(length);
for (int i = 0; i < length; i++) {
final Constant key = readConstantReference();
final Constant value = readConstantReference();
entries[i] = new ConstantMapEntry(key, value);
}
return new MapConstant(keyType, valueType, entries);
case ConstantTag.ListConstant:
final DartType typeArgument = readDartType();
final int length = readUInt();
final List<Constant> entries = new List<Constant>(length);
for (int i = 0; i < length; i++) {
entries[i] = readConstantReference();
}
return new ListConstant(typeArgument, entries);
case ConstantTag.InstanceConstant:
final Reference classReference = readClassReference();
final int typeArgumentCount = readUInt();
final List<DartType> typeArguments =
new List<DartType>(typeArgumentCount);
for (int i = 0; i < typeArgumentCount; i++) {
typeArguments[i] = readDartType();
}
final int fieldValueCount = readUInt();
final Map<Reference, Constant> fieldValues = <Reference, Constant>{};
for (int i = 0; i < fieldValueCount; i++) {
final Reference fieldRef =
readCanonicalNameReference().getReference();
final Constant constant = readConstantReference();
fieldValues[fieldRef] = constant;
}
return new InstanceConstant(classReference, typeArguments, fieldValues);
case ConstantTag.TearOffConstant:
final Reference reference = readCanonicalNameReference().getReference();
return new TearOffConstant.byReference(reference);
case ConstantTag.TypeLiteralConstant:
final DartType type = readDartType();
return new TypeLiteralConstant(type);
}
throw 'Invalid constant tag $constantTag';
}
Constant readConstantReference() {
final int index = readUInt();
Constant constant = _constantTable[index];
assert(constant != null);
return constant;
}
String readUriReference() {
return _sourceUriTable[readUInt()];
}
@ -340,12 +418,13 @@ class BinaryBuilder {
// There are these fields: file size, library count, library count + 1
// offsets, main reference, string table offset, canonical name offset and
// source table offset. That's 6 fields + number of libraries.
_byteOffset -= (result.libraryCount + 7) * 4;
_byteOffset -= (result.libraryCount + 8) * 4;
// Now read the program index.
result.binaryOffsetForSourceTable = _programStartOffset + readUint32();
result.binaryOffsetForCanonicalNames = _programStartOffset + readUint32();
result.binaryOffsetForStringTable = _programStartOffset + readUint32();
result.binaryOffsetForConstantTable = _programStartOffset + readUint32();
result.mainMethodReference = readUint32();
for (int i = 0; i < result.libraryCount + 1; ++i) {
result.libraryOffsets[i] = _programStartOffset + readUint32();
@ -387,6 +466,9 @@ class BinaryBuilder {
Map<String, Source> uriToSource = readUriToSource();
program.uriToSource.addAll(uriToSource);
_byteOffset = index.binaryOffsetForConstantTable;
readConstantTable();
int numberOfLibraries = index.libraryCount;
for (int i = 0; i < numberOfLibraries; ++i) {
_byteOffset = index.libraryOffsets[i];
@ -1245,6 +1327,8 @@ class BinaryBuilder {
var typeArgs = readDartTypeList();
return new ClosureCreation.byReference(
topLevelFunctionReference, contextVector, functionType, typeArgs);
case Tag.ConstantExpression:
return new ConstantExpression(readConstantReference());
default:
throw fail('Invalid expression tag: $tag');
}
@ -1265,7 +1349,8 @@ class BinaryBuilder {
List<Statement> readStatementList() {
int length = readUInt();
List<Statement> result = new List<Statement>(length);
List<Statement> result = <Statement>[];
result.length = length;
for (int i = 0; i < length; ++i) {
result[i] = readStatement();
}

View file

@ -19,6 +19,7 @@ class BinaryPrinter extends Visitor implements BinarySink {
SwitchCaseIndexer _switchCaseIndexer;
final TypeParameterIndexer _typeParameterIndexer = new TypeParameterIndexer();
final StringIndexer stringIndexer;
ConstantIndexer _constantIndexer;
final StringIndexer _sourceUriIndexer = new StringIndexer();
final Set<String> _knownSourceUri = new Set<String>();
Map<LibraryDependency, int> _libraryDependencyIndex =
@ -38,6 +39,7 @@ class BinaryPrinter extends Visitor implements BinarySink {
int _binaryOffsetForSourceTable = -1;
int _binaryOffsetForStringTable = -1;
int _binaryOffsetForLinkTable = -1;
int _binaryOffsetForConstantTable = -1;
List<CanonicalName> _canonicalNameList;
Set<CanonicalName> _knownCanonicalNameNonRootTops = new Set<CanonicalName>();
@ -53,7 +55,9 @@ class BinaryPrinter extends Visitor implements BinarySink {
/// in every printer.
BinaryPrinter(Sink<List<int>> sink, {StringIndexer stringIndexer})
: _sink = new BufferedSink(sink),
stringIndexer = stringIndexer ?? new StringIndexer();
stringIndexer = stringIndexer ?? new StringIndexer() {
_constantIndexer = new ConstantIndexer(this.stringIndexer);
}
void _flush() {
_sink.flushAndDestroy();
@ -93,10 +97,8 @@ class BinaryPrinter extends Visitor implements BinarySink {
return _sink.flushedLength + _sink.length;
}
void writeStringTable(StringIndexer indexer, bool updateBinaryOffset) {
if (updateBinaryOffset) {
_binaryOffsetForStringTable = getBufferOffset();
}
void writeStringTable(StringIndexer indexer) {
_binaryOffsetForStringTable = getBufferOffset();
// Write the end offsets.
writeUInt30(indexer.numberOfStrings);
@ -119,6 +121,73 @@ class BinaryPrinter extends Visitor implements BinarySink {
writeList(strings, writeStringReference);
}
void writeConstantReference(Constant constant) {
writeUInt30(_constantIndexer.put(constant));
}
void writeConstantTable(ConstantIndexer indexer) {
_binaryOffsetForConstantTable = getBufferOffset();
writeUInt30(indexer.entries.length);
for (final entry in indexer.entries) {
writeConstantTableEntry(entry);
}
}
void writeConstantTableEntry(Constant constant) {
if (constant is NullConstant) {
writeByte(ConstantTag.NullConstant);
} else if (constant is BoolConstant) {
writeByte(ConstantTag.BoolConstant);
writeByte(constant.value ? 1 : 0);
} else if (constant is IntConstant) {
writeByte(ConstantTag.IntConstant);
writeInteger(constant.value);
} else if (constant is DoubleConstant) {
writeByte(ConstantTag.DoubleConstant);
writeStringReference('${constant.value}');
} else if (constant is StringConstant) {
writeByte(ConstantTag.StringConstant);
writeStringReference(constant.value);
} else if (constant is MapConstant) {
writeByte(ConstantTag.MapConstant);
writeDartType(constant.keyType);
writeDartType(constant.valueType);
writeUInt30(constant.entries.length);
for (final ConstantMapEntry entry in constant.entries) {
writeConstantReference(entry.key);
writeConstantReference(entry.value);
}
} else if (constant is ListConstant) {
writeByte(ConstantTag.ListConstant);
writeDartType(constant.typeArgument);
writeUInt30(constant.entries.length);
constant.entries.forEach(writeConstantReference);
} else if (constant is InstanceConstant) {
writeByte(ConstantTag.InstanceConstant);
writeClassReference(constant.klass);
writeUInt30(constant.typeArguments.length);
constant.typeArguments.forEach(writeDartType);
writeUInt30(constant.fieldValues.length);
constant.fieldValues.forEach((Reference fieldRef, Constant value) {
writeCanonicalNameReference(fieldRef.canonicalName);
writeConstantReference(value);
});
} else if (constant is TearOffConstant) {
writeByte(ConstantTag.TearOffConstant);
writeCanonicalNameReference(constant.procedure.canonicalName);
} else if (constant is TypeLiteralConstant) {
writeByte(ConstantTag.TypeLiteralConstant);
writeDartType(constant.type);
} else {
throw 'Unsupported constant $constant';
}
}
void writeDartType(DartType type) {
type.accept(this);
}
void writeUriReference(String string) {
int index = 0; // equivalent to index = _sourceUriIndexer[""];
if (_knownSourceUri.contains(string)) {
@ -220,7 +289,8 @@ class BinaryPrinter extends Visitor implements BinarySink {
writeUriToSource(program.uriToSource);
writeLinkTable(program);
_writeMetadataMappingSection(program);
writeStringTable(stringIndexer, true);
writeStringTable(stringIndexer);
writeConstantTable(_constantIndexer);
writeProgramIndex(program, program.libraries);
_flush();
@ -345,6 +415,8 @@ class BinaryPrinter extends Visitor implements BinarySink {
writeUInt32(_binaryOffsetForLinkTable);
assert(_binaryOffsetForStringTable >= 0);
writeUInt32(_binaryOffsetForStringTable);
assert(_binaryOffsetForConstantTable >= 0);
writeUInt32(_binaryOffsetForConstantTable);
CanonicalName main = getCanonicalNameOfMember(program.mainMethod);
if (main == null) {
@ -974,7 +1046,10 @@ class BinaryPrinter extends Visitor implements BinarySink {
}
visitIntLiteral(IntLiteral node) {
int value = node.value;
writeInteger(node.value);
}
writeInteger(int value) {
int biasedValue = value + Tag.SpecializedIntLiteralBias;
if (biasedValue >= 0 &&
biasedValue & Tag.SpecializedPayloadMask == biasedValue) {
@ -990,14 +1065,18 @@ class BinaryPrinter extends Visitor implements BinarySink {
} else {
// TODO: Pick a better format for big int literals.
writeByte(Tag.BigIntLiteral);
writeStringReference('${node.value}');
writeStringReference('$value');
}
}
visitDoubleLiteral(DoubleLiteral node) {
writeDouble(node.value);
}
writeDouble(double value) {
// TODO: Pick a better format for double literals.
writeByte(Tag.DoubleLiteral);
writeStringReference('${node.value}');
writeStringReference('$value');
}
visitBoolLiteral(BoolLiteral node) {
@ -1159,6 +1238,11 @@ class BinaryPrinter extends Visitor implements BinarySink {
_labelIndexer.exit();
}
visitConstantExpression(ConstantExpression node) {
writeByte(Tag.ConstantExpression);
writeConstantReference(node.constant);
}
visitBreakStatement(BreakStatement node) {
writeByte(Tag.BreakStatement);
writeOffset(node.fileOffset);
@ -1414,6 +1498,10 @@ class BinaryPrinter extends Visitor implements BinarySink {
writeNode(node.bound);
}
defaultConstant(Constant node) {
throw 'Implement handling of ${node.runtimeType}';
}
defaultNode(Node node) {
throw 'Unsupported node: $node';
}
@ -1479,6 +1567,72 @@ class SwitchCaseIndexer {
int operator [](SwitchCase node) => index[node];
}
class ConstantIndexer extends RecursiveVisitor {
final StringIndexer stringIndexer;
final List<Constant> entries = <Constant>[];
final Map<Constant, int> index = <Constant, int>{};
ConstantIndexer(this.stringIndexer);
defaultConstantReference(Constant node) {
put(node);
}
int put(Constant constant) {
final int value = index[constant];
if (value != null) return value;
// Traverse DAG in post-order to ensure children have their id's assigned
// before the parent.
return constant.accept(this);
}
defaultConstant(Constant node) {
final int oldIndex = index[node];
if (oldIndex != null) return oldIndex;
if (node is StringConstant) {
stringIndexer.put(node.value);
} else if (node is DoubleConstant) {
stringIndexer.put('${node.value}');
} else if (node is IntConstant) {
final int value = node.value;
if ((value.abs() >> 30) != 0) {
stringIndexer.put('$value');
}
}
final int newIndex = entries.length;
entries.add(node);
return index[node] = newIndex;
}
visitMapConstant(MapConstant node) {
for (final ConstantMapEntry entry in node.entries) {
put(entry.key);
put(entry.value);
}
return defaultConstant(node);
}
visitListConstant(ListConstant node) {
for (final Constant entry in node.entries) {
put(entry);
}
return defaultConstant(node);
}
visitInstanceConstant(InstanceConstant node) {
for (final Constant entry in node.fieldValues.values) {
put(entry);
}
return defaultConstant(node);
}
int operator [](Constant node) => index[node];
}
class TypeParameterIndexer {
final Map<TypeParameter, int> index = <TypeParameter, int>{};
int stackHeight = 0;

View file

@ -113,6 +113,8 @@ class Tag {
static const int ClosureCreation = 106;
static const int ConstantExpression = 107;
static const int SpecializedTagHighBit = 0x80; // 10000000
static const int SpecializedTagMask = 0xF8; // 11111000
static const int SpecializedPayloadMask = 0x7; // 00000111
@ -130,3 +132,16 @@ class Tag {
/// Keep in sync with runtime/vm/kernel_binary.h.
static const int BinaryFormatVersion = 1;
}
abstract class ConstantTag {
static const int NullConstant = 0;
static const int BoolConstant = 1;
static const int IntConstant = 2;
static const int DoubleConstant = 3;
static const int StringConstant = 4;
static const int MapConstant = 5;
static const int ListConstant = 6;
static const int InstanceConstant = 7;
static const int TearOffConstant = 8;
static const int TypeLiteralConstant = 9;
}

View file

@ -52,6 +52,10 @@ class CloneVisitor extends TreeVisitor {
return substitute(type, typeSubstitution);
}
Constant visitConstant(Constant constant) {
return constant;
}
DartType visitOptionalType(DartType type) {
return type == null ? null : substitute(type, typeSubstitution);
}
@ -202,6 +206,10 @@ class CloneVisitor extends TreeVisitor {
return new FunctionExpression(clone(node.function));
}
visitConstantExpression(ConstantExpression node) {
return new ConstantExpression(visitConstant(node.constant));
}
visitStringLiteral(StringLiteral node) {
return new StringLiteral(node.value);
}

View file

@ -6,14 +6,35 @@ library kernel.ast_to_text;
import '../ast.dart';
import '../import_table.dart';
class Namer<T> {
abstract class Namer<T> {
int index = 0;
final String prefix;
final Map<T, String> map = <T, String>{};
Namer(this.prefix);
String getName(T key) => map.putIfAbsent(key, () => '$prefix${++index}');
String get prefix;
}
class NormalNamer<T> extends Namer<T> {
final String prefix;
NormalNamer(this.prefix);
}
class ConstantNamer extends RecursiveVisitor<Null> with Namer<Constant> {
final String prefix;
ConstantNamer(this.prefix);
String getName(Constant constant) {
if (!map.containsKey(constant)) {
// Name everything in post-order visit of DAG.
constant.visitChildren(this);
}
return super.getName(constant);
}
defaultConstantReference(Constant constant) {
getName(constant);
}
}
class Disambiguator<T> {
@ -103,12 +124,14 @@ String programToString(Program node) {
class NameSystem {
final Namer<VariableDeclaration> variables =
new Namer<VariableDeclaration>('#t');
final Namer<Member> members = new Namer<Member>('#m');
final Namer<Class> classes = new Namer<Class>('#class');
final Namer<Library> libraries = new Namer<Library>('#lib');
final Namer<TypeParameter> typeParameters = new Namer<TypeParameter>('#T');
final Namer<TreeNode> labels = new Namer<TreeNode>('#L');
new NormalNamer<VariableDeclaration>('#t');
final Namer<Member> members = new NormalNamer<Member>('#m');
final Namer<Class> classes = new NormalNamer<Class>('#class');
final Namer<Library> libraries = new NormalNamer<Library>('#lib');
final Namer<TypeParameter> typeParameters =
new NormalNamer<TypeParameter>('#T');
final Namer<TreeNode> labels = new NormalNamer<TreeNode>('#L');
final Namer<Constant> constants = new ConstantNamer('#C');
final Disambiguator<Library> prefixes = new Disambiguator<Library>();
nameVariable(VariableDeclaration node) => variables.getName(node);
@ -118,6 +141,7 @@ class NameSystem {
nameTypeParameter(TypeParameter node) => typeParameters.getName(node);
nameSwitchCase(SwitchCase node) => labels.getName(node);
nameLabeledStatement(LabeledStatement node) => labels.getName(node);
nameConstant(Constant node) => constants.getName(node);
nameLibraryPrefix(Library node, {String proposedName}) {
return prefixes.disambiguate(node, () {
@ -369,6 +393,15 @@ class Printer extends Visitor<Null> {
--inner.indentation;
endLine('}');
}
writeWord('constants ');
endLine(' {');
++inner.indentation;
for (final Constant constant
in syntheticNames.constants.map.keys.toList()) {
inner.writeNode(constant);
}
--inner.indentation;
endLine('}');
}
int getPrecedence(TreeNode node) {
@ -1646,6 +1679,55 @@ class Printer extends Visitor<Null> {
writeType(node.bound);
}
visitConstantExpression(ConstantExpression node) {
writeWord(syntheticNames.nameConstant(node.constant));
}
defaultConstant(Constant node) {
final String name = syntheticNames.nameConstant(node);
endLine(' $name = $node');
}
visitListConstant(ListConstant node) {
final String name = syntheticNames.nameConstant(node);
write(' $name = ');
final String entries = node.entries.map((Constant constant) {
return syntheticNames.nameConstant(constant);
}).join(', ');
endLine('${node.runtimeType}<${node.typeArgument}>($entries)');
}
visitMapConstant(MapConstant node) {
final String name = syntheticNames.nameConstant(node);
write(' $name = ');
final String entries = node.entries.map((ConstantMapEntry entry) {
final String key = syntheticNames.nameConstant(entry.key);
final String value = syntheticNames.nameConstant(entry.value);
return '$key: $value';
}).join(', ');
endLine(
'${node.runtimeType}<${node.keyType}, ${node.valueType}>($entries)');
}
visitInstanceConstant(InstanceConstant node) {
final String name = syntheticNames.nameConstant(node);
write(' $name = ');
final sb = new StringBuffer();
sb.write('${node.klass}');
if (!node.klass.typeParameters.isEmpty) {
sb.write('<');
sb.write(node.typeArguments.map((type) => type.toString()).join(', '));
sb.write('>');
}
sb.write(' {');
node.fieldValues.forEach((Reference fieldRef, Constant constant) {
final String name = syntheticNames.nameConstant(constant);
sb.write('${fieldRef.asField.name}: $name, ');
});
sb.write('}');
endLine(sb.toString());
}
defaultNode(Node node) {
write('<${node.runtimeType}>');
}

View file

@ -0,0 +1,813 @@
// Copyright (c) 2017, 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.transformations.constants;
import '../kernel.dart';
import '../ast.dart';
import '../type_algebra.dart';
import 'treeshaker.dart' show findNativeName;
Program transformProgram(Program program, ConstantsBackend backend,
{bool keepFields: false}) {
transformLibraries(program.libraries, backend, keepFields: keepFields);
return program;
}
void transformLibraries(List<Library> libraries, ConstantsBackend backend,
{bool keepFields: false, bool keepVariables: false}) {
final ConstantsTransformer constantsTransformer =
new ConstantsTransformer(backend, keepFields, keepVariables);
for (final Library library in libraries) {
for (final Field field in library.fields.toList()) {
constantsTransformer.convertField(field);
}
for (final Procedure procedure in library.procedures) {
constantsTransformer.convertProcedure(procedure);
}
for (final Class klass in library.classes) {
constantsTransformer.convertClassAnnotations(klass);
for (final Field field in klass.fields.toList()) {
constantsTransformer.convertField(field);
}
for (final Procedure procedure in klass.procedures) {
constantsTransformer.convertProcedure(procedure);
}
for (final Constructor constructor in klass.constructors) {
constantsTransformer.convertConstructor(constructor);
}
}
}
}
class ConstantsTransformer extends Transformer {
final ConstantEvaluator constantEvaluator;
/// Whether to preserve constant [Field]s. All use-sites will be rewritten.
final bool keepFields;
final bool keepVariables;
ConstantsTransformer(
ConstantsBackend backend, this.keepFields, this.keepVariables)
: constantEvaluator = new ConstantEvaluator(backend);
// Transform the library/class members:
void convertClassAnnotations(Class klass) {
constantEvaluator.withNewEnvironment(() {
transformList(klass.annotations, this, klass);
});
}
void convertProcedure(Procedure procedure) {
constantEvaluator.withNewEnvironment(() {
procedure.accept(this);
});
}
void convertConstructor(Constructor constructor) {
constantEvaluator.withNewEnvironment(() {
constructor.accept(this);
});
}
void convertField(Field field) {
constantEvaluator.withNewEnvironment(() {
if (field.accept(this) == null) field.remove();
});
}
// Handle definition of constants:
visitVariableDeclaration(VariableDeclaration node) {
if (node.isConst) {
final Constant constant = constantEvaluator.evaluate(node.initializer);
constantEvaluator.env.addVariableValue(node, constant);
if (keepVariables) {
// So the value of the variable is still available for debugging
// purposes we convert the constant variable to be a final variable
// initialized to the evaluated constant expression.
node.initializer = new ConstantExpression(constant)..parent = node;
node.isFinal = true;
node.isConst = false;
} else {
// Since we convert all use-sites of constants, the constant
// [VariableDeclaration] is unused and we'll therefore remove it.
return null;
}
}
return super.visitVariableDeclaration(node);
}
visitField(Field node) {
if (node.isConst) {
// Since we convert all use-sites of constants, the constant [Field]
// cannot be referenced anymore. We therefore get rid of it if
// [keepFields] was not specified.
if (!keepFields) return null;
// Otherwise we keep the constant [Field] and convert it's initializer.
if (node.initializer != null) {
final Constant constant = constantEvaluator.evaluate(node.initializer);
node.initializer = new ConstantExpression(constant)..parent = node;
}
return node;
}
return super.visitField(node);
}
// Handle use-sites of constants (and "inline" constant expressions):
visitStaticGet(StaticGet node) {
final Member target = node.target;
if (target is Field && target.isConst) {
final Constant constant = constantEvaluator.evaluate(target.initializer);
return new ConstantExpression(constant);
} else if (target is Procedure && target.kind == ProcedureKind.Method) {
final Constant constant = constantEvaluator.evaluate(node);
return new ConstantExpression(constant);
}
return super.visitStaticGet(node);
}
visitVariableGet(VariableGet node) {
if (node.variable.isConst) {
final Constant constant =
constantEvaluator.evaluate(node.variable.initializer);
return new ConstantExpression(constant);
}
return super.visitVariableGet(node);
}
visitListLiteral(ListLiteral node) {
if (node.isConst) {
return new ConstantExpression(constantEvaluator.evaluate(node));
}
return super.visitListLiteral(node);
}
visitMapLiteral(MapLiteral node) {
if (node.isConst) {
return new ConstantExpression(constantEvaluator.evaluate(node));
}
return super.visitMapLiteral(node);
}
visitConstructorInvocation(ConstructorInvocation node) {
if (node.isConst) {
return new ConstantExpression(constantEvaluator.evaluate(node));
}
return super.visitConstructorInvocation(node);
}
}
class ConstantEvaluator extends RecursiveVisitor {
final ConstantsBackend backend;
final Map<Constant, Constant> canonicalizationCache;
final Map<Node, Constant> nodeCache;
final NullConstant nullConstant = new NullConstant();
final BoolConstant trueConstant = new BoolConstant(true);
final BoolConstant falseConstant = new BoolConstant(false);
InstanceBuilder instanceBuilder;
EvaluationEnvironment env;
ConstantEvaluator(this.backend)
: canonicalizationCache = <Constant, Constant>{},
nodeCache = <Node, Constant>{};
/// Evaluates [node] and possibly cache the evaluation result.
Constant evaluate(Expression node) {
if (node == null) return nullConstant;
if (env.isEmpty) {
return nodeCache.putIfAbsent(node, () => node.accept(this));
}
return node.accept(this);
}
defaultTreeNode(Node node) {
// Only a subset of the expression language is valid for constant
// evaluation.
throw new ConstantEvaluationError(
'Constant evaluation has no support for ${node.runtimeType} yet!');
}
visitNullLiteral(NullLiteral node) => nullConstant;
visitBoolLiteral(BoolLiteral node) {
return node.value ? trueConstant : falseConstant;
}
visitIntLiteral(IntLiteral node) {
return canonicalize(new IntConstant(node.value));
}
visitDoubleLiteral(DoubleLiteral node) {
return canonicalize(new DoubleConstant(node.value));
}
visitStringLiteral(StringLiteral node) {
return canonicalize(new StringConstant(node.value));
}
visitTypeLiteral(TypeLiteral node) {
final DartType type = evaluateDartType(node.type);
return canonicalize(new TypeLiteralConstant(type));
}
visitConstantExpression(ConstantExpression node) {
// If there were already constants in the AST then we make sure we
// re-canonicalize them. After running the transformer we will therefore
// have a fully-canonicalized constant DAG with roots coming from the
// [ConstantExpression] nodes in the AST.
return canonicalize(node.constant);
}
visitListLiteral(ListLiteral node) {
final List<Constant> entries = new List<Constant>(node.expressions.length);
for (int i = 0; i < node.expressions.length; ++i) {
entries[i] = node.expressions[i].accept(this);
}
final DartType typeArgument = evaluateDartType(node.typeArgument);
final ListConstant listConstant = new ListConstant(typeArgument, entries);
return canonicalize(backend.lowerListConstant(listConstant));
}
visitMapLiteral(MapLiteral node) {
final Set<Constant> usedKeys = new Set<Constant>();
final List<ConstantMapEntry> entries =
new List<ConstantMapEntry>(node.entries.length);
for (int i = 0; i < node.entries.length; ++i) {
final key = node.entries[i].key.accept(this);
final value = node.entries[i].value.accept(this);
if (!usedKeys.add(key)) {
throw new ConstantEvaluationError(
'Duplicate key "$key" in constant map literal.');
}
entries[i] = new ConstantMapEntry(key, value);
}
final DartType keyType = evaluateDartType(node.keyType);
final DartType valueType = evaluateDartType(node.valueType);
final MapConstant mapConstant =
new MapConstant(keyType, valueType, entries);
return canonicalize(backend.lowerMapConstant(mapConstant));
}
visitConstructorInvocation(ConstructorInvocation node) {
final Constructor constructor = node.target;
final Class klass = constructor.enclosingClass;
final typeArguments = evaluateTypeArguments(node.arguments);
final positionals = evaluatePositionalArguments(node.arguments);
final named = evaluateNamedArguments(node.arguments);
// Fill in any missing type arguments with "dynamic".
for (int i = typeArguments.length; i < klass.typeParameters.length; i++) {
typeArguments.add(const DynamicType());
}
// Start building a new instance.
return withNewInstanceBuilder(klass, typeArguments, () {
// "Run" the constructor (and any super constructor calls), which will
// initialize the fields of the new instance.
handleConstructorInvocation(
constructor, typeArguments, positionals, named);
return canonicalize(instanceBuilder.buildInstance());
});
}
void handleConstructorInvocation(
Constructor constructor,
List<DartType> typeArguments,
List<Constant> positionalArguments,
Map<String, Constant> namedArguments) {
return withNewEnvironment(() {
final Class klass = constructor.enclosingClass;
final FunctionNode function = constructor.function;
// We simulate now the constructor invocation.
// Step 1) Map type arguments and normal arguments from caller to callee.
for (int i = 0; i < klass.typeParameters.length; i++) {
env.addTypeParameterValue(klass.typeParameters[i], typeArguments[i]);
}
for (int i = 0; i < function.positionalParameters.length; i++) {
final VariableDeclaration parameter = function.positionalParameters[i];
final Constant value = (i < positionalArguments.length)
? positionalArguments[i]
: evaluate(parameter.initializer);
env.addVariableValue(parameter, value);
}
for (final VariableDeclaration parameter in function.namedParameters) {
final Constant value =
namedArguments[parameter.name] ?? evaluate(parameter.initializer);
env.addVariableValue(parameter, value);
}
// Step 2) Run all initializers (including super calls) with environment setup.
for (final Field field in klass.fields) {
if (!field.isStatic) {
instanceBuilder.setFieldValue(field, evaluate(field.initializer));
}
}
for (final Initializer init in constructor.initializers) {
if (init is FieldInitializer) {
instanceBuilder.setFieldValue(init.field, evaluate(init.value));
} else if (init is LocalInitializer) {
final VariableDeclaration variable = init.variable;
env.addVariableValue(variable, evaluate(variable.initializer));
} else if (init is SuperInitializer) {
handleConstructorInvocation(
init.target,
evaluateSuperTypeArguments(constructor.enclosingClass.supertype),
evaluatePositionalArguments(init.arguments),
evaluateNamedArguments(init.arguments));
} else if (init is RedirectingInitializer) {
// Since a redirecting constructor targets a constructor of the same
// class, we pass the same [typeArguments].
handleConstructorInvocation(
init.target,
typeArguments,
evaluatePositionalArguments(init.arguments),
evaluateNamedArguments(init.arguments));
} else {
throw new ConstantEvaluationError(
'Cannot evaluate constant with [${init.runtimeType}].');
}
}
});
}
visitMethodInvocation(MethodInvocation node) {
// We have no support for generic method invocation atm.
assert(node.arguments.named.isEmpty);
final Constant receiver = evaluate(node.receiver);
final List<Constant> arguments =
evaluatePositionalArguments(node.arguments);
// Handle == and != first (it's common between all types).
if (arguments.length == 1 && node.name.name == '==') {
ensurePrimitiveConstant(receiver);
final right = arguments[0];
ensurePrimitiveConstant(right);
return receiver == right ? trueConstant : falseConstant;
}
if (arguments.length == 1 && node.name.name == '!=') {
ensurePrimitiveConstant(receiver);
final right = arguments[0];
ensurePrimitiveConstant(right);
return receiver != right ? trueConstant : falseConstant;
}
// This is a white-listed set of methods we need to support on constants.
if (receiver is StringConstant) {
if (arguments.length == 1) {
switch (node.name.name) {
case '+':
final StringConstant other = arguments[0];
return canonicalize(
new StringConstant(receiver.value + other.value));
}
}
} else if (receiver is BoolConstant) {
if (arguments.length == 1) {
switch (node.name.name) {
case '!':
return !receiver.value ? trueConstant : falseConstant;
}
} else if (arguments.length == 2) {
final right = arguments[0];
if (right is BoolConstant) {
switch (node.name.name) {
case '&&':
return (receiver.value && right.value)
? trueConstant
: falseConstant;
case '||':
return (receiver.value || right.value)
? trueConstant
: falseConstant;
}
}
throw new ConstantEvaluationError(
'Method "${node.name}" is only allowed with boolean arguments.');
}
} else if (receiver is IntConstant) {
if (arguments.length == 0) {
switch (node.name.name) {
case 'unary-':
return canonicalize(new IntConstant(-receiver.value));
case '~':
return canonicalize(new IntConstant(~receiver.value));
}
} else if (arguments.length == 1) {
final Constant other = arguments[0];
if (other is IntConstant) {
switch (node.name.name) {
case '|':
return canonicalize(
new IntConstant(receiver.value | other.value));
case '&':
return canonicalize(
new IntConstant(receiver.value & other.value));
case '^':
return canonicalize(
new IntConstant(receiver.value ^ other.value));
case '<<':
return canonicalize(
new IntConstant(receiver.value << other.value));
case '>>':
return canonicalize(
new IntConstant(receiver.value >> other.value));
}
}
if (other is IntConstant || other is DoubleConstant) {
final num value = (other is IntConstant)
? other.value
: (other as DoubleConstant).value;
return evaluateBinaryNumericOperation(
node.name.name, receiver.value, value);
}
}
} else if (receiver is DoubleConstant) {
if (arguments.length == 0) {
switch (node.name.name) {
case 'unary-':
return canonicalize(new DoubleConstant(-receiver.value));
}
} else if (arguments.length == 1) {
final Constant other = arguments[0];
if (other is IntConstant || other is DoubleConstant) {
final num value = (other is IntConstant)
? other.value
: (other as DoubleConstant).value;
return evaluateBinaryNumericOperation(
node.name.name, receiver.value, value);
}
}
}
throw new ConstantEvaluationError(
'Cannot evaluate general method invocation: '
'receiver: $receiver, method: ${node.name}, arguments: $arguments!');
}
visitLogicalExpression(LogicalExpression node) {
final Constant left = evaluate(node.left);
switch (node.operator) {
case '||':
if (left is BoolConstant) {
if (left.value) return trueConstant;
final Constant right = evaluate(node.right);
if (right is BoolConstant) {
return right;
}
throw new ConstantEvaluationError(
'"$right" is not bool constant and is disallowed with "||".');
}
throw new ConstantEvaluationError(
'"$left" is not bool constant and is disallowed with "||".');
case '&&':
if (left is BoolConstant) {
if (!left.value) return falseConstant;
final Constant right = evaluate(node.right);
if (right is BoolConstant) {
return right;
}
throw new ConstantEvaluationError(
'"$right" is not bool constant and is disallowed with "&&".');
}
throw new ConstantEvaluationError(
'"$left" is not bool constant and is disallowed with "&&".');
case '??':
return (left is! NullConstant) ? left : evaluate(node.right);
default:
throw new ConstantEvaluationError(
'No support for logical operator ${node.operator}.');
}
}
visitConditionalExpression(ConditionalExpression node) {
final BoolConstant constant = evaluate(node.condition);
if (constant == trueConstant) {
return evaluate(node.then);
} else {
assert(constant == falseConstant);
return evaluate(node.otherwise);
}
}
visitPropertyGet(PropertyGet node) {
if (node.receiver is ThisExpression) {
// Access "this" during instance creation.
for (final Field field in instanceBuilder.fields.keys) {
if (field.name == node.name) {
return instanceBuilder.fields[field];
}
}
throw 'Could not evaluate field get ${node.name} on incomplete instance';
}
final Constant receiver = evaluate(node.receiver);
if (receiver is StringConstant && node.name.name == 'length') {
return canonicalize(new IntConstant(receiver.value.length));
} else if (receiver is InstanceConstant) {
for (final Reference fieldRef in receiver.fieldValues.keys) {
if (fieldRef.asField.name == node.name) {
return receiver.fieldValues[fieldRef];
}
}
}
throw 'Could not evaluate property get on $receiver.';
}
visitLet(Let node) {
env.addVariableValue(node.variable, evaluate(node.variable.initializer));
return node.body.accept(this);
}
visitVariableGet(VariableGet node) {
return env.lookupVariable(node.variable);
}
visitStaticGet(StaticGet node) {
return withNewEnvironment(() {
final Member target = node.target;
if (target is Field && target.isConst) {
return evaluate(target.initializer);
} else if (target is Procedure) {
return canonicalize(new TearOffConstant(target));
}
throw 'Could not handle static get of $target.';
});
}
visitStringConcatenation(StringConcatenation node) {
final String value = node.expressions.map((Expression node) {
final Constant constant = node.accept(this);
if (constant is NullConstant) {
return 'null';
} else if (constant is BoolConstant) {
return constant.value ? 'true' : 'false';
} else if (constant is IntConstant) {
return constant.value.toString();
} else if (constant is DoubleConstant) {
return constant.value.toString();
} else if (constant is StringConstant) {
return constant.value;
} else {
throw new ConstantEvaluationError(
'Only null/bool/int/double/String values are allowed as string '
'interpolation expressions during constant evaluation.');
}
}).join('');
return canonicalize(new StringConstant(value));
}
visitStaticInvocation(StaticInvocation node) {
final Member target = node.target;
if (target is Procedure) {
if (target.kind == ProcedureKind.Factory) {
final String nativeName = findNativeName(target);
if (nativeName != null) {
final Constant constant = backend.buildConstantForNative(
nativeName,
evaluateTypeArguments(node.arguments),
evaluatePositionalArguments(node.arguments),
evaluateNamedArguments(node.arguments));
assert(constant != null);
return canonicalize(constant);
}
} else if (target.name.name == 'identical') {
// Ensure the "identical()" function comes from dart:core.
final parent = target.parent;
if (parent is Library && parent.importUri == 'dart:core') {
final positionalArguments =
evaluatePositionalArguments(node.arguments);
final left = positionalArguments[0];
ensurePrimitiveConstant(left);
final right = positionalArguments[1];
ensurePrimitiveConstant(right);
return left == right ? trueConstant : falseConstant;
}
}
}
throw new ConstantEvaluationError(
'Calling "$target" during constant evaluation is disallowed.');
}
// Helper methods:
List<DartType> evaluateTypeArguments(Arguments arguments) {
return evaluateDartTypes(arguments.types);
}
List<DartType> evaluateSuperTypeArguments(Supertype type) {
return evaluateDartTypes(type.typeArguments);
}
List<DartType> evaluateDartTypes(List<DartType> types) {
if (env.isEmpty) return types;
return types.map(evaluateDartType).toList();
}
DartType evaluateDartType(DartType type) {
return env.subsituteType(type);
}
List<Constant> evaluatePositionalArguments(Arguments arguments) {
return arguments.positional.map((Expression node) {
return node.accept(this);
}).toList();
}
Map<String, Constant> evaluateNamedArguments(Arguments arguments) {
if (arguments.named.isEmpty) return const <String, Constant>{};
final Map<String, Constant> named = {};
arguments.named.forEach((NamedExpression pair) {
named[pair.name] = pair.value.accept(this);
});
return named;
}
canonicalize(Constant constant) {
return canonicalizationCache.putIfAbsent(constant, () => constant);
}
withNewInstanceBuilder(Class klass, List<DartType> typeArguments, fn()) {
InstanceBuilder old = instanceBuilder;
instanceBuilder = new InstanceBuilder(klass, typeArguments);
final result = fn();
instanceBuilder = old;
return result;
}
withNewEnvironment(fn()) {
final EvaluationEnvironment oldEnv = env;
env = new EvaluationEnvironment();
final result = fn();
env = oldEnv;
return result;
}
ensurePrimitiveConstant(Constant value) {
if (value is! NullConstant &&
value is! BoolConstant &&
value is! IntConstant &&
value is! DoubleConstant &&
value is! StringConstant) {
throw new ConstantEvaluationError(
'"$value" is not a primitive constant (null/bool/int/double/string) '
' and is disallowed in this context.');
}
}
evaluateBinaryNumericOperation(String op, num a, num b) {
num result;
switch (op) {
case '+':
result = a + b;
break;
case '-':
result = a - b;
break;
case '*':
result = a - b;
break;
case '/':
result = a / b;
break;
case '~/':
result = a ~/ b;
break;
case '%':
result = a ~/ b;
break;
}
if (result != null) {
return canonicalize(result is int
? new IntConstant(result)
: new DoubleConstant(result as double));
}
switch (op) {
case '<':
return a < b ? trueConstant : falseConstant;
case '<=':
return a <= b ? trueConstant : falseConstant;
case '>=':
return a >= b ? trueConstant : falseConstant;
case '>':
return a > b ? trueConstant : falseConstant;
}
throw new ConstantEvaluationError(
'Binary operation "$op" on num is disallowed.');
}
}
/// Holds the necessary information for a constant object, namely
/// * the [klass] being instantiated
/// * the [typeArguments] used for the instantiation
/// * the [fields] the instance will obtain (all fields from the
/// instantiated [klass] up to the [Object] klass).
class InstanceBuilder {
/// The class of the new instance.
final Class klass;
/// The values of the type parameters of the new instance.
final List<DartType> typeArguments;
/// The field values of the new instance.
final Map<Field, Constant> fields = <Field, Constant>{};
InstanceBuilder(this.klass, this.typeArguments);
void setFieldValue(Field field, Constant constant) {
fields[field] = constant;
}
InstanceConstant buildInstance() {
final Map<Reference, Constant> fieldValues = <Reference, Constant>{};
fields.forEach((Field field, Constant value) {
fieldValues[field.reference] = value;
});
return new InstanceConstant(klass.reference, typeArguments, fieldValues);
}
}
/// Holds an environment of type parameters, parameters and variables.
class EvaluationEnvironment {
/// The values of the type parameters in scope.
final Map<TypeParameter, DartType> _typeVariables =
<TypeParameter, DartType>{};
/// The values of the parameters/variables in scope.
final Map<VariableDeclaration, Constant> _variables =
<VariableDeclaration, Constant>{};
/// Whether the current environment is empty.
bool get isEmpty => _typeVariables.isEmpty && _variables.isEmpty;
void addTypeParameterValue(TypeParameter parameter, DartType value) {
assert(!_typeVariables.containsKey(parameter));
_typeVariables[parameter] = value;
}
void addVariableValue(VariableDeclaration variable, Constant value) {
assert(!_variables.containsKey(variable));
_variables[variable] = value;
}
DartType lookupParameterValue(TypeParameter parameter) {
final DartType value = _typeVariables[parameter];
assert(value != null);
return value;
}
Constant lookupVariable(VariableDeclaration variable) {
final Constant value = _variables[variable];
assert(value != null);
return value;
}
DartType subsituteType(DartType type) {
if (_typeVariables.isEmpty) return type;
return substitute(type, _typeVariables);
}
}
abstract class ConstantsBackend {
Constant buildConstantForNative(
String nativeName,
List<DartType> typeArguments,
List<Constant> positionalArguments,
Map<String, Constant> namedArguments);
Constant lowerListConstant(ListConstant constant);
Constant lowerMapConstant(MapConstant constant);
}
/// Represents a compile-time error reported during constant evaluation.
class ConstantEvaluationError {
final String message;
ConstantEvaluationError(this.message);
String toString() => 'Error during constant evaluation: $message';
}

View file

@ -1087,3 +1087,28 @@ class _ExternalTypeVisitor extends DartTypeVisitor {
/// Exception that is thrown to stop the tree shaking analysis when a use
/// of `dart:mirrors` is found.
class _UsingMirrorsException {}
String findNativeName(Member procedure) {
// Native procedures are marked as external and have an annotation,
// which looks like this:
//
// import 'dart:_internal' as internal;
//
// @internal.ExternalName("<name-of-native>")
// external Object foo(arg0, ...);
//
if (procedure.isExternal) {
for (final Expression annotation in procedure.annotations) {
if (annotation is ConstructorInvocation) {
final Class klass = annotation.target.enclosingClass;
if (klass.name == 'ExternalName' &&
klass.enclosingLibrary.importUri.toString() == 'dart:_internal') {
assert(annotation.arguments.positional.length == 1);
return (annotation.arguments.positional[0] as StringLiteral).value;
}
}
}
throw 'External procedure has no @ExternalName("...") annotation!';
}
return null;
}

View file

@ -995,4 +995,11 @@ class TypeCheckingVisitor
@override
visitInvalidInitializer(InvalidInitializer node) {}
@override
visitConstantExpression(ConstantExpression node) {
// Without explicitly running the "constants" transformation, we should
// never get here!
throw 'unreachable';
}
}

View file

@ -382,7 +382,15 @@ class VerifyingVisitor extends RecursiveVisitor {
if (node.target == null) {
problem(node, "StaticGet without target.");
}
if (!node.target.hasGetter) {
// Currently Constructor.hasGetter returns `false` even though fasta uses it
// as a getter for internal purposes:
//
// Fasta is letting all call site of a redirecting constructor be resolved
// to the real target. In order to resolve it, it seems to add a body into
// the redirecting-factory constructor which caches the target constructor.
// That cache is via a `StaticGet(real-constructor)` node, which we make
// here pass the verifier.
if (!node.target.hasGetter && node.target is! Constructor) {
problem(node, "StaticGet of '${node.target}' without getter.");
}
if (node.target.isInstanceMember) {

View file

@ -47,6 +47,7 @@ abstract class ExpressionVisitor<R> {
R visitMapLiteral(MapLiteral node) => defaultExpression(node);
R visitAwaitExpression(AwaitExpression node) => defaultExpression(node);
R visitFunctionExpression(FunctionExpression node) => defaultExpression(node);
R visitConstantExpression(ConstantExpression node) => defaultExpression(node);
R visitStringLiteral(StringLiteral node) => defaultBasicLiteral(node);
R visitIntLiteral(IntLiteral node) => defaultBasicLiteral(node);
R visitDoubleLiteral(DoubleLiteral node) => defaultBasicLiteral(node);
@ -167,6 +168,7 @@ class TreeVisitor<R>
R visitMapLiteral(MapLiteral node) => defaultExpression(node);
R visitAwaitExpression(AwaitExpression node) => defaultExpression(node);
R visitFunctionExpression(FunctionExpression node) => defaultExpression(node);
R visitConstantExpression(ConstantExpression node) => defaultExpression(node);
R visitStringLiteral(StringLiteral node) => defaultBasicLiteral(node);
R visitIntLiteral(IntLiteral node) => defaultBasicLiteral(node);
R visitDoubleLiteral(DoubleLiteral node) => defaultBasicLiteral(node);
@ -258,6 +260,21 @@ class DartTypeVisitor<R> {
R visitTypedefType(TypedefType node) => defaultDartType(node);
}
class ConstantVisitor<R> {
R defaultConstant(Constant node) => null;
R visitNullConstant(NullConstant node) => defaultConstant(node);
R visitBoolConstant(BoolConstant node) => defaultConstant(node);
R visitIntConstant(IntConstant node) => defaultConstant(node);
R visitDoubleConstant(DoubleConstant node) => defaultConstant(node);
R visitStringConstant(StringConstant node) => defaultConstant(node);
R visitMapConstant(MapConstant node) => defaultConstant(node);
R visitListConstant(ListConstant node) => defaultConstant(node);
R visitInstanceConstant(InstanceConstant node) => defaultConstant(node);
R visitTearOffConstant(TearOffConstant node) => defaultConstant(node);
R visitTypeLiteralConstant(TypeLiteralConstant node) => defaultConstant(node);
}
class MemberReferenceVisitor<R> {
const MemberReferenceVisitor();
@ -269,7 +286,10 @@ class MemberReferenceVisitor<R> {
}
class Visitor<R> extends TreeVisitor<R>
implements DartTypeVisitor<R>, MemberReferenceVisitor<R> {
implements
DartTypeVisitor<R>,
ConstantVisitor<R>,
MemberReferenceVisitor<R> {
const Visitor();
/// The catch-all case, except for references.
@ -288,10 +308,46 @@ class Visitor<R> extends TreeVisitor<R>
R visitTypeParameterType(TypeParameterType node) => defaultDartType(node);
R visitTypedefType(TypedefType node) => defaultDartType(node);
// Constants
R defaultConstant(Constant node) => defaultNode(node);
R visitNullConstant(NullConstant node) => defaultConstant(node);
R visitBoolConstant(BoolConstant node) => defaultConstant(node);
R visitIntConstant(IntConstant node) => defaultConstant(node);
R visitDoubleConstant(DoubleConstant node) => defaultConstant(node);
R visitStringConstant(StringConstant node) => defaultConstant(node);
R visitMapConstant(MapConstant node) => defaultConstant(node);
R visitListConstant(ListConstant node) => defaultConstant(node);
R visitInstanceConstant(InstanceConstant node) => defaultConstant(node);
R visitTearOffConstant(TearOffConstant node) => defaultConstant(node);
R visitTypeLiteralConstant(TypeLiteralConstant node) => defaultConstant(node);
// Class references
R visitClassReference(Class node) => null;
R visitTypedefReference(Typedef node) => null;
// Constant references
R defaultConstantReference(Constant node) => null;
R visitNullConstantReference(NullConstant node) =>
defaultConstantReference(node);
R visitBoolConstantReference(BoolConstant node) =>
defaultConstantReference(node);
R visitIntConstantReference(IntConstant node) =>
defaultConstantReference(node);
R visitDoubleConstantReference(DoubleConstant node) =>
defaultConstantReference(node);
R visitStringConstantReference(StringConstant node) =>
defaultConstantReference(node);
R visitMapConstantReference(MapConstant node) =>
defaultConstantReference(node);
R visitListConstantReference(ListConstant node) =>
defaultConstantReference(node);
R visitInstanceConstantReference(InstanceConstant node) =>
defaultConstantReference(node);
R visitTearOffConstantReference(TearOffConstant node) =>
defaultConstantReference(node);
R visitTypeLiteralConstantReference(TypeLiteralConstant node) =>
defaultConstantReference(node);
// Member references
R defaultMemberReference(Member node) => null;
R visitFieldReference(Field node) => defaultMemberReference(node);
@ -344,6 +400,8 @@ class Transformer extends TreeVisitor<TreeNode> {
/// By default, recursion stops at this point.
DartType visitDartType(DartType node) => node;
Constant visitConstant(Constant node) => node;
Supertype visitSupertype(Supertype node) => node;
TreeNode defaultTreeNode(TreeNode node) {
@ -398,6 +456,8 @@ abstract class ExpressionVisitor1<R, T> {
R visitTypeLiteral(TypeLiteral node, T arg) => defaultExpression(node, arg);
R visitThisExpression(ThisExpression node, T arg) =>
defaultExpression(node, arg);
R visitConstantExpression(ConstantExpression node, arg) =>
defaultExpression(node, arg);
R visitRethrow(Rethrow node, T arg) => defaultExpression(node, arg);
R visitThrow(Throw node, T arg) => defaultExpression(node, arg);
R visitListLiteral(ListLiteral node, T arg) => defaultExpression(node, arg);
@ -406,9 +466,9 @@ abstract class ExpressionVisitor1<R, T> {
defaultExpression(node, arg);
R visitFunctionExpression(FunctionExpression node, T arg) =>
defaultExpression(node, arg);
R visitIntLiteral(IntLiteral node, T arg) => defaultBasicLiteral(node, arg);
R visitStringLiteral(StringLiteral node, T arg) =>
defaultBasicLiteral(node, arg);
R visitIntLiteral(IntLiteral node, T arg) => defaultBasicLiteral(node, arg);
R visitDoubleLiteral(DoubleLiteral node, T arg) =>
defaultBasicLiteral(node, arg);
R visitBoolLiteral(BoolLiteral node, T arg) => defaultBasicLiteral(node, arg);

View file

@ -0,0 +1,84 @@
// Copyright (c) 2017, 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 vm.constants_native_effects;
import '../ast.dart';
import '../transformations/constants.dart';
import '../core_types.dart';
class VmConstantsBackend implements ConstantsBackend {
final Class immutableMapClass;
VmConstantsBackend._(this.immutableMapClass);
factory VmConstantsBackend(CoreTypes coreTypes) {
final Library coreLibrary = coreTypes.coreLibrary;
final Class immutableMapClass = coreLibrary.classes
.firstWhere((Class klass) => klass.name == '_ImmutableMap');
assert(immutableMapClass != null);
return new VmConstantsBackend._(immutableMapClass);
}
Constant buildConstantForNative(
String nativeName,
List<DartType> typeArguments,
List<Constant> positionalArguments,
Map<String, Constant> namedArguments) {
switch (nativeName) {
case 'Bool_fromEnvironment':
final String name = (positionalArguments[0] as StringConstant).value;
final Constant constant = namedArguments['defaultValue'];
final bool defaultValue =
constant is BoolConstant ? constant.value : false;
return new BoolConstant(
new bool.fromEnvironment(name, defaultValue: defaultValue));
case 'Integer_fromEnvironment':
final String name = (positionalArguments[0] as StringConstant).value;
final Constant constant = namedArguments['defaultValue'];
final int defaultValue =
constant is IntConstant ? constant.value : null;
final int value =
new int.fromEnvironment(name, defaultValue: defaultValue);
return value != null ? new IntConstant(value) : new NullConstant();
case 'String_fromEnvironment':
final String name = (positionalArguments[0] as StringConstant).value;
final Constant constant = namedArguments['defaultValue'];
final String defaultValue =
constant is StringConstant ? constant.value : null;
final String value =
new String.fromEnvironment(name, defaultValue: defaultValue);
return value == null ? new NullConstant() : new StringConstant(value);
}
throw 'No native effect registered for constant evaluation: $nativeName';
}
Constant lowerMapConstant(MapConstant constant) {
// The _ImmutableMap class is implemented via one field pointing to a list
// of key/value pairs -- see runtime/lib/immutable_map.dart!
final List<Constant> kvListPairs =
new List<Constant>(2 * constant.entries.length);
for (int i = 0; i < constant.entries.length; i++) {
final ConstantMapEntry entry = constant.entries[i];
kvListPairs[2 * i] = entry.key;
kvListPairs[2 * i + 1] = entry.value;
}
// Strong mode is a bit fishy here, since we merge the key and the value
// type by putting both into the same list!
final kvListConstant = new ListConstant(const DynamicType(), kvListPairs);
assert(immutableMapClass.fields.length == 1);
final Field kvPairListField = immutableMapClass.fields[0];
return new InstanceConstant(immutableMapClass.reference, <DartType>[
constant.keyType,
constant.valueType,
], <Reference, Constant>{
kvPairListField.reference: kvListConstant,
});
}
Constant lowerListConstant(ListConstant constant) {
// Currently we let vipunen deal with the [ListConstant]s.
return constant;
}
}

View file

@ -50,6 +50,7 @@ front_end/test/fasta/strong_test: Pass, Slow
front_end/test/fasta/outline_test: Pass, Slow
front_end/test/fasta/ast_builder_test: Pass, Slow
front_end/tool/incremental_perf_test: Slow, Pass
front_end/test/whole_program_test: Slow, Pass
# These are not tests but input for tests.
kernel/testcases/*: Skip
@ -61,6 +62,9 @@ kernel/test/closures_test: Slow, Pass
front_end/tool/_fasta/compile_platform_test: Fail
[ $use_sdk || $mode != release || $compiler != none || $runtime != vm || $arch != x64 ]
front_end/test/whole_program_test: SkipByDesign
[ $compiler != dart2analyzer ]
analyzer/test/src/summary/summarize_fasta_test: RuntimeError, Slow

View file

@ -1396,6 +1396,10 @@ void StreamingScopeBuilder::VisitExpression() {
VisitDartType(); // read function type of the closure.
builder_->SkipListOfDartTypes(); // read type arguments.
return;
case kConstantExpression: {
builder_->SkipConstantReference();
return;
}
default:
H.ReportError("Unsupported tag at this point: %d.", tag);
UNREACHABLE();
@ -2603,6 +2607,9 @@ Instance& StreamingConstantEvaluator::EvaluateExpression(intptr_t offset,
case kNullLiteral:
EvaluateNullLiteral();
break;
case kConstantExpression:
EvaluateConstantExpression();
break;
default:
H.ReportError(script_, TokenPosition::kNoSource,
"Not a constant expression.");
@ -3194,6 +3201,10 @@ void StreamingConstantEvaluator::EvaluateNullLiteral() {
result_ = Instance::null();
}
void StreamingConstantEvaluator::EvaluateConstantExpression() {
result_ ^= H.constants().At(builder_->ReadUInt());
}
// This depends on being about to read the list of positionals on arguments.
const Object& StreamingConstantEvaluator::RunFunction(
const Function& function,
@ -4398,6 +4409,8 @@ Fragment StreamingFlowGraphBuilder::BuildExpression(TokenPosition* position) {
return BuildVectorCopy(position);
case kClosureCreation:
return BuildClosureCreation(position);
case kConstantExpression:
return BuildConstantExpression(position);
default:
H.ReportError("Unsupported tag at this point: %d.", tag);
UNREACHABLE();
@ -4568,6 +4581,10 @@ void StreamingFlowGraphBuilder::SkipStringReference() {
ReadUInt();
}
void StreamingFlowGraphBuilder::SkipConstantReference() {
ReadUInt();
}
void StreamingFlowGraphBuilder::SkipCanonicalNameReference() {
ReadUInt();
}
@ -4934,6 +4951,9 @@ void StreamingFlowGraphBuilder::SkipExpression() {
return;
case kNullLiteral:
return;
case kConstantExpression:
SkipConstantReference();
return;
default:
H.ReportError("Unsupported tag at this point: %d.", tag);
UNREACHABLE();
@ -7469,6 +7489,13 @@ Fragment StreamingFlowGraphBuilder::BuildClosureCreation(
return instructions;
}
Fragment StreamingFlowGraphBuilder::BuildConstantExpression(
TokenPosition* position) {
if (position != NULL) *position = TokenPosition::kNoSource;
const intptr_t constant_index = ReadUInt();
return Constant(Object::ZoneHandle(Z, H.constants().At(constant_index)));
}
Fragment StreamingFlowGraphBuilder::BuildInvalidStatement() {
H.ReportError("Invalid statements not implemented yet!");
return Fragment();
@ -8972,6 +8999,149 @@ void StreamingFlowGraphBuilder::EnsureMetadataIsScanned() {
}
}
const Array& ConstantHelper::ReadConstantTable() {
const intptr_t number_of_constants = builder_.ReadUInt();
if (number_of_constants == 0) {
return Array::Handle(Z, Array::null());
}
const Library& corelib = Library::Handle(Z, Library::CoreLibrary());
const Class& list_class =
Class::Handle(Z, corelib.LookupClassAllowPrivate(Symbols::_List()));
// Eagerly finalize _ImmutableList (instead of doing it on every list
// constant).
temp_class_ = I->class_table()->At(kImmutableArrayCid);
temp_object_ = temp_class_.EnsureIsFinalized(H.thread());
ASSERT(temp_object_.IsNull());
const Array& constants =
Array::Handle(Z, Array::New(number_of_constants, Heap::kOld));
for (intptr_t i = 0; i < number_of_constants; ++i) {
const intptr_t constant_tag = builder_.ReadByte();
switch (constant_tag) {
case kNullConstant:
temp_instance_ = Instance::null();
break;
case kBoolConstant:
temp_instance_ = builder_.ReadByte() == 1 ? Object::bool_true().raw()
: Object::bool_false().raw();
break;
case kIntConstant: {
temp_instance_ = const_evaluator_
.EvaluateExpression(builder_.ReaderOffset(),
false /* reset position */)
.raw();
break;
}
case kDoubleConstant: {
temp_instance_ = Double::New(
H.DartString(builder_.ReadStringReference()), Heap::kOld);
temp_instance_ = H.Canonicalize(temp_instance_);
break;
}
case kStringConstant: {
temp_instance_ =
H.Canonicalize(H.DartString(builder_.ReadStringReference()));
break;
}
case kListConstant: {
temp_type_arguments_ = TypeArguments::New(1, Heap::kOld);
const AbstractType& type = type_translator_.BuildType();
temp_type_arguments_.SetTypeAt(0, type);
InstantiateTypeArguments(list_class, &temp_type_arguments_);
const intptr_t length = builder_.ReadUInt();
temp_array_ = ImmutableArray::New(length, Heap::kOld);
temp_array_.SetTypeArguments(temp_type_arguments_);
for (intptr_t j = 0; j < length; ++j) {
const intptr_t entry_index = builder_.ReadUInt();
ASSERT(entry_index < i); // We have a DAG!
temp_object_ = constants.At(entry_index);
temp_array_.SetAt(j, temp_object_);
}
temp_instance_ = H.Canonicalize(temp_array_);
break;
}
case kInstanceConstant: {
temp_class_ =
H.LookupClassByKernelClass(builder_.ReadCanonicalNameReference());
temp_object_ = temp_class_.EnsureIsFinalized(H.thread());
ASSERT(temp_object_.IsNull());
temp_instance_ = Instance::New(temp_class_, Heap::kOld);
const intptr_t number_of_type_arguments = builder_.ReadUInt();
if (number_of_type_arguments > 0) {
temp_type_arguments_ =
TypeArguments::New(number_of_type_arguments, Heap::kOld);
for (intptr_t j = 0; j < number_of_type_arguments; ++j) {
temp_type_arguments_.SetTypeAt(j, type_translator_.BuildType());
}
InstantiateTypeArguments(list_class, &temp_type_arguments_);
temp_instance_.SetTypeArguments(temp_type_arguments_);
}
const intptr_t number_of_fields = builder_.ReadUInt();
for (intptr_t j = 0; j < number_of_fields; ++j) {
temp_field_ =
H.LookupFieldByKernelField(builder_.ReadCanonicalNameReference());
const intptr_t entry_index = builder_.ReadUInt();
ASSERT(entry_index < i); // We have a DAG!
temp_object_ = constants.At(entry_index);
temp_instance_.SetField(temp_field_, temp_object_);
}
temp_instance_ = H.Canonicalize(temp_instance_);
break;
}
case kTearOffConstant: {
const NameIndex index = builder_.ReadCanonicalNameReference();
NameIndex lib_index = index;
while (!H.IsLibrary(lib_index)) {
lib_index = H.CanonicalNameParent(lib_index);
}
ASSERT(H.IsLibrary(lib_index));
if (lib_index == skip_vmservice_library_) {
temp_instance_ = Instance::null();
break;
}
temp_function_ = H.LookupStaticMethodByKernelProcedure(index);
temp_function_ = temp_function_.ImplicitClosureFunction();
temp_instance_ = temp_function_.ImplicitStaticClosure();
temp_instance_ = H.Canonicalize(temp_instance_);
break;
}
case kTypeLiteralConstant: {
temp_instance_ = type_translator_.BuildType().raw();
break;
}
case kMapConstant:
// Note: This is already lowered to InstanceConstant/ListConstant.
UNREACHABLE();
break;
default:
UNREACHABLE();
}
constants.SetAt(i, temp_instance_);
}
return constants;
}
void ConstantHelper::InstantiateTypeArguments(const Class& receiver_class,
TypeArguments* type_arguments) {
// We make a temporary [Type] object and use `ClassFinalizer::FinalizeType` to
// finalize the argument types.
// (This can for example make the [type_arguments] vector larger)
temp_type_ =
Type::New(receiver_class, *type_arguments, TokenPosition::kNoSource);
temp_type_ = ClassFinalizer::FinalizeType(*active_class_->klass, temp_type_,
ClassFinalizer::kCanonicalize);
*type_arguments = temp_type_.arguments();
}
} // namespace kernel
} // namespace dart

View file

@ -777,6 +777,7 @@ class StreamingConstantEvaluator {
void EvaluateDoubleLiteral();
void EvaluateBoolLiteral(bool value);
void EvaluateNullLiteral();
void EvaluateConstantExpression();
void EvaluateGetStringLength(intptr_t expression_offset,
TokenPosition position);
@ -948,6 +949,7 @@ class StreamingFlowGraphBuilder {
const String& ReadNameAsFieldName();
void SkipFlags();
void SkipStringReference();
void SkipConstantReference();
void SkipCanonicalNameReference();
void SkipDartType();
void SkipOptionalDartType();
@ -1179,6 +1181,7 @@ class StreamingFlowGraphBuilder {
Fragment BuildVectorSet(TokenPosition* position);
Fragment BuildVectorCopy(TokenPosition* position);
Fragment BuildClosureCreation(TokenPosition* position);
Fragment BuildConstantExpression(TokenPosition* position);
Fragment BuildInvalidStatement();
Fragment BuildExpressionStatement();
@ -1236,21 +1239,23 @@ class StreamingFlowGraphBuilder {
DirectCallMetadataHelper direct_call_metadata_helper_;
bool metadata_scanned_;
friend class ClassHelper;
friend class ConstantHelper;
friend class ConstructorHelper;
friend class DirectCallMetadataHelper;
friend class FieldHelper;
friend class FunctionNodeHelper;
friend class KernelLoader;
friend class KernelReader;
friend class LibraryDependencyHelper;
friend class LibraryHelper;
friend class MetadataHelper;
friend class ProcedureHelper;
friend class SimpleExpressionConverter;
friend class StreamingConstantEvaluator;
friend class StreamingDartTypeTranslator;
friend class StreamingScopeBuilder;
friend class FunctionNodeHelper;
friend class VariableDeclarationHelper;
friend class FieldHelper;
friend class ProcedureHelper;
friend class ClassHelper;
friend class LibraryHelper;
friend class LibraryDependencyHelper;
friend class MetadataHelper;
friend class DirectCallMetadataHelper;
friend class ConstructorHelper;
friend class SimpleExpressionConverter;
friend class KernelLoader;
};
// A helper class that saves the current reader position, goes to another reader
@ -1304,6 +1309,60 @@ class AlternativeReadingScope {
intptr_t saved_offset_;
};
// Helper class that reads a kernel Constant from binary.
class ConstantHelper {
public:
ConstantHelper(ActiveClass* active_class,
StreamingFlowGraphBuilder* builder,
StreamingDartTypeTranslator* type_translator,
TranslationHelper* translation_helper,
Zone* zone,
NameIndex skip_vmservice_library)
: skip_vmservice_library_(skip_vmservice_library),
active_class_(active_class),
builder_(*builder),
type_translator_(*type_translator),
const_evaluator_(&builder_),
translation_helper_(*translation_helper),
zone_(zone),
temp_type_(AbstractType::Handle(zone)),
temp_type_arguments_(TypeArguments::Handle(zone)),
temp_object_(Object::Handle(zone)),
temp_array_(Array::Handle(zone)),
temp_instance_(Instance::Handle(zone)),
temp_field_(Field::Handle(zone)),
temp_class_(Class::Handle(zone)),
temp_function_(Function::Handle(zone)),
temp_integer_(Integer::Handle(zone)) {}
// Reads the constant table from the binary.
//
// This method assumes the Reader is positioned already at the constant table
// and an active class scope is setup.
const Array& ReadConstantTable();
private:
void InstantiateTypeArguments(const Class& receiver_class,
TypeArguments* type_arguments);
NameIndex skip_vmservice_library_;
ActiveClass* active_class_;
StreamingFlowGraphBuilder& builder_;
StreamingDartTypeTranslator& type_translator_;
StreamingConstantEvaluator const_evaluator_;
TranslationHelper translation_helper_;
Zone* zone_;
AbstractType& temp_type_;
TypeArguments& temp_type_arguments_;
Object& temp_object_;
Array& temp_array_;
Instance& temp_instance_;
Field& temp_field_;
Class& temp_class_;
Function& temp_function_;
Integer& temp_integer_;
};
} // namespace kernel
} // namespace dart

View file

@ -153,7 +153,8 @@ TranslationHelper::TranslationHelper(Thread* thread)
string_data_(TypedData::Handle(Z)),
canonical_names_(TypedData::Handle(Z)),
metadata_payloads_(TypedData::Handle(Z)),
metadata_mappings_(TypedData::Handle(Z)) {}
metadata_mappings_(TypedData::Handle(Z)),
constants_(Array::Handle(Z)) {}
void TranslationHelper::InitFromScript(const Script& script) {
const KernelProgramInfo& info =
@ -175,6 +176,7 @@ void TranslationHelper::InitFromKernelProgramInfo(
SetCanonicalNames(TypedData::Handle(Z, info.canonical_names()));
SetMetadataPayloads(TypedData::Handle(Z, info.metadata_payloads()));
SetMetadataMappings(TypedData::Handle(Z, info.metadata_mappings()));
SetConstants(Array::Handle(Z, info.constants()));
}
void TranslationHelper::SetStringOffsets(const TypedData& string_offsets) {
@ -204,6 +206,11 @@ void TranslationHelper::SetMetadataMappings(
metadata_mappings_ = metadata_mappings.raw();
}
void TranslationHelper::SetConstants(const Array& constants) {
ASSERT(constants_.IsNull());
constants_ = constants.raw();
}
intptr_t TranslationHelper::StringOffset(StringIndex index) const {
return string_offsets_.GetUint32(index << 2);
}
@ -766,7 +773,8 @@ FlowGraphBuilder::FlowGraphBuilder(
next_used_try_index_(0),
catch_block_(NULL),
streaming_flow_graph_builder_(NULL) {
Script& script = Script::Handle(Z, parsed_function->function().script());
const Script& script =
Script::Handle(Z, parsed_function->function().script());
H.InitFromScript(script);
}
@ -1788,6 +1796,19 @@ void FlowGraphBuilder::InlineBailout(const char* reason) {
FlowGraph* FlowGraphBuilder::BuildGraph() {
const Function& function = parsed_function_->function();
#ifdef DEBUG
// If we attached the native name to the function after it's creation (namely
// after reading the constant table from the kernel blob), we must have done
// so before building flow graph for the functions (since FGB depends needs
// the native name to be there).
const Script& script = Script::Handle(Z, function.script());
const KernelProgramInfo& info =
KernelProgramInfo::Handle(script.kernel_program_info());
ASSERT(info.IsNull() ||
info.potential_natives() == GrowableObjectArray::null());
#endif
StreamingFlowGraphBuilder streaming_flow_graph_builder(
this, TypedData::Handle(Z, function.KernelData()),
function.KernelDataProgramOffset());

View file

@ -321,6 +321,9 @@ class TranslationHelper {
const TypedData& metadata_mappings() { return metadata_mappings_; }
void SetMetadataMappings(const TypedData& metadata_mappings);
const Array& constants() { return constants_; }
void SetConstants(const Array& constants);
intptr_t StringOffset(StringIndex index) const;
intptr_t StringSize(StringIndex index) const;
@ -443,6 +446,7 @@ class TranslationHelper {
TypedData& canonical_names_;
TypedData& metadata_payloads_;
TypedData& metadata_mappings_;
Array& constants_;
};
struct FunctionScope {

View file

@ -78,6 +78,7 @@ class Program {
intptr_t source_table_offset() const { return source_table_offset_; }
intptr_t string_table_offset() const { return string_table_offset_; }
intptr_t name_table_offset() const { return name_table_offset_; }
intptr_t constant_table_offset() { return constant_table_offset_; }
const uint8_t* kernel_data() { return kernel_data_; }
intptr_t kernel_data_size() { return kernel_data_size_; }
intptr_t library_count() { return library_count_; }
@ -97,6 +98,9 @@ class Program {
// The offset from the start of the binary to the start of the source table.
intptr_t source_table_offset_;
// The offset from the start of the binary to the start of the constant table.
intptr_t constant_table_offset_;
// The offset from the start of the binary to the canonical name table.
intptr_t name_table_offset_;

View file

@ -51,9 +51,13 @@ Program* Program::ReadFrom(Reader* reader, bool take_buffer_ownership) {
reader->size_, LibraryCountFieldCountFromEnd, 1, 0);
program->source_table_offset_ = reader->ReadFromIndexNoReset(
reader->size_,
LibraryCountFieldCountFromEnd + 1 + program->library_count_ + 1, 4, 0);
LibraryCountFieldCountFromEnd + 1 + program->library_count_ + 1 +
SourceTableFieldCountFromFirstLibraryOffset,
1, 0);
program->name_table_offset_ = reader->ReadUInt32();
program->string_table_offset_ = reader->ReadUInt32();
program->constant_table_offset_ = reader->ReadUInt32();
program->main_method_reference_ = NameIndex(reader->ReadUInt32() - 1);
return program;

View file

@ -21,6 +21,7 @@ namespace kernel {
static const uint32_t kMagicProgramFile = 0x90ABCDEFu;
static const uint32_t kBinaryFormatVersion = 1;
// Keep in sync with package:kernel/lib/binary/tag.dart
enum Tag {
kNothing = 0,
kSomething = 1,
@ -125,6 +126,8 @@ enum Tag {
kClosureCreation = 106,
kConstantExpression = 107,
kSpecializedTagHighBit = 0x80, // 10000000
kSpecializedTagMask = 0xF8, // 11111000
kSpecializedPayloadMask = 0x7, // 00000111
@ -134,9 +137,23 @@ enum Tag {
kSpecialIntLiteral = 144,
};
// Keep in sync with package:kernel/lib/binary/tag.dart
enum ConstantTag {
kNullConstant = 0,
kBoolConstant = 1,
kIntConstant = 2,
kDoubleConstant = 3,
kStringConstant = 4,
kMapConstant = 5,
kListConstant = 6,
kInstanceConstant = 7,
kTearOffConstant = 8,
kTypeLiteralConstant = 9,
};
static const int SpecializedIntLiteralBias = 3;
static const int LibraryCountFieldCountFromEnd = 1;
static const int SourceTableFieldCountFromFirstLibraryOffset = 3;
static const int SourceTableFieldCountFromFirstLibraryOffset = 4;
static const int HeaderSize = 8; // 'magic', 'formatVersion'.
static const int MetadataPayloadOffset = HeaderSize; // Right after header.

View file

@ -175,7 +175,10 @@ KernelLoader::KernelLoader(Program* program)
zone_,
program_->kernel_data(),
program_->kernel_data_size(),
0) {
0),
external_name_class_(Class::Handle(Z)),
external_name_field_(Field::Handle(Z)),
potential_natives_(GrowableObjectArray::Handle(Z)) {
if (!program->is_single_program()) {
FATAL(
"Trying to load a concatenated dill file at a time where that is "
@ -344,7 +347,10 @@ KernelLoader::KernelLoader(const Script& script,
kernel_program_info_(
KernelProgramInfo::ZoneHandle(zone_, script.kernel_program_info())),
translation_helper_(this, thread_),
builder_(&translation_helper_, script.raw(), zone_, kernel_data, 0) {
builder_(&translation_helper_, script.raw(), zone_, kernel_data, 0),
external_name_class_(Class::Handle(Z)),
external_name_field_(Field::Handle(Z)),
potential_natives_(GrowableObjectArray::Handle(Z)) {
T.active_class_ = &active_class_;
T.finalize_ = false;
@ -355,7 +361,84 @@ KernelLoader::KernelLoader(const Script& script,
H.InitFromKernelProgramInfo(kernel_program_info_);
}
const Array& KernelLoader::ReadConstantTable() {
// We use the very first library's toplevel class as an owner for an
// [ActiveClassScope]
//
// Though since constants cannot refer to types containing type parameter
// references, the only purpose of the class is to serve as an owner for
// signature functions (which get created for function types).
const dart::Library& owner_library = LookupLibrary(library_canonical_name(0));
const dart::Class& toplevel_class =
Class::Handle(Z, owner_library.toplevel_class());
ActiveClassScope active_class_scope(&active_class_, &toplevel_class);
builder_.SetOffset(program_->constant_table_offset());
StreamingDartTypeTranslator type_translator_(&builder_, true /* finalize */);
type_translator_.active_class_ = &active_class_;
ConstantHelper helper(&active_class_, &builder_, &type_translator_,
&translation_helper_, Z, skip_vmservice_library_);
return helper.ReadConstantTable();
}
void KernelLoader::AnnotateNativeProcedures(const Array& constant_table) {
potential_natives_ = kernel_program_info_.potential_natives();
const intptr_t length =
!potential_natives_.IsNull() ? potential_natives_.Length() : 0;
if (length > 0) {
// Obtain `dart:_internal::ExternalName.name`.
EnsureExternalClassIsLookedUp();
Instance& constant = Instance::Handle(Z);
String& native_name = String::Handle(Z);
// Start scanning all candidates in [potential_natives] for the annotation
// constant. If the annotation is found, flag the [Function] as native and
// attach the native name to it.
Function& function = Function::Handle(Z);
for (intptr_t i = 0; i < length; ++i) {
function ^= potential_natives_.At(i);
builder_.SetOffset(function.KernelDataProgramOffset() +
function.kernel_offset());
{
ProcedureHelper procedure_helper(&builder_);
procedure_helper.ReadUntilExcluding(ProcedureHelper::kAnnotations);
}
const intptr_t annotation_count = builder_.ReadListLength();
for (intptr_t j = 0; j < annotation_count; ++j) {
const intptr_t tag = builder_.PeekTag();
if (tag == kConstantExpression) {
builder_.ReadByte(); // Skip the tag.
// We have a candiate. Let's look if it's an instance of the
// ExternalName class.
const intptr_t constant_table_index = builder_.ReadUInt();
constant ^= constant_table.At(constant_table_index);
if (constant.clazz() == external_name_class_.raw()) {
// We found the annotation, let's flag the function as native and
// set the native name!
native_name ^= constant.GetField(external_name_field_);
function.set_is_native(true);
function.set_native_name(native_name);
break;
}
} else {
builder_.SkipExpression();
}
}
}
// Clear out the list of [Function] objects which might need their native
// name to be set after reading the constant table from the kernel blob.
potential_natives_ = GrowableObjectArray::null();
kernel_program_info_.set_potential_natives(potential_natives_);
}
}
Object& KernelLoader::LoadProgram(bool process_pending_classes) {
ASSERT(kernel_program_info_.constants() == Array::null());
if (!program_->is_single_program()) {
FATAL(
"Trying to load a concatenated dill file at a time where that is "
@ -364,32 +447,46 @@ Object& KernelLoader::LoadProgram(bool process_pending_classes) {
LongJumpScope jump;
if (setjmp(*jump.Set()) == 0) {
intptr_t length = program_->library_count();
const intptr_t length = program_->library_count();
for (intptr_t i = 0; i < length; i++) {
LoadLibrary(i);
}
if (process_pending_classes && ClassFinalizer::ProcessPendingClasses()) {
// If 'main' is not found return a null library, this is the case
// when bootstrapping is in progress.
NameIndex main = program_->main_method();
if (main == -1) {
return Library::Handle(Z);
if (process_pending_classes) {
if (!ClassFinalizer::ProcessPendingClasses()) {
// Class finalization failed -> sticky error would be set.
Error& error = Error::Handle(Z);
error = H.thread()->sticky_error();
H.thread()->clear_sticky_error();
return error;
}
NameIndex main_library = H.EnclosingName(main);
Library& library = LookupLibrary(main_library);
return library;
} else if (!process_pending_classes) {
NameIndex main = program_->main_method();
if (main == -1) {
return Library::Handle(Z);
}
NameIndex main_library = H.EnclosingName(main);
Library& library = LookupLibrary(main_library);
return library;
}
// All classes were successfully loaded, so let's:
// a) load & canonicalize the constant table
const Array& constants = ReadConstantTable();
// b) set the native names for native functions which have been created
// so far (the rest will be directly set during LoadProcedure)
AnnotateNativeProcedures(constants);
ASSERT(kernel_program_info_.constants() == Array::null());
// c) update all scripts with the constants array
kernel_program_info_.set_constants(constants);
NameIndex main = program_->main_method();
if (main == -1) {
return Library::Handle(Z);
}
NameIndex main_library = H.EnclosingName(main);
Library& library = LookupLibrary(main_library);
// Sanity check that we can find the main entrypoint.
ASSERT(library.LookupObjectAllowPrivate(H.DartSymbol("main")) !=
Object::null());
return library;
}
// Either class finalization failed or we caught a compile error.
@ -495,11 +592,14 @@ void KernelLoader::LoadLibrary(intptr_t index) {
if (H.StringEquals(lib_name_index, kVMServiceIOLibraryUri)) {
// We are not the service isolate and we are not generating an AOT
// snapshot so we skip loading 'dart:vmservice_io'.
skip_vmservice_library_ = library_helper.canonical_name_;
ASSERT(H.IsLibrary(skip_vmservice_library_));
return;
}
}
Library& library = LookupLibrary(library_helper.canonical_name_);
// The Kernel library is external implies that it is already loaded.
ASSERT(!library_helper.IsExternal() || library.Loaded());
if (library.Loaded()) return;
@ -964,58 +1064,98 @@ void KernelLoader::LoadProcedure(const Library& library,
bool is_external = procedure_helper.IsExternal();
String* native_name = NULL;
intptr_t annotation_count;
bool is_potential_native = false;
if (is_external) {
// Maybe it has a native implementation, which is not external as far as
// the VM is concerned because it does have an implementation. Check for
// an ExternalName annotation and extract the string from it.
annotation_count = builder_.ReadListLength(); // read list length.
for (int i = 0; i < annotation_count; ++i) {
if (builder_.PeekTag() != kConstructorInvocation &&
builder_.PeekTag() != kConstConstructorInvocation) {
const intptr_t tag = builder_.PeekTag();
if (tag == kConstructorInvocation || tag == kConstConstructorInvocation) {
builder_.ReadTag();
builder_.ReadPosition();
NameIndex annotation_class = H.EnclosingName(
builder_.ReadCanonicalNameReference()); // read target reference,
ASSERT(H.IsClass(annotation_class));
StringIndex class_name_index = H.CanonicalNameString(annotation_class);
// Just compare by name, do not generate the annotation class.
if (!H.StringEquals(class_name_index, "ExternalName")) {
builder_.SkipArguments();
continue;
}
ASSERT(H.IsLibrary(H.CanonicalNameParent(annotation_class)));
StringIndex library_name_index =
H.CanonicalNameString(H.CanonicalNameParent(annotation_class));
if (!H.StringEquals(library_name_index, "dart:_internal")) {
builder_.SkipArguments();
continue;
}
is_external = false;
// Read arguments:
intptr_t total_arguments = builder_.ReadUInt(); // read argument count.
builder_.SkipListOfDartTypes(); // read list of types.
intptr_t positional_arguments = builder_.ReadListLength();
ASSERT(total_arguments == 1 && positional_arguments == 1);
Tag tag = builder_.ReadTag();
ASSERT(tag == kStringLiteral);
native_name = &H.DartSymbol(
builder_.ReadStringReference()); // read index into string table.
// List of named.
intptr_t list_length = builder_.ReadListLength(); // read list length.
ASSERT(list_length == 0);
// Skip remaining annotations
for (++i; i < annotation_count; ++i) {
builder_.SkipExpression(); // read ith annotation.
}
break;
} else if (tag == kConstantExpression) {
if (kernel_program_info_.constants() == Array::null()) {
// We can only read in the constant table once all classes have been
// finalized (otherwise we can't create instances of the classes!).
//
// We therefore delay the scanning for `ExternalName {name: ... }`
// constants in the annotation list to later.
is_potential_native = true;
builder_.SkipExpression();
} else {
builder_.ReadByte(); // Skip the tag.
// Obtain `dart:_internal::ExternalName.name`.
EnsureExternalClassIsLookedUp();
const Array& constant_table =
Array::Handle(kernel_program_info_.constants());
// We have a candiate. Let's look if it's an instance of the
// ExternalName class.
const intptr_t constant_table_index = builder_.ReadUInt();
const Object& constant =
Object::Handle(constant_table.At(constant_table_index));
if (constant.clazz() == external_name_class_.raw()) {
const Instance& instance =
Instance::Handle(Instance::RawCast(constant.raw()));
// We found the annotation, let's flag the function as native and
// set the native name!
native_name = &String::Handle(
String::RawCast(instance.GetField(external_name_field_)));
// Skip remaining annotations
for (++i; i < annotation_count; ++i) {
builder_.SkipExpression(); // read ith annotation.
}
break;
}
}
} else {
builder_.SkipExpression();
continue;
}
builder_.ReadTag();
builder_.ReadPosition();
NameIndex annotation_class = H.EnclosingName(
builder_.ReadCanonicalNameReference()); // read target reference,
ASSERT(H.IsClass(annotation_class));
StringIndex class_name_index = H.CanonicalNameString(annotation_class);
// Just compare by name, do not generate the annotation class.
if (!H.StringEquals(class_name_index, "ExternalName")) {
builder_.SkipArguments();
continue;
}
ASSERT(H.IsLibrary(H.CanonicalNameParent(annotation_class)));
StringIndex library_name_index =
H.CanonicalNameString(H.CanonicalNameParent(annotation_class));
if (!H.StringEquals(library_name_index, "dart:_internal")) {
builder_.SkipArguments();
continue;
}
is_external = false;
// Read arguments:
intptr_t total_arguments = builder_.ReadUInt(); // read argument count.
builder_.SkipListOfDartTypes(); // read list of types.
intptr_t positional_arguments = builder_.ReadListLength();
ASSERT(total_arguments == 1 && positional_arguments == 1);
Tag tag = builder_.ReadTag();
ASSERT(tag == kStringLiteral);
native_name = &H.DartSymbol(
builder_.ReadStringReference()); // read index into string table.
// List of named.
intptr_t list_length = builder_.ReadListLength(); // read list length.
ASSERT(list_length == 0);
// Skip remaining annotations
for (++i; i < annotation_count; ++i) {
builder_.SkipExpression(); // read ith annotation.
}
break;
}
procedure_helper.SetJustRead(ProcedureHelper::kAnnotations);
} else {
@ -1066,6 +1206,10 @@ void KernelLoader::LoadProcedure(const Library& library,
if (native_name != NULL) {
function.set_native_name(*native_name);
}
if (is_potential_native) {
EnsurePotentialNatives();
potential_natives_.Add(function);
}
function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kTypeParameters);
builder_.SetupFunctionParameters(&active_class_, owner, function, is_method,

View file

@ -140,6 +140,13 @@ class KernelLoader {
static void FinishLoading(const Class& klass);
const Array& ReadConstantTable();
void AnnotateNativeProcedures(const Array& constant_table);
const String& DartSymbol(StringIndex index) {
return translation_helper_.DartSymbol(index);
}
const String& LibraryUri(intptr_t library_index) {
return translation_helper_.DartSymbol(
translation_helper_.CanonicalNameString(
@ -221,6 +228,28 @@ class KernelLoader {
RawFunction::Kind GetFunctionType(ProcedureHelper::Kind procedure_kind);
void EnsureExternalClassIsLookedUp() {
if (external_name_class_.IsNull()) {
ASSERT(external_name_field_.IsNull());
const Library& internal_lib =
Library::Handle(zone_, dart::Library::InternalLibrary());
external_name_class_ = internal_lib.LookupClass(Symbols::ExternalName());
external_name_field_ = external_name_class_.LookupField(Symbols::name());
} else {
ASSERT(!external_name_field_.IsNull());
}
}
void EnsurePotentialNatives() {
potential_natives_ = kernel_program_info_.potential_natives();
if (potential_natives_.IsNull()) {
// To avoid too many grows in this array, we'll set it's initial size to
// something close to the actual number of potential native functions.
potential_natives_ = GrowableObjectArray::New(100, Heap::kNew);
kernel_program_info_.set_potential_natives(potential_natives_);
}
}
Program* program_;
Thread* thread_;
@ -236,11 +265,18 @@ class KernelLoader {
// to their library's kernel data, have to be corrected.
intptr_t correction_offset_;
bool loading_native_wrappers_library_;
NameIndex skip_vmservice_library_;
TypedData& library_kernel_data_;
KernelProgramInfo& kernel_program_info_;
BuildingTranslationHelper translation_helper_;
StreamingFlowGraphBuilder builder_;
Class& external_name_class_;
Field& external_name_field_;
GrowableObjectArray& potential_natives_;
Mapping<Library> libraries_;
Mapping<Class> classes_;

View file

@ -5539,14 +5539,20 @@ RawFunction* Function::implicit_closure_function() const {
void Function::set_implicit_closure_function(const Function& value) const {
ASSERT(!IsClosureFunction() && !IsSignatureFunction() &&
!IsConvertedClosureFunction());
const Object& old_data = Object::Handle(raw_ptr()->data_);
if (is_native()) {
const Object& obj = Object::Handle(raw_ptr()->data_);
ASSERT(obj.IsArray());
ASSERT((Array::Cast(obj).At(1) == Object::null()) || value.IsNull());
Array::Cast(obj).SetAt(1, value);
ASSERT(old_data.IsArray());
ASSERT((Array::Cast(old_data).At(1) == Object::null()) || value.IsNull());
Array::Cast(old_data).SetAt(1, value);
} else {
ASSERT((raw_ptr()->data_ == Object::null()) || value.IsNull());
set_data(value);
// Maybe this function will turn into a native later on :-/
if (old_data.IsArray()) {
ASSERT((Array::Cast(old_data).At(1) == Object::null()) || value.IsNull());
Array::Cast(old_data).SetAt(1, value);
} else {
ASSERT(old_data.IsNull() || value.IsNull());
set_data(value);
}
}
}
@ -5827,11 +5833,26 @@ RawString* Function::native_name() const {
}
void Function::set_native_name(const String& value) const {
Zone* zone = Thread::Current()->zone();
ASSERT(is_native());
ASSERT(raw_ptr()->data_ == Object::null());
const Array& pair = Array::Handle(Array::New(2, Heap::kOld));
// Due to the fact that kernel needs to read in the constant table before the
// annotation data is available, we don't know at function creation time
// whether the function is a native or not.
//
// Reading the constant table can cause a static function to get an implicit
// closure function.
//
// We therefore handle both cases.
const Object& old_data = Object::Handle(zone, raw_ptr()->data_);
ASSERT(old_data.IsNull() ||
(old_data.IsFunction() &&
Function::Handle(zone, Function::RawCast(old_data.raw()))
.IsImplicitClosureFunction()));
const Array& pair = Array::Handle(zone, Array::New(2, Heap::kOld));
pair.SetAt(0, value);
// pair[1] will be the implicit closure function if needed.
pair.SetAt(1, old_data); // will be the implicit closure function if needed.
set_data(pair);
}
@ -11921,6 +11942,15 @@ RawScript* KernelProgramInfo::ScriptAt(intptr_t index) const {
return Script::RawCast(script);
}
void KernelProgramInfo::set_constants(const Array& constants) const {
StorePointer(&raw_ptr()->constants_, constants.raw());
}
void KernelProgramInfo::set_potential_natives(
const GrowableObjectArray& candidates) const {
StorePointer(&raw_ptr()->potential_natives_, candidates.raw());
}
RawError* Library::CompileAll() {
Thread* thread = Thread::Current();
Zone* zone = thread->zone();

View file

@ -3979,6 +3979,19 @@ class KernelProgramInfo : public Object {
RawArray* scripts() const { return raw_ptr()->scripts_; }
RawArray* constants() const { return raw_ptr()->constants_; }
void set_constants(const Array& constants) const;
// If we load a kernel blob with evaluated constants, then we delay setting
// the native names of [Function] objects until we've read the constant table
// (since native names are encoded as constants).
//
// This array will hold the functions which might need their native name set.
RawGrowableObjectArray* potential_natives() const {
return raw_ptr()->potential_natives_;
}
void set_potential_natives(const GrowableObjectArray& candidates) const;
RawScript* ScriptAt(intptr_t index) const;
private:

View file

@ -1204,7 +1204,9 @@ class RawKernelProgramInfo : public RawObject {
RawTypedData* metadata_payloads_;
RawTypedData* metadata_mappings_;
RawArray* scripts_;
VISIT_TO(RawObject*, scripts_);
RawArray* constants_;
RawGrowableObjectArray* potential_natives_;
VISIT_TO(RawObject*, potential_natives_);
};
class RawCode : public RawObject {

View file

@ -372,6 +372,7 @@ class ObjectPointerVisitor;
V(DartIOLibName, "dart.io") \
V(DartVMProduct, "dart.vm.product") \
V(EvalSourceUri, "evaluate:source") \
V(ExternalName, "ExternalName") \
V(_Random, "_Random") \
V(_state, "_state") \
V(_A, "_A") \
@ -429,6 +430,7 @@ class ObjectPointerVisitor;
V(DartLibrary, "dart.library.") \
V(DartLibraryMirrors, "dart.library.mirrors") \
V(_name, "_name") \
V(name, "name") \
V(_classRangeCheck, "_classRangeCheck") \
V(_classRangeCheckNegative, "_classRangeCheckNegative") \
V(_classRangeAssert, "_classRangeAssert") \