8bfc4b47c0
This adds a class CanonicalName that can represent a library, class, or member. All references now go through a Reference object, which is linked to both the AST node and its CanonicalName, so either can be created first. dartk now accepts multiple input files: - If multiple dart files are given, they are all compiled. - If multiple binaries are given, they are linked together. Mixed dart and binary input is not supported by dartk. dartk now has a flag --include-sdk which includes the entire SDK in the output. This is so the SDK can be compiled alone and then linked. Example of compiling separately and then linking: dartk foo.dart -o foo.dill dartk main.dart -o main.dill dartk --include-sdk -o sdk.dill dartk main.dill foo.dill sdk.dill --target=vm --link -o program.dill dartk still has incredibly slow cold start due to the analyzer loading the dart sdk, so this does not actually speed things up at the moment. BUG= R=ahe@google.com, kmillikin@google.com, kustermann@google.com, sigmund@google.com Review-Url: https://codereview.chromium.org/2665723002 .
437 lines
14 KiB
Dart
437 lines
14 KiB
Dart
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
/// A library to help transform compounds and null-aware accessors into
|
|
/// let expressions.
|
|
library kernel.frontend.accessors;
|
|
|
|
import '../ast.dart';
|
|
|
|
abstract class Accessor {
|
|
// [builtBinary] and [builtGetter] capture the inner nodes. Used by
|
|
// dart2js+rasta for determining how subexpressions map to legacy dart2js Ast
|
|
// nodes. This will be removed once dart2js type analysis (aka inference) is
|
|
// reimplemented on kernel.
|
|
Expression builtBinary;
|
|
Expression builtGetter;
|
|
|
|
Expression buildSimpleRead() {
|
|
return _finish(_makeSimpleRead());
|
|
}
|
|
|
|
/// Returns an assignment to the accessor.
|
|
///
|
|
/// The returned expression evaluates to the assigned value, unless
|
|
/// [voidContext] is true, in which case it may evaluate to anything.
|
|
Expression buildAssignment(Expression value, {bool voidContext: false}) {
|
|
return _finish(_makeSimpleWrite(value, voidContext));
|
|
}
|
|
|
|
Expression buildNullAwareAssignment(Expression value, DartType type,
|
|
{bool voidContext: false}) {
|
|
if (voidContext) {
|
|
return _finish(new ConditionalExpression(buildIsNull(_makeRead()),
|
|
_makeWrite(value, false), new NullLiteral(), type));
|
|
}
|
|
var tmp = new VariableDeclaration.forValue(_makeRead());
|
|
return _finish(makeLet(
|
|
tmp,
|
|
new ConditionalExpression(buildIsNull(new VariableGet(tmp)),
|
|
_makeWrite(value, false), new VariableGet(tmp), type)));
|
|
}
|
|
|
|
Expression buildCompoundAssignment(Name binaryOperator, Expression value,
|
|
{int offset: TreeNode.noOffset,
|
|
bool voidContext: false,
|
|
Procedure interfaceTarget}) {
|
|
return _finish(_makeWrite(
|
|
builtBinary = makeBinary(
|
|
_makeRead(), binaryOperator, interfaceTarget, value,
|
|
offset: offset),
|
|
voidContext));
|
|
}
|
|
|
|
Expression buildPrefixIncrement(Name binaryOperator,
|
|
{int offset: TreeNode.noOffset,
|
|
bool voidContext: false,
|
|
Procedure interfaceTarget}) {
|
|
return buildCompoundAssignment(binaryOperator, new IntLiteral(1),
|
|
offset: offset,
|
|
voidContext: voidContext,
|
|
interfaceTarget: interfaceTarget);
|
|
}
|
|
|
|
Expression buildPostfixIncrement(Name binaryOperator,
|
|
{int offset: TreeNode.noOffset,
|
|
bool voidContext: false,
|
|
Procedure interfaceTarget}) {
|
|
if (voidContext) {
|
|
return buildPrefixIncrement(binaryOperator,
|
|
offset: offset, voidContext: true, interfaceTarget: interfaceTarget);
|
|
}
|
|
var value = new VariableDeclaration.forValue(_makeRead());
|
|
valueAccess() => new VariableGet(value);
|
|
var dummy = new VariableDeclaration.forValue(_makeWrite(
|
|
builtBinary = makeBinary(
|
|
valueAccess(), binaryOperator, interfaceTarget, new IntLiteral(1),
|
|
offset: offset),
|
|
true));
|
|
return _finish(makeLet(value, makeLet(dummy, valueAccess())));
|
|
}
|
|
|
|
Expression _makeSimpleRead() => _makeRead();
|
|
|
|
Expression _makeSimpleWrite(Expression value, bool voidContext) {
|
|
return _makeWrite(value, voidContext);
|
|
}
|
|
|
|
Expression _makeRead();
|
|
|
|
Expression _makeWrite(Expression value, bool voidContext);
|
|
|
|
Expression _finish(Expression body) => body;
|
|
|
|
makeInvalidRead() => new InvalidExpression();
|
|
|
|
makeInvalidWrite(Expression value) => wrapInvalid(value);
|
|
}
|
|
|
|
class VariableAccessor extends Accessor {
|
|
VariableDeclaration variable;
|
|
DartType promotedType;
|
|
|
|
VariableAccessor(this.variable, [this.promotedType]);
|
|
|
|
_makeRead() => new VariableGet(variable, promotedType);
|
|
|
|
_makeWrite(Expression value, bool voidContext) {
|
|
return variable.isFinal || variable.isConst
|
|
? makeInvalidWrite(value)
|
|
: new VariableSet(variable, value);
|
|
}
|
|
}
|
|
|
|
class PropertyAccessor extends Accessor {
|
|
VariableDeclaration _receiverVariable;
|
|
Expression receiver;
|
|
Name name;
|
|
Member getter, setter;
|
|
|
|
static Accessor make(
|
|
Expression receiver, Name name, Member getter, Member setter) {
|
|
if (receiver is ThisExpression) {
|
|
return new ThisPropertyAccessor(name, getter, setter);
|
|
} else {
|
|
return new PropertyAccessor._internal(receiver, name, getter, setter);
|
|
}
|
|
}
|
|
|
|
PropertyAccessor._internal(
|
|
this.receiver, this.name, this.getter, this.setter);
|
|
|
|
_makeSimpleRead() => new PropertyGet(receiver, name, getter);
|
|
_makeSimpleWrite(Expression value, bool voidContext) {
|
|
return new PropertySet(receiver, name, value, setter);
|
|
}
|
|
|
|
receiverAccess() {
|
|
_receiverVariable ??= new VariableDeclaration.forValue(receiver);
|
|
return new VariableGet(_receiverVariable);
|
|
}
|
|
|
|
_makeRead() => builtGetter = new PropertyGet(receiverAccess(), name, getter);
|
|
|
|
_makeWrite(Expression value, bool voidContext) {
|
|
return new PropertySet(receiverAccess(), name, value, setter);
|
|
}
|
|
|
|
_finish(Expression body) => makeLet(_receiverVariable, body);
|
|
}
|
|
|
|
/// Special case of [PropertyAccessor] to avoid creating an indirect access to
|
|
/// 'this'.
|
|
class ThisPropertyAccessor extends Accessor {
|
|
Name name;
|
|
Member getter, setter;
|
|
|
|
ThisPropertyAccessor(this.name, this.getter, this.setter);
|
|
|
|
_makeRead() =>
|
|
builtGetter = new PropertyGet(new ThisExpression(), name, getter);
|
|
|
|
_makeWrite(Expression value, bool voidContext) {
|
|
return new PropertySet(new ThisExpression(), name, value, setter);
|
|
}
|
|
}
|
|
|
|
class NullAwarePropertyAccessor extends Accessor {
|
|
VariableDeclaration receiver;
|
|
Name name;
|
|
Member getter, setter;
|
|
DartType type;
|
|
|
|
NullAwarePropertyAccessor(
|
|
Expression receiver, this.name, this.getter, this.setter, this.type)
|
|
: this.receiver = makeOrReuseVariable(receiver);
|
|
|
|
receiverAccess() => new VariableGet(receiver);
|
|
|
|
_makeRead() => builtGetter = new PropertyGet(receiverAccess(), name, getter);
|
|
|
|
_makeWrite(Expression value, bool voidContext) {
|
|
return new PropertySet(receiverAccess(), name, value, setter);
|
|
}
|
|
|
|
_finish(Expression body) => makeLet(
|
|
receiver,
|
|
new ConditionalExpression(
|
|
buildIsNull(receiverAccess()), new NullLiteral(), body, type));
|
|
}
|
|
|
|
class SuperPropertyAccessor extends Accessor {
|
|
Name name;
|
|
Member getter, setter;
|
|
|
|
SuperPropertyAccessor(this.name, this.getter, this.setter);
|
|
|
|
_makeRead() => builtGetter = new SuperPropertyGet(name, getter);
|
|
|
|
_makeWrite(Expression value, bool voidContext) {
|
|
return new SuperPropertySet(name, value, setter);
|
|
}
|
|
}
|
|
|
|
final Name _indexGet = new Name('[]');
|
|
final Name _indexSet = new Name('[]=');
|
|
|
|
class IndexAccessor extends Accessor {
|
|
Expression receiver;
|
|
Expression index;
|
|
VariableDeclaration receiverVariable;
|
|
VariableDeclaration indexVariable;
|
|
Procedure getter, setter;
|
|
|
|
static Accessor make(Expression receiver, Expression index, Procedure getter,
|
|
Procedure setter) {
|
|
if (receiver is ThisExpression) {
|
|
return new ThisIndexAccessor(index, getter, setter);
|
|
} else {
|
|
return new IndexAccessor._internal(receiver, index, getter, setter);
|
|
}
|
|
}
|
|
|
|
IndexAccessor._internal(this.receiver, this.index, this.getter, this.setter);
|
|
|
|
_makeSimpleRead() => new MethodInvocation(
|
|
receiver, _indexGet, new Arguments(<Expression>[index]), getter);
|
|
|
|
_makeSimpleWrite(Expression value, bool voidContext) {
|
|
if (!voidContext) return _makeWriteAndReturn(value);
|
|
return new MethodInvocation(
|
|
receiver, _indexSet, new Arguments(<Expression>[index, value]), setter);
|
|
}
|
|
|
|
receiverAccess() {
|
|
// We cannot reuse the receiver if it is a variable since it might be
|
|
// reassigned in the index expression.
|
|
receiverVariable ??= new VariableDeclaration.forValue(receiver);
|
|
return new VariableGet(receiverVariable);
|
|
}
|
|
|
|
indexAccess() {
|
|
indexVariable ??= new VariableDeclaration.forValue(index);
|
|
return new VariableGet(indexVariable);
|
|
}
|
|
|
|
_makeRead() {
|
|
return builtGetter = new MethodInvocation(receiverAccess(), _indexGet,
|
|
new Arguments(<Expression>[indexAccess()]), getter);
|
|
}
|
|
|
|
_makeWrite(Expression value, bool voidContext) {
|
|
if (!voidContext) return _makeWriteAndReturn(value);
|
|
return new MethodInvocation(receiverAccess(), _indexSet,
|
|
new Arguments(<Expression>[indexAccess(), value]), setter);
|
|
}
|
|
|
|
_makeWriteAndReturn(Expression value) {
|
|
// The call to []= does not return the value like direct-style assignments
|
|
// do. We need to bind the value in a let.
|
|
var valueVariable = new VariableDeclaration.forValue(value);
|
|
var dummy = new VariableDeclaration.forValue(new MethodInvocation(
|
|
receiverAccess(),
|
|
_indexSet,
|
|
new Arguments(
|
|
<Expression>[indexAccess(), new VariableGet(valueVariable)]),
|
|
setter));
|
|
return makeLet(
|
|
valueVariable, makeLet(dummy, new VariableGet(valueVariable)));
|
|
}
|
|
|
|
Expression _finish(Expression body) {
|
|
return makeLet(receiverVariable, makeLet(indexVariable, body));
|
|
}
|
|
}
|
|
|
|
/// Special case of [IndexAccessor] to avoid creating an indirect access to
|
|
/// 'this'.
|
|
class ThisIndexAccessor extends Accessor {
|
|
Expression index;
|
|
VariableDeclaration indexVariable;
|
|
Procedure getter, setter;
|
|
|
|
ThisIndexAccessor(this.index, this.getter, this.setter);
|
|
|
|
_makeSimpleRead() {
|
|
return new MethodInvocation(new ThisExpression(), _indexGet,
|
|
new Arguments(<Expression>[index]), getter);
|
|
}
|
|
|
|
_makeSimpleWrite(Expression value, bool voidContext) {
|
|
if (!voidContext) return _makeWriteAndReturn(value);
|
|
return new MethodInvocation(new ThisExpression(), _indexSet,
|
|
new Arguments(<Expression>[index, value]), setter);
|
|
}
|
|
|
|
indexAccess() {
|
|
indexVariable ??= new VariableDeclaration.forValue(index);
|
|
return new VariableGet(indexVariable);
|
|
}
|
|
|
|
_makeRead() => builtGetter = new MethodInvocation(new ThisExpression(),
|
|
_indexGet, new Arguments(<Expression>[indexAccess()]), getter);
|
|
|
|
_makeWrite(Expression value, bool voidContext) {
|
|
if (!voidContext) return _makeWriteAndReturn(value);
|
|
return new MethodInvocation(new ThisExpression(), _indexSet,
|
|
new Arguments(<Expression>[indexAccess(), value]), setter);
|
|
}
|
|
|
|
_makeWriteAndReturn(Expression value) {
|
|
var valueVariable = new VariableDeclaration.forValue(value);
|
|
var dummy = new VariableDeclaration.forValue(new MethodInvocation(
|
|
new ThisExpression(),
|
|
_indexSet,
|
|
new Arguments(
|
|
<Expression>[indexAccess(), new VariableGet(valueVariable)]),
|
|
setter));
|
|
return makeLet(
|
|
valueVariable, makeLet(dummy, new VariableGet(valueVariable)));
|
|
}
|
|
|
|
Expression _finish(Expression body) => makeLet(indexVariable, body);
|
|
}
|
|
|
|
class SuperIndexAccessor extends Accessor {
|
|
Expression index;
|
|
VariableDeclaration indexVariable;
|
|
Member getter, setter;
|
|
|
|
SuperIndexAccessor(this.index, this.getter, this.setter);
|
|
|
|
indexAccess() {
|
|
indexVariable ??= new VariableDeclaration.forValue(index);
|
|
return new VariableGet(indexVariable);
|
|
}
|
|
|
|
_makeSimpleRead() => new SuperMethodInvocation(
|
|
_indexGet, new Arguments(<Expression>[index]), getter);
|
|
|
|
_makeSimpleWrite(Expression value, bool voidContext) {
|
|
if (!voidContext) return _makeWriteAndReturn(value);
|
|
return new SuperMethodInvocation(
|
|
_indexSet, new Arguments(<Expression>[index, value]), setter);
|
|
}
|
|
|
|
_makeRead() {
|
|
return builtGetter = new SuperMethodInvocation(
|
|
_indexGet, new Arguments(<Expression>[indexAccess()]), getter);
|
|
}
|
|
|
|
_makeWrite(Expression value, bool voidContext) {
|
|
if (!voidContext) return _makeWriteAndReturn(value);
|
|
return new SuperMethodInvocation(
|
|
_indexSet, new Arguments(<Expression>[indexAccess(), value]), setter);
|
|
}
|
|
|
|
_makeWriteAndReturn(Expression value) {
|
|
var valueVariable = new VariableDeclaration.forValue(value);
|
|
var dummy = new VariableDeclaration.forValue(new SuperMethodInvocation(
|
|
_indexSet,
|
|
new Arguments(
|
|
<Expression>[indexAccess(), new VariableGet(valueVariable)]),
|
|
setter));
|
|
return makeLet(
|
|
valueVariable, makeLet(dummy, new VariableGet(valueVariable)));
|
|
}
|
|
|
|
Expression _finish(Expression body) {
|
|
return makeLet(indexVariable, body);
|
|
}
|
|
}
|
|
|
|
class StaticAccessor extends Accessor {
|
|
Member readTarget;
|
|
Member writeTarget;
|
|
|
|
StaticAccessor(this.readTarget, this.writeTarget);
|
|
|
|
_makeRead() => builtGetter =
|
|
readTarget == null ? makeInvalidRead() : new StaticGet(readTarget);
|
|
|
|
_makeWrite(Expression value, bool voidContext) {
|
|
return writeTarget == null
|
|
? makeInvalidWrite(value)
|
|
: new StaticSet(writeTarget, value);
|
|
}
|
|
}
|
|
|
|
class ReadOnlyAccessor extends Accessor {
|
|
Expression expression;
|
|
VariableDeclaration value;
|
|
|
|
ReadOnlyAccessor(this.expression);
|
|
|
|
_makeSimpleRead() => expression;
|
|
|
|
_makeRead() {
|
|
value ??= new VariableDeclaration.forValue(expression);
|
|
return new VariableGet(value);
|
|
}
|
|
|
|
_makeWrite(Expression value, bool voidContext) => makeInvalidWrite(value);
|
|
|
|
Expression _finish(Expression body) => makeLet(value, body);
|
|
}
|
|
|
|
Expression makeLet(VariableDeclaration variable, Expression body) {
|
|
if (variable == null) return body;
|
|
return new Let(variable, body);
|
|
}
|
|
|
|
Expression makeBinary(
|
|
Expression left, Name operator, Procedure interfaceTarget, Expression right,
|
|
{int offset: TreeNode.noOffset}) {
|
|
return new MethodInvocation(
|
|
left, operator, new Arguments(<Expression>[right]), interfaceTarget)
|
|
..fileOffset = offset;
|
|
}
|
|
|
|
final Name _equalOperator = new Name('==');
|
|
|
|
Expression buildIsNull(Expression value, {int offset: TreeNode.noOffset}) {
|
|
return makeBinary(value, _equalOperator, null, new NullLiteral(),
|
|
offset: offset);
|
|
}
|
|
|
|
VariableDeclaration makeOrReuseVariable(Expression value) {
|
|
// TODO: Devise a way to remember if a variable declaration was reused
|
|
// or is fresh (hence needs a let binding).
|
|
return new VariableDeclaration.forValue(value);
|
|
}
|
|
|
|
Expression wrapInvalid(Expression e) {
|
|
return new Let(new VariableDeclaration.forValue(e), new InvalidExpression());
|
|
}
|