diff --git a/pkg/analyzer/lib/src/kernel/ast_from_analyzer.dart b/pkg/analyzer/lib/src/kernel/ast_from_analyzer.dart index a965d8c60dc..a0a470d841d 100644 --- a/pkg/analyzer/lib/src/kernel/ast_from_analyzer.dart +++ b/pkg/analyzer/lib/src/kernel/ast_from_analyzer.dart @@ -4,7 +4,6 @@ library kernel.analyzer.ast_from_analyzer; import 'package:kernel/ast.dart' as ast; -import 'package:kernel/frontend/accessors.dart'; import 'package:kernel/frontend/super_initializers.dart'; import 'package:kernel/log.dart'; import 'package:kernel/type_algebra.dart'; @@ -18,6 +17,7 @@ import 'package:analyzer/dart/ast/standard_resolution_map.dart'; import 'package:analyzer/src/generated/parser.dart'; import 'package:analyzer/src/dart/element/member.dart'; import 'package:analyzer/src/error/codes.dart'; +import 'package:front_end/src/fasta/kernel/frontend_accessors.dart'; /// Provides reference-level access to libraries, classes, and members. /// @@ -484,7 +484,8 @@ class TypeScope extends ReferenceScope { positionalParameters: positional, namedParameters: named, requiredParameterCount: requiredParameterCount, - returnType: returnType)..fileOffset = element.nameOffset; + returnType: returnType) + ..fileOffset = element.nameOffset; } } @@ -655,8 +656,8 @@ class ExpressionScope extends TypeScope { ast.VariableDeclaration getVariableReference(LocalElement element) { return localVariables.putIfAbsent(element, () { return new ast.VariableDeclaration(element.name, - isFinal: isFinal(element), - isConst: isConst(element))..fileOffset = element.nameOffset; + isFinal: isFinal(element), isConst: isConst(element)) + ..fileOffset = element.nameOffset; }); } @@ -1279,7 +1280,8 @@ class StatementBuilder extends GeneralizingAstVisitor { variable, scope.buildExpression(node.iterable), makeBreakTarget(body, continueNode), - isAsync: node.awaitKeyword != null)..fileOffset = node.offset; + isAsync: node.awaitKeyword != null) + ..fileOffset = node.offset; return makeBreakTarget(loop, breakNode); } @@ -1357,7 +1359,8 @@ class StatementBuilder extends GeneralizingAstVisitor { typeParameters: scope.buildOptionalTypeParameterList( expression.typeParameters, strongModeOnly: true), - returnType: declaration.returnType))..fileOffset = node.offset; + returnType: declaration.returnType)) + ..fileOffset = node.offset; } @override @@ -2001,7 +2004,8 @@ class ExpressionBuilder new ast.VariableGet(receiver), scope.buildName(node.methodName), buildArgumentsForInvocation(node), - element)..fileOffset = node.methodName.offset, + element) + ..fileOffset = node.methodName.offset, scope.buildType(node.staticType))); } else { return buildDecomposableMethodInvocation( @@ -2637,7 +2641,8 @@ class ClassBodyBuilder extends GeneralizingAstVisitor { new ast.FieldInitializer( nameField, new ast.VariableGet(nameParameter)), new ast.SuperInitializer(superConstructor, new ast.Arguments.empty()) - ])..fileOffset = element.nameOffset; + ]) + ..fileOffset = element.nameOffset; classNode.addMember(constructor); int index = 0; @@ -2650,7 +2655,8 @@ class ClassBodyBuilder extends GeneralizingAstVisitor { new ast.IntLiteral(index), new ast.StringLiteral('${classNode.name}.${field.name.name}') ]), - isConst: true)..parent = field; + isConst: true) + ..parent = field; field.type = classNode.rawType; classNode.addMember(field); ++index; @@ -2666,7 +2672,8 @@ class ClassBodyBuilder extends GeneralizingAstVisitor { valuesField.initializer = new ast.ListLiteral( enumConstantFields.map(_makeStaticGet).toList(), isConst: true, - typeArgument: enumType)..parent = valuesField; + typeArgument: enumType) + ..parent = valuesField; classNode.addMember(valuesField); // Add the 'toString()' method. @@ -2784,7 +2791,8 @@ class MemberBodyBuilder extends GeneralizingAstVisitor { addAnnotations(node.metadata); ast.Constructor constructor = currentMember; constructor.function = scope.buildFunctionNode(node.parameters, node.body, - inferredReturnType: const ast.VoidType())..parent = constructor; + inferredReturnType: const ast.VoidType()) + ..parent = constructor; handleNativeBody(node.body); if (node.body is EmptyFunctionBody && !constructor.isExternal) { var function = constructor.function; @@ -2902,9 +2910,8 @@ class MemberBodyBuilder extends GeneralizingAstVisitor { if (constructorsField == null) { ast.ListLiteral literal = new ast.ListLiteral([]); constructorsField = new ast.Field(constructors, - isStatic: true, - initializer: literal, - fileUri: classNode.fileUri)..fileOffset = classNode.fileOffset; + isStatic: true, initializer: literal, fileUri: classNode.fileUri) + ..fileOffset = classNode.fileOffset; classNode.addMember(constructorsField); } ast.ListLiteral literal = constructorsField.initializer; @@ -2933,7 +2940,8 @@ class MemberBodyBuilder extends GeneralizingAstVisitor { resolutionMap.elementDeclaredByMethodDeclaration(node).returnType), typeParameters: scope.buildOptionalTypeParameterList( node.typeParameters, - strongModeOnly: true))..parent = procedure; + strongModeOnly: true)) + ..parent = procedure; handleNativeBody(node.body); } @@ -2961,7 +2969,8 @@ class MemberBodyBuilder extends GeneralizingAstVisitor { returnType: node.returnType, typeParameters: scope.buildOptionalTypeParameterList( function.typeParameters, - strongModeOnly: true))..parent = procedure; + strongModeOnly: true)) + ..parent = procedure; handleNativeBody(function.body); } diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart index eca84354e80..19cbda29b07 100644 --- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart +++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart @@ -21,8 +21,7 @@ import 'package:kernel/class_hierarchy.dart' show ClassHierarchy; import 'package:kernel/core_types.dart' show CoreTypes; -import 'package:kernel/frontend/accessors.dart' - show buildIsNull, makeBinary, makeLet; +import 'frontend_accessors.dart' show buildIsNull, makeBinary, makeLet; import '../parser/dart_vm_native.dart' show skipNativeClause; @@ -957,7 +956,8 @@ class BodyBuilder extends ScopeListener implements BuilderHelper { initializer: initializer, type: currentLocalVariableType ?? const DynamicType(), isFinal: isFinal, - isConst: isConst)..fileEqualsOffset = equalsCharOffset); + isConst: isConst) + ..fileEqualsOffset = equalsCharOffset); } @override @@ -1413,7 +1413,8 @@ class BodyBuilder extends ScopeListener implements BuilderHelper { type: type, initializer: name.initializer, isFinal: isFinal, - isConst: isConst)..fileOffset = name.fileOffset; + isConst: isConst) + ..fileOffset = name.fileOffset; } else { addCompileTimeError( name.fileOffset, "'${name.name}' isn't a field in this class."); @@ -1423,7 +1424,8 @@ class BodyBuilder extends ScopeListener implements BuilderHelper { type: type ?? const DynamicType(), initializer: name.initializer, isFinal: isFinal, - isConst: isConst)..fileOffset = name.fileOffset; + isConst: isConst) + ..fileOffset = name.fileOffset; push(variable); } @@ -2016,7 +2018,8 @@ class BodyBuilder extends ScopeListener implements BuilderHelper { "Expected lvalue, but got ${lvalue}", forToken.next.next.charOffset)); } Statement result = new ForInStatement(variable, expression, body, - isAsync: awaitToken != null)..fileOffset = body.fileOffset; + isAsync: awaitToken != null) + ..fileOffset = body.fileOffset; if (breakTarget.hasUsers) { result = new LabeledStatement(result); breakTarget.resolveBreaks(result); @@ -2174,7 +2177,8 @@ class BodyBuilder extends ScopeListener implements BuilderHelper { expressionOffsets.add(expression.fileOffset); } push(new SwitchCase(expressions, expressionOffsets, block, - isDefault: defaultKeyword != null)..fileOffset = firstToken.charOffset); + isDefault: defaultKeyword != null) + ..fileOffset = firstToken.charOffset); push(labels); } diff --git a/pkg/front_end/lib/src/fasta/kernel/fasta_accessors.dart b/pkg/front_end/lib/src/fasta/kernel/fasta_accessors.dart index c6e7e93e694..25336a6ff9b 100644 --- a/pkg/front_end/lib/src/fasta/kernel/fasta_accessors.dart +++ b/pkg/front_end/lib/src/fasta/kernel/fasta_accessors.dart @@ -4,10 +4,9 @@ library fasta.fasta_accessors; -export 'package:kernel/frontend/accessors.dart' show wrapInvalid; +export 'frontend_accessors.dart' show wrapInvalid; -import 'package:kernel/frontend/accessors.dart' - show Accessor, buildIsNull, makeLet; +import 'frontend_accessors.dart' show Accessor, buildIsNull, makeLet; import 'package:kernel/ast.dart'; @@ -15,7 +14,7 @@ import '../errors.dart' show internalError; import '../scope.dart' show AccessErrorBuilder, ProblemBuilder; -import 'package:kernel/frontend/accessors.dart' as kernel +import 'frontend_accessors.dart' as kernel show IndexAccessor, NullAwarePropertyAccessor, 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 00000000000..f91cbbd87c8 --- /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("[]"); + +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([index]), getter) + ..fileOffset = offset; + + _makeSimpleWrite(Expression value, bool voidContext) { + if (!voidContext) return _makeWriteAndReturn(value); + return new MethodInvocation(receiver, indexSetName, + new Arguments([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([indexAccess()]), getter) + ..fileOffset = offset; + } + + _makeWrite(Expression value, bool voidContext) { + if (!voidContext) return _makeWriteAndReturn(value); + return new MethodInvocation(receiverAccess(), indexSetName, + new Arguments([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( + [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([index]), getter); + } + + _makeSimpleWrite(Expression value, bool voidContext) { + if (!voidContext) return _makeWriteAndReturn(value); + return new MethodInvocation(new ThisExpression(), indexSetName, + new Arguments([index, value]), setter); + } + + indexAccess() { + indexVariable ??= new VariableDeclaration.forValue(index); + return new VariableGet(indexVariable); + } + + _makeRead() => builtGetter = new MethodInvocation(new ThisExpression(), + indexGetName, new Arguments([indexAccess()]), getter); + + _makeWrite(Expression value, bool voidContext) { + if (!voidContext) return _makeWriteAndReturn(value); + return new MethodInvocation(new ThisExpression(), indexSetName, + new Arguments([indexAccess(), value]), setter); + } + + _makeWriteAndReturn(Expression value) { + var valueVariable = new VariableDeclaration.forValue(value); + var dummy = new VariableDeclaration.forValue(new MethodInvocation( + new ThisExpression(), + indexSetName, + new Arguments( + [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([index]), getter); + + _makeSimpleWrite(Expression value, bool voidContext) { + if (!voidContext) return _makeWriteAndReturn(value); + return new SuperMethodInvocation( + indexSetName, new Arguments([index, value]), setter); + } + + _makeRead() { + return builtGetter = new SuperMethodInvocation( + indexGetName, new Arguments([indexAccess()]), getter); + } + + _makeWrite(Expression value, bool voidContext) { + if (!voidContext) return _makeWriteAndReturn(value); + return new SuperMethodInvocation(indexSetName, + new Arguments([indexAccess(), value]), setter); + } + + _makeWriteAndReturn(Expression value) { + var valueVariable = new VariableDeclaration.forValue(value); + var dummy = new VariableDeclaration.forValue(new SuperMethodInvocation( + indexSetName, + new Arguments( + [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) { + 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([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()); +} diff --git a/pkg/front_end/lib/src/fasta/names.dart b/pkg/front_end/lib/src/fasta/names.dart index 0253d7d4a5a..d3f7c7be8da 100644 --- a/pkg/front_end/lib/src/fasta/names.dart +++ b/pkg/front_end/lib/src/fasta/names.dart @@ -6,7 +6,7 @@ library front_end.src.fasta.names; import 'package:kernel/ast.dart' show Name; -export 'package:kernel/frontend/accessors.dart' show indexGetName, indexSetName; +export 'kernel/frontend_accessors.dart' show indexGetName, indexSetName; final Name callName = new Name("call");