Chromium Code Reviews| Index: pkg/front_end/lib/src/fasta/kernel/frontend_accessors.dart |
| diff --git a/pkg/front_end/lib/src/fasta/kernel/frontend_accessors.dart b/pkg/front_end/lib/src/fasta/kernel/frontend_accessors.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..d1d7b2ed4da9479c46dc798b86192a0e764aa73c |
| --- /dev/null |
| +++ b/pkg/front_end/lib/src/fasta/kernel/frontend_accessors.dart |
| @@ -0,0 +1,509 @@ |
| +// 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. |
| + |
| +import 'package:kernel/ast.dart'; |
| + |
| +final Name indexGetName = new Name("[]"); |
|
ahe
2017/04/04 09:18:50
We could export these from the kernel version.
Paul Berry
2017/04/04 16:21:10
Done.
Paul Berry
2017/04/04 17:45:20
But for some reason this caused test failures whic
|
| + |
| +final Name indexSetName = new Name("[]="); |
| + |
| +/// An [Accessor] represents a subexpression for which we can't yet build a |
| +/// kernel [Expression] because we don't yet know the context in which it is |
| +/// used. |
| +/// |
| +/// Once the context is known, an [Accessor] can be converted into an |
| +/// [Expression] by calling a "build" method. |
| +/// |
| +/// For example, when building a kernel representation for `a[x] = b`, after |
| +/// parsing `a[x]` but before parsing `= b`, we don't yet know whether to |
| +/// generate an invocation of `operator[]` or `operator[]=`, so we generate an |
| +/// [Accessor] object. Later, after `= b` is parsed, [buildAssignment] will be |
| +/// called. |
| +abstract class Accessor { |
| + final int offset; |
| + |
| + // [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; |
| + |
| + Accessor(this.offset); |
| + |
| + /// Builds an [Expression] representing a read from the accessor. |
| + Expression buildSimpleRead() { |
| + return _finish(_makeSimpleRead()); |
| + } |
| + |
| + /// Builds an [Expression] representing an assignment with the accessor on |
| + /// the LHS and [value] on the RHS. |
| + /// |
| + /// 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)); |
| + } |
| + |
| + /// Returns an [Expression] representing a null-aware assignment (`??=`) with |
| + /// the accessor on the LHS and [value] on the RHS. |
| + /// |
| + /// The returned expression evaluates to the assigned value, unless |
| + /// [voidContext] is true, in which case it may evaluate to anything. |
| + /// |
| + /// [type] is the static type of the RHS. |
| + 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))); |
| + } |
| + |
| + /// Returns an [Expression] representing a compound assignment (e.g. `+=`) |
| + /// with the accessor on the LHS and [value] on the RHS. |
| + 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)); |
| + } |
| + |
| + /// Returns an [Expression] representing a pre-increment or pre-decrement |
| + /// of the accessor. |
| + 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); |
| + } |
| + |
| + /// Returns an [Expression] representing a post-increment or post-decrement |
| + /// of the accessor. |
| + 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; |
| + |
| + /// Returns an [Expression] representing a compile-time error. |
| + /// |
| + /// At runtime, an exception will be thrown. |
| + makeInvalidRead() => new InvalidExpression(); |
| + |
| + /// Returns an [Expression] representing a compile-time error wrapping |
| + /// [value]. |
| + /// |
| + /// At runtime, [value] will be evaluated before throwing an exception. |
| + makeInvalidWrite(Expression value) => wrapInvalid(value); |
| +} |
| + |
| +class VariableAccessor extends Accessor { |
| + VariableDeclaration variable; |
| + DartType promotedType; |
| + |
| + VariableAccessor(this.variable, this.promotedType, int offset) |
| + : super(offset); |
| + |
| + _makeRead() => new VariableGet(variable, promotedType)..fileOffset = offset; |
| + |
| + _makeWrite(Expression value, bool voidContext) { |
| + return variable.isFinal || variable.isConst |
| + ? makeInvalidWrite(value) |
| + : new VariableSet(variable, value)..fileOffset = offset; |
| + } |
| +} |
| + |
| +class PropertyAccessor extends Accessor { |
| + VariableDeclaration _receiverVariable; |
| + Expression receiver; |
| + Name name; |
| + Member getter, setter; |
| + |
| + static Accessor make( |
| + Expression receiver, Name name, Member getter, Member setter, |
| + {int offset: TreeNode.noOffset}) { |
| + if (receiver is ThisExpression) { |
| + return new ThisPropertyAccessor(name, getter, setter, offset); |
| + } else { |
| + return new PropertyAccessor.internal( |
| + receiver, name, getter, setter, offset); |
| + } |
| + } |
| + |
| + PropertyAccessor.internal( |
| + this.receiver, this.name, this.getter, this.setter, int offset) |
| + : super(offset); |
| + |
| + _makeSimpleRead() => |
| + new PropertyGet(receiver, name, getter)..fileOffset = offset; |
| + |
| + _makeSimpleWrite(Expression value, bool voidContext) { |
| + return new PropertySet(receiver, name, value, setter)..fileOffset = offset; |
| + } |
| + |
| + receiverAccess() { |
| + _receiverVariable ??= new VariableDeclaration.forValue(receiver); |
| + return new VariableGet(_receiverVariable)..fileOffset = offset; |
| + } |
| + |
| + _makeRead() => builtGetter = new PropertyGet(receiverAccess(), name, getter) |
| + ..fileOffset = offset; |
| + |
| + _makeWrite(Expression value, bool voidContext) { |
| + return new PropertySet(receiverAccess(), name, value, setter) |
| + ..fileOffset = offset; |
| + } |
| + |
| + _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, int offset) |
| + : super(offset); |
| + |
| + _makeRead() => builtGetter = |
| + new PropertyGet(new ThisExpression(), name, getter)..fileOffset = offset; |
| + |
| + _makeWrite(Expression value, bool voidContext) { |
| + return new PropertySet(new ThisExpression(), name, value, setter) |
| + ..fileOffset = offset; |
| + } |
| +} |
| + |
| +class NullAwarePropertyAccessor extends Accessor { |
| + VariableDeclaration receiver; |
| + Name name; |
| + Member getter, setter; |
| + DartType type; |
| + |
| + NullAwarePropertyAccessor(Expression receiver, this.name, this.getter, |
| + this.setter, this.type, int offset) |
| + : this.receiver = makeOrReuseVariable(receiver), |
| + super(offset); |
| + |
| + 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, int offset) |
| + : super(offset); |
| + |
| + _makeRead() { |
| + if (getter == null) return makeInvalidRead(); |
| + // TODO(ahe): Use [DirectPropertyGet] when possible. |
| + return builtGetter = new SuperPropertyGet(name, getter) |
| + ..fileOffset = offset; |
| + } |
| + |
| + _makeWrite(Expression value, bool voidContext) { |
| + if (setter == null) return makeInvalidWrite(value); |
| + // TODO(ahe): Use [DirectPropertySet] when possible. |
| + return new SuperPropertySet(name, value, setter)..fileOffset = offset; |
| + } |
| +} |
| + |
| +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, |
| + {int offset: TreeNode.noOffset}) { |
| + if (receiver is ThisExpression) { |
| + return new ThisIndexAccessor(index, getter, setter, offset); |
| + } else { |
| + return new IndexAccessor.internal( |
| + receiver, index, getter, setter, offset); |
| + } |
| + } |
| + |
| + IndexAccessor.internal( |
| + this.receiver, this.index, this.getter, this.setter, int offset) |
| + : super(offset); |
| + |
| + _makeSimpleRead() => new MethodInvocation( |
| + receiver, indexGetName, new Arguments(<Expression>[index]), getter) |
| + ..fileOffset = offset; |
| + |
| + _makeSimpleWrite(Expression value, bool voidContext) { |
| + if (!voidContext) return _makeWriteAndReturn(value); |
| + return new MethodInvocation(receiver, indexSetName, |
| + new Arguments(<Expression>[index, value]), setter)..fileOffset = offset; |
| + } |
| + |
| + 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)..fileOffset = offset; |
| + } |
| + |
| + indexAccess() { |
| + indexVariable ??= new VariableDeclaration.forValue(index); |
| + return new VariableGet(indexVariable)..fileOffset = offset; |
| + } |
| + |
| + _makeRead() { |
| + return builtGetter = new MethodInvocation( |
| + receiverAccess(), |
| + indexGetName, |
| + new Arguments(<Expression>[indexAccess()]), |
| + getter)..fileOffset = offset; |
| + } |
| + |
| + _makeWrite(Expression value, bool voidContext) { |
| + if (!voidContext) return _makeWriteAndReturn(value); |
| + return new MethodInvocation( |
| + receiverAccess(), |
| + indexSetName, |
| + new Arguments(<Expression>[indexAccess(), value]), |
| + setter)..fileOffset = offset; |
| + } |
| + |
| + // TODO(dmitryas): remove this method after the "[]=" operator of the Context |
| + // class is made to return a value. |
| + _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(), |
| + indexSetName, |
| + new Arguments( |
| + <Expression>[indexAccess(), new VariableGet(valueVariable)]), |
| + setter)..fileOffset = offset); |
| + 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, int offset) |
| + : super(offset); |
| + |
| + _makeSimpleRead() { |
| + return new MethodInvocation(new ThisExpression(), indexGetName, |
| + new Arguments(<Expression>[index]), getter); |
| + } |
| + |
| + _makeSimpleWrite(Expression value, bool voidContext) { |
| + if (!voidContext) return _makeWriteAndReturn(value); |
| + return new MethodInvocation(new ThisExpression(), indexSetName, |
| + new Arguments(<Expression>[index, value]), setter); |
| + } |
| + |
| + indexAccess() { |
| + indexVariable ??= new VariableDeclaration.forValue(index); |
| + return new VariableGet(indexVariable); |
| + } |
| + |
| + _makeRead() => builtGetter = new MethodInvocation(new ThisExpression(), |
| + indexGetName, new Arguments(<Expression>[indexAccess()]), getter); |
| + |
| + _makeWrite(Expression value, bool voidContext) { |
| + if (!voidContext) return _makeWriteAndReturn(value); |
| + return new MethodInvocation(new ThisExpression(), indexSetName, |
| + new Arguments(<Expression>[indexAccess(), value]), setter); |
| + } |
| + |
| + _makeWriteAndReturn(Expression value) { |
| + var valueVariable = new VariableDeclaration.forValue(value); |
| + var dummy = new VariableDeclaration.forValue(new MethodInvocation( |
| + new ThisExpression(), |
| + indexSetName, |
| + 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, int offset) |
| + : super(offset); |
| + |
| + indexAccess() { |
| + indexVariable ??= new VariableDeclaration.forValue(index); |
| + return new VariableGet(indexVariable); |
| + } |
| + |
| + _makeSimpleRead() => new SuperMethodInvocation( |
| + indexGetName, new Arguments(<Expression>[index]), getter); |
| + |
| + _makeSimpleWrite(Expression value, bool voidContext) { |
| + if (!voidContext) return _makeWriteAndReturn(value); |
| + return new SuperMethodInvocation( |
| + indexSetName, new Arguments(<Expression>[index, value]), setter); |
| + } |
| + |
| + _makeRead() { |
| + return builtGetter = new SuperMethodInvocation( |
| + indexGetName, new Arguments(<Expression>[indexAccess()]), getter); |
| + } |
| + |
| + _makeWrite(Expression value, bool voidContext) { |
| + if (!voidContext) return _makeWriteAndReturn(value); |
| + return new SuperMethodInvocation(indexSetName, |
| + new Arguments(<Expression>[indexAccess(), value]), setter); |
| + } |
| + |
| + _makeWriteAndReturn(Expression value) { |
| + var valueVariable = new VariableDeclaration.forValue(value); |
| + var dummy = new VariableDeclaration.forValue(new SuperMethodInvocation( |
| + indexSetName, |
| + 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, int offset) : super(offset); |
| + |
| + _makeRead() => builtGetter = readTarget == null |
| + ? makeInvalidRead() |
| + : new StaticGet(readTarget)..fileOffset = offset; |
| + |
| + _makeWrite(Expression value, bool voidContext) { |
| + return writeTarget == null |
| + ? makeInvalidWrite(value) |
| + : new StaticSet(writeTarget, value)..fileOffset = offset; |
| + } |
| +} |
| + |
| +class ReadOnlyAccessor extends Accessor { |
| + Expression expression; |
| + VariableDeclaration value; |
| + |
| + ReadOnlyAccessor(this.expression, int offset) : super(offset); |
| + |
| + _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) { |
|
ahe
2017/04/04 09:18:50
These helper methods could probably also be export
Paul Berry
2017/04/04 16:21:10
I'm going to leave these as is for now because I b
|
| + 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()); |
| +} |