| // 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 generate expression. |
| library fasta.expression_generator; |
| |
| import 'dart:core' hide MapEntry; |
| |
| import 'package:_fe_analyzer_shared/src/parser/parser.dart' |
| show lengthForToken, lengthOfSpan; |
| |
| import 'package:_fe_analyzer_shared/src/scanner/token.dart' show Token; |
| |
| import 'package:kernel/ast.dart'; |
| |
| import '../builder/builder.dart'; |
| import '../builder/declaration_builder.dart'; |
| import '../builder/extension_builder.dart'; |
| import '../builder/invalid_type_declaration_builder.dart'; |
| import '../builder/member_builder.dart'; |
| import '../builder/named_type_builder.dart'; |
| import '../builder/nullability_builder.dart'; |
| import '../builder/prefix_builder.dart'; |
| import '../builder/type_alias_builder.dart'; |
| import '../builder/type_builder.dart'; |
| import '../builder/type_declaration_builder.dart'; |
| import '../builder/unresolved_type.dart'; |
| |
| import '../constant_context.dart' show ConstantContext; |
| |
| import '../fasta_codes.dart'; |
| |
| import '../names.dart' |
| show |
| ampersandName, |
| barName, |
| callName, |
| caretName, |
| divisionName, |
| equalsName, |
| indexGetName, |
| indexSetName, |
| leftShiftName, |
| lengthName, |
| minusName, |
| multiplyName, |
| mustacheName, |
| percentName, |
| plusName, |
| rightShiftName, |
| tripleShiftName; |
| |
| import '../problems.dart'; |
| |
| import '../scope.dart'; |
| |
| import '../source/stack_listener_impl.dart' show offsetForToken; |
| |
| import 'body_builder.dart' show noLocation; |
| |
| import 'constness.dart' show Constness; |
| |
| import 'expression_generator_helper.dart' show ExpressionGeneratorHelper; |
| |
| import 'forest.dart'; |
| |
| import 'kernel_api.dart' show NameSystem, printNodeOn, printQualifiedNameOn; |
| |
| import 'kernel_ast_api.dart' |
| show |
| Arguments, |
| DartType, |
| DynamicType, |
| Expression, |
| Initializer, |
| Member, |
| Name, |
| Procedure, |
| VariableDeclaration; |
| |
| import 'kernel_builder.dart' show LoadLibraryBuilder; |
| |
| import 'internal_ast.dart'; |
| |
| /// A generator represents a subexpression for which we can't yet build an |
| /// expression because we don't yet know the context in which it's used. |
| /// |
| /// Once the context is known, a generator 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 create a |
| /// [Generator] object. Later, after `= b` is parsed, [buildAssignment] will |
| /// be called. |
| abstract class Generator { |
| /// Helper that provides access to contextual information. |
| final ExpressionGeneratorHelper _helper; |
| |
| /// A token that defines a position subexpression that being built. |
| final Token token; |
| |
| final int fileOffset; |
| |
| Generator(this._helper, this.token) : fileOffset = offsetForToken(token); |
| |
| /// Easy access to the [Forest] factory object. |
| Forest get _forest => _helper.forest; |
| |
| // TODO(johnniwinther): Improve the semantic precision of this property or |
| // remove it. It's unclear if the semantics is inconsistent. It's for instance |
| // used both for the name of a variable in [VariableUseGenerator] and for |
| // `[]` in [IndexedAccessGenerator], and while the former text occurs in the |
| // underlying source code, the latter doesn't. |
| String get _plainNameForRead; |
| |
| /// Internal name used for debugging. |
| String get _debugName; |
| |
| /// The source uri for use in error messaging. |
| Uri get _uri => _helper.uri; |
| |
| /// Builds a [Expression] representing a read from the generator. |
| /// |
| /// The read of this subexpression does _not_ need to support a simultaneous |
| /// write of the same subexpression. |
| Expression buildSimpleRead(); |
| |
| /// Builds a [Expression] representing an assignment with the generator 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}); |
| |
| /// Returns a [Expression] representing a null-aware assignment (`??=`) with |
| /// the generator 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 buildIfNullAssignment(Expression value, DartType type, int offset, |
| {bool voidContext: false}); |
| |
| /// Returns a [Expression] representing a compound assignment (e.g. `+=`) |
| /// with the generator on the LHS and [value] on the RHS. |
| Expression buildCompoundAssignment(Name binaryOperator, Expression value, |
| {int offset: TreeNode.noOffset, |
| bool voidContext: false, |
| bool isPreIncDec: false, |
| bool isPostIncDec: false}); |
| |
| /// Returns a [Expression] representing a pre-increment or pre-decrement of |
| /// the generator. |
| Expression buildPrefixIncrement(Name binaryOperator, |
| {int offset: TreeNode.noOffset, bool voidContext: false}) { |
| return buildCompoundAssignment( |
| binaryOperator, _forest.createIntLiteral(offset, 1), |
| offset: offset, |
| // TODO(johnniwinther): We are missing some void contexts here. For |
| // instance `++a?.b;` is not providing a void context making it default |
| // `true`. |
| voidContext: voidContext, |
| isPreIncDec: true); |
| } |
| |
| /// Returns a [Expression] representing a post-increment or post-decrement of |
| /// the generator. |
| Expression buildPostfixIncrement(Name binaryOperator, |
| {int offset: TreeNode.noOffset, bool voidContext: false}); |
| |
| /// Returns a [Generator] or [Expression] representing an index access |
| /// (e.g. `a[b]`) with the generator on the receiver and [index] as the |
| /// index expression. |
| Generator buildIndexedAccess(Expression index, Token token, |
| {bool isNullAware}); |
| |
| /// Returns a [Expression] representing a compile-time error. |
| /// |
| /// At runtime, an exception will be thrown. |
| Expression _makeInvalidRead() { |
| return _helper.throwNoSuchMethodError(_forest.createNullLiteral(fileOffset), |
| _plainNameForRead, _forest.createArgumentsEmpty(noLocation), fileOffset, |
| isGetter: true); |
| } |
| |
| /// Returns a [Expression] representing a compile-time error wrapping |
| /// [value]. |
| /// |
| /// At runtime, [value] will be evaluated before throwing an exception. |
| Expression _makeInvalidWrite(Expression value) { |
| return _helper.throwNoSuchMethodError( |
| _forest.createNullLiteral(fileOffset), |
| _plainNameForRead, |
| _forest.createArguments(noLocation, <Expression>[value]), |
| fileOffset, |
| isSetter: true); |
| } |
| |
| Expression buildForEffect() => buildSimpleRead(); |
| |
| List<Initializer> buildFieldInitializer(Map<String, int> initializedFields) { |
| return <Initializer>[ |
| _helper.buildInvalidInitializer( |
| _helper.buildProblem( |
| messageInvalidInitializer, fileOffset, lengthForToken(token)), |
| fileOffset) |
| ]; |
| } |
| |
| /// Returns an expression, generator or initializer for an invocation of this |
| /// subexpression with [typeArguments] and [arguments] at [offset]. Callers |
| /// must pass `isInForest: true` iff [typeArguments] have already been added |
| /// to [forest]. |
| /// |
| /// For instance: |
| /// * If this is a [PropertyAccessGenerator] for `a.b`, this will create |
| /// a [MethodInvocation] for `a.b(...)`. |
| /// * If this is a [ThisAccessGenerator] for `this` in an initializer list, |
| /// this will create a [RedirectingInitializer] for `this(...)`. |
| /// * If this is an [IncompleteErrorGenerator], this will return the error |
| /// generator itself. |
| /// |
| /// If the invocation has explicit type arguments |
| /// [buildTypeWithResolvedArguments] called instead. |
| /* Expression | Generator | Initializer */ doInvocation( |
| int offset, List<UnresolvedType> typeArguments, Arguments arguments, |
| {bool isTypeArgumentsInForest = false}); |
| |
| /* Expression | Generator */ buildPropertyAccess( |
| IncompleteSendGenerator send, int operatorOffset, bool isNullAware) { |
| if (send is SendAccessGenerator) { |
| return _helper.buildMethodInvocation(buildSimpleRead(), send.name, |
| send.arguments, offsetForToken(send.token), |
| isNullAware: isNullAware, |
| isConstantExpression: send.isPotentiallyConstant); |
| } else { |
| if (_helper.constantContext != ConstantContext.none && |
| send.name != lengthName) { |
| _helper.addProblem( |
| messageNotAConstantExpression, fileOffset, token.length); |
| } |
| return PropertyAccessGenerator.make( |
| _helper, send.token, buildSimpleRead(), send.name, isNullAware); |
| } |
| } |
| |
| /*Expression | Generator*/ buildEqualsOperation(Token token, Expression right, |
| {bool isNot}) { |
| assert(isNot != null); |
| return _forest.createEquals(offsetForToken(token), buildSimpleRead(), right, |
| isNot: isNot); |
| } |
| |
| /*Expression | Generator*/ buildBinaryOperation( |
| Token token, Name binaryName, Expression right) { |
| return _forest.createBinary( |
| offsetForToken(token), buildSimpleRead(), binaryName, right); |
| } |
| |
| /*Expression | Generator*/ buildUnaryOperation(Token token, Name unaryName) { |
| return _forest.createUnary( |
| offsetForToken(token), unaryName, buildSimpleRead()); |
| } |
| |
| /// Returns a [TypeBuilder] for this subexpression instantiated with the |
| /// type [arguments]. If no type arguments are provided [arguments] is `null`. |
| /// |
| /// The type arguments have not been resolved and should be resolved to |
| /// create a [TypeBuilder] for a valid type. |
| TypeBuilder buildTypeWithResolvedArguments( |
| NullabilityBuilder nullabilityBuilder, List<UnresolvedType> arguments) { |
| // TODO(johnniwinther): Could we use a FixedTypeBuilder(InvalidType()) here? |
| NamedTypeBuilder result = new NamedTypeBuilder( |
| token.lexeme, |
| nullabilityBuilder, |
| /* arguments = */ null, |
| /* fileUri = */ null, |
| /* charOffset = */ null); |
| Message message = templateNotAType.withArguments(token.lexeme); |
| _helper.libraryBuilder |
| .addProblem(message, fileOffset, lengthForToken(token), _uri); |
| result.bind(result.buildInvalidTypeDeclarationBuilder( |
| message.withLocation(_uri, fileOffset, lengthForToken(token)))); |
| return result; |
| } |
| |
| /* Expression | Generator */ Object qualifiedLookup(Token name) { |
| return new UnexpectedQualifiedUseGenerator(_helper, name, this, false); |
| } |
| |
| Expression invokeConstructor( |
| List<UnresolvedType> typeArguments, |
| String name, |
| Arguments arguments, |
| Token nameToken, |
| Token nameLastToken, |
| Constness constness) { |
| if (typeArguments != null) { |
| assert(_forest.argumentsTypeArguments(arguments).isEmpty); |
| _forest.argumentsSetTypeArguments( |
| arguments, _helper.buildDartTypeArguments(typeArguments)); |
| } |
| return _helper.throwNoSuchMethodError( |
| _forest.createNullLiteral(fileOffset), |
| _helper.constructorNameForDiagnostics(name, |
| className: _plainNameForRead), |
| arguments, |
| nameToken.charOffset); |
| } |
| |
| void printOn(StringSink sink); |
| |
| String toString() { |
| StringBuffer buffer = new StringBuffer(); |
| buffer.write(_debugName); |
| buffer.write("(offset: "); |
| buffer.write("${fileOffset}"); |
| printOn(buffer); |
| buffer.write(")"); |
| return "$buffer"; |
| } |
| } |
| |
| /// [VariableUseGenerator] represents the subexpression whose prefix is a |
| /// local variable or parameter name. |
| /// |
| /// For instance: |
| /// |
| /// method(a) { |
| /// var b; |
| /// a; // a VariableUseGenerator is created for `a`. |
| /// b = a[]; // a VariableUseGenerator is created for `a` and `b`. |
| /// b(); // a VariableUseGenerator is created for `b`. |
| /// b.c = a.d; // a VariableUseGenerator is created for `a` and `b`. |
| /// } |
| /// |
| /// If the variable is final or read-only (like a parameter in a catch clause) a |
| /// [ReadOnlyAccessGenerator] is created instead. |
| class VariableUseGenerator extends Generator { |
| final VariableDeclaration variable; |
| |
| final DartType promotedType; |
| |
| VariableUseGenerator( |
| ExpressionGeneratorHelper helper, Token token, this.variable, |
| [this.promotedType]) |
| : assert(variable.isAssignable, 'Variable $variable is not assignable'), |
| super(helper, token); |
| |
| @override |
| String get _debugName => "VariableUseGenerator"; |
| |
| @override |
| String get _plainNameForRead => variable.name; |
| |
| @override |
| Expression buildSimpleRead() { |
| return _createRead(); |
| } |
| |
| Expression _createRead() { |
| return _helper.createVariableGet(variable, fileOffset); |
| } |
| |
| @override |
| Expression buildAssignment(Expression value, {bool voidContext: false}) { |
| return _createWrite(fileOffset, value); |
| } |
| |
| Expression _createWrite(int offset, Expression value) { |
| _helper.registerVariableAssignment(variable); |
| return new VariableSet(variable, value)..fileOffset = offset; |
| } |
| |
| @override |
| Expression buildIfNullAssignment(Expression value, DartType type, int offset, |
| {bool voidContext: false}) { |
| Expression read = _createRead(); |
| Expression write = _createWrite(fileOffset, value); |
| return new IfNullSet(read, write, forEffect: voidContext) |
| ..fileOffset = offset; |
| } |
| |
| @override |
| Expression buildCompoundAssignment(Name binaryOperator, Expression value, |
| {int offset: TreeNode.noOffset, |
| bool voidContext: false, |
| bool isPreIncDec: false, |
| bool isPostIncDec: false}) { |
| Expression binary = _helper.forest |
| .createBinary(offset, _createRead(), binaryOperator, value); |
| return _createWrite(fileOffset, binary); |
| } |
| |
| @override |
| Expression buildPostfixIncrement(Name binaryOperator, |
| {int offset = TreeNode.noOffset, bool voidContext = false}) { |
| Expression value = _forest.createIntLiteral(offset, 1); |
| if (voidContext) { |
| return buildCompoundAssignment(binaryOperator, value, |
| offset: offset, voidContext: voidContext, isPostIncDec: true); |
| } |
| VariableDeclaration read = |
| _helper.forest.createVariableDeclarationForValue(_createRead()); |
| Expression binary = _helper.forest.createBinary(offset, |
| _helper.createVariableGet(read, fileOffset), binaryOperator, value); |
| VariableDeclaration write = _helper.forest |
| .createVariableDeclarationForValue(_createWrite(offset, binary)); |
| return new LocalPostIncDec(read, write)..fileOffset = offset; |
| } |
| |
| @override |
| Expression doInvocation( |
| int offset, List<UnresolvedType> typeArguments, Arguments arguments, |
| {bool isTypeArgumentsInForest = false}) { |
| return _helper.forest.createExpressionInvocation( |
| adjustForImplicitCall(_plainNameForRead, offset), |
| buildSimpleRead(), |
| arguments); |
| } |
| |
| @override |
| Generator buildIndexedAccess(Expression index, Token token, |
| {bool isNullAware}) { |
| assert(isNullAware != null); |
| return new IndexedAccessGenerator(_helper, token, buildSimpleRead(), index, |
| isNullAware: isNullAware); |
| } |
| |
| @override |
| void printOn(StringSink sink) { |
| NameSystem syntheticNames = new NameSystem(); |
| sink.write(", variable: "); |
| printNodeOn(variable, sink, syntheticNames: syntheticNames); |
| sink.write(", promotedType: "); |
| printNodeOn(promotedType, sink, syntheticNames: syntheticNames); |
| } |
| } |
| |
| /// A [PropertyAccessGenerator] represents a subexpression whose prefix is |
| /// an explicit property access. |
| /// |
| /// For instance |
| /// |
| /// method(a) { |
| /// a.b; // a PropertyAccessGenerator is created for `a.b`. |
| /// a.b(); // a PropertyAccessGenerator is created for `a.b`. |
| /// a.b = c; // a PropertyAccessGenerator is created for `a.b`. |
| /// a.b += c; // a PropertyAccessGenerator is created for `a.b`. |
| /// } |
| /// |
| /// If the receiver is `this`, a [ThisPropertyAccessGenerator] is created |
| /// instead. If the access is null-aware, e.g. `a?.b`, a |
| /// [NullAwarePropertyAccessGenerator] is created instead. |
| class PropertyAccessGenerator extends Generator { |
| /// The receiver expression. `a` in the examples in the class documentation. |
| final Expression receiver; |
| |
| /// The name for the accessed property. `b` in the examples in the class |
| /// documentation. |
| final Name name; |
| |
| PropertyAccessGenerator( |
| ExpressionGeneratorHelper helper, Token token, this.receiver, this.name) |
| : super(helper, token); |
| |
| @override |
| String get _debugName => "PropertyAccessGenerator"; |
| |
| @override |
| String get _plainNameForRead => name.text; |
| |
| @override |
| Expression doInvocation( |
| int offset, List<UnresolvedType> typeArguments, Arguments arguments, |
| {bool isTypeArgumentsInForest = false}) { |
| return _helper.buildMethodInvocation(receiver, name, arguments, offset); |
| } |
| |
| @override |
| void printOn(StringSink sink) { |
| NameSystem syntheticNames = new NameSystem(); |
| sink.write(", receiver: "); |
| printNodeOn(receiver, sink, syntheticNames: syntheticNames); |
| sink.write(", name: "); |
| sink.write(name.text); |
| } |
| |
| @override |
| Expression buildSimpleRead() { |
| return _forest.createPropertyGet(fileOffset, receiver, name); |
| } |
| |
| @override |
| Expression buildAssignment(Expression value, {bool voidContext: false}) { |
| return _helper.forest.createPropertySet(fileOffset, receiver, name, value, |
| forEffect: voidContext); |
| } |
| |
| @override |
| Expression buildIfNullAssignment(Expression value, DartType type, int offset, |
| {bool voidContext = false}) { |
| return new IfNullPropertySet(receiver, name, value, |
| forEffect: voidContext, readOffset: fileOffset, writeOffset: fileOffset) |
| ..fileOffset = offset; |
| } |
| |
| @override |
| Expression buildCompoundAssignment(Name binaryOperator, Expression value, |
| {int offset: TreeNode.noOffset, |
| bool voidContext: false, |
| bool isPreIncDec: false, |
| bool isPostIncDec: false}) { |
| return new CompoundPropertySet(receiver, name, binaryOperator, value, |
| forEffect: voidContext, |
| readOnlyReceiver: false, |
| readOffset: fileOffset, |
| binaryOffset: offset, |
| writeOffset: fileOffset) |
| ..fileOffset = offset; |
| } |
| |
| @override |
| Expression buildPostfixIncrement(Name binaryOperator, |
| {int offset = TreeNode.noOffset, bool voidContext = false}) { |
| Expression value = _forest.createIntLiteral(offset, 1); |
| if (voidContext) { |
| return buildCompoundAssignment(binaryOperator, value, |
| offset: offset, voidContext: voidContext, isPostIncDec: true); |
| } |
| VariableDeclaration variable = |
| _helper.forest.createVariableDeclarationForValue(receiver); |
| VariableDeclaration read = _helper.forest.createVariableDeclarationForValue( |
| _forest.createPropertyGet(fileOffset, |
| _helper.createVariableGet(variable, receiver.fileOffset), name)); |
| Expression binary = _helper.forest.createBinary(offset, |
| _helper.createVariableGet(read, fileOffset), binaryOperator, value); |
| VariableDeclaration write = _helper.forest |
| .createVariableDeclarationForValue(_helper.forest.createPropertySet( |
| fileOffset, |
| _helper.createVariableGet(variable, receiver.fileOffset), |
| name, |
| binary, |
| forEffect: true)); |
| return new PropertyPostIncDec(variable, read, write)..fileOffset = offset; |
| } |
| |
| @override |
| Generator buildIndexedAccess(Expression index, Token token, |
| {bool isNullAware}) { |
| assert(isNullAware != null); |
| return new IndexedAccessGenerator(_helper, token, buildSimpleRead(), index, |
| isNullAware: isNullAware); |
| } |
| |
| /// Creates a [Generator] for the access of property [name] on [receiver]. |
| static Generator make(ExpressionGeneratorHelper helper, Token token, |
| Expression receiver, Name name, bool isNullAware) { |
| if (helper.forest.isThisExpression(receiver)) { |
| return new ThisPropertyAccessGenerator(helper, token, name); |
| } else { |
| return isNullAware |
| ? new NullAwarePropertyAccessGenerator(helper, token, receiver, name) |
| : new PropertyAccessGenerator(helper, token, receiver, name); |
| } |
| } |
| } |
| |
| /// A [ThisPropertyAccessGenerator] represents a subexpression whose prefix is |
| /// an implicit or explicit access on `this`. |
| /// |
| /// For instance |
| /// |
| /// class C { |
| /// var b; |
| /// method() { |
| /// b; // a ThisPropertyAccessGenerator is created for `b`. |
| /// b(); // a ThisPropertyAccessGenerator is created for `b`. |
| /// b = c; // a ThisPropertyAccessGenerator is created for `b`. |
| /// b += c; // a ThisPropertyAccessGenerator is created for `b`. |
| /// this.b; // a ThisPropertyAccessGenerator is created for `this.b`. |
| /// this.b(); // a ThisPropertyAccessGenerator is created for `this.b`. |
| /// this.b = c; // a ThisPropertyAccessGenerator is created for `this.b`. |
| /// this.b += c; // a ThisPropertyAccessGenerator is created for `this.b`. |
| /// } |
| /// } |
| /// |
| /// This is a special case of [PropertyAccessGenerator] to avoid creating an |
| /// indirect access to 'this' in for instance `this.b += c` which by |
| /// [PropertyAccessGenerator] would have been created as |
| /// |
| /// let #1 = this in #.b = #.b + c |
| /// |
| /// instead of |
| /// |
| /// this.b = this.b + c |
| /// |
| class ThisPropertyAccessGenerator extends Generator { |
| /// The name for the accessed property. `b` in the examples in the class |
| /// documentation. |
| final Name name; |
| |
| ThisPropertyAccessGenerator( |
| ExpressionGeneratorHelper helper, Token token, this.name) |
| : super(helper, token); |
| |
| @override |
| String get _debugName => "ThisPropertyAccessGenerator"; |
| |
| @override |
| String get _plainNameForRead => name.text; |
| |
| @override |
| Expression buildSimpleRead() { |
| return _createRead(); |
| } |
| |
| Expression _createRead() { |
| return _forest.createPropertyGet( |
| fileOffset, _forest.createThisExpression(fileOffset), name); |
| } |
| |
| @override |
| Expression buildAssignment(Expression value, {bool voidContext: false}) { |
| return _createWrite(fileOffset, value, forEffect: voidContext); |
| } |
| |
| Expression _createWrite(int offset, Expression value, {bool forEffect}) { |
| return _helper.forest.createPropertySet( |
| fileOffset, _forest.createThisExpression(fileOffset), name, value, |
| forEffect: forEffect); |
| } |
| |
| @override |
| Expression buildIfNullAssignment(Expression value, DartType type, int offset, |
| {bool voidContext: false}) { |
| return new IfNullSet( |
| _createRead(), _createWrite(offset, value, forEffect: voidContext), |
| forEffect: voidContext) |
| ..fileOffset = offset; |
| } |
| |
| @override |
| Expression buildCompoundAssignment(Name binaryOperator, Expression value, |
| {int offset: TreeNode.noOffset, |
| bool voidContext: false, |
| bool isPreIncDec: false, |
| bool isPostIncDec: false}) { |
| _helper.forest.createBinary( |
| offset, |
| _forest.createPropertyGet( |
| fileOffset, _forest.createThisExpression(fileOffset), name), |
| binaryOperator, |
| value); |
| Expression binary = _helper.forest.createBinary( |
| offset, |
| _forest.createPropertyGet( |
| fileOffset, _forest.createThisExpression(fileOffset), name), |
| binaryOperator, |
| value); |
| return _createWrite(fileOffset, binary, forEffect: voidContext); |
| } |
| |
| @override |
| Expression buildPostfixIncrement(Name binaryOperator, |
| {int offset = TreeNode.noOffset, bool voidContext = false}) { |
| Expression value = _forest.createIntLiteral(offset, 1); |
| if (voidContext) { |
| return buildCompoundAssignment(binaryOperator, value, |
| offset: offset, voidContext: voidContext, isPostIncDec: true); |
| } |
| VariableDeclaration read = _helper.forest.createVariableDeclarationForValue( |
| _forest.createPropertyGet( |
| fileOffset, _forest.createThisExpression(fileOffset), name)); |
| Expression binary = _helper.forest.createBinary(offset, |
| _helper.createVariableGet(read, fileOffset), binaryOperator, value); |
| VariableDeclaration write = _helper.forest |
| .createVariableDeclarationForValue( |
| _createWrite(fileOffset, binary, forEffect: true)); |
| return new PropertyPostIncDec.onReadOnly(read, write)..fileOffset = offset; |
| } |
| |
| @override |
| Expression doInvocation( |
| int offset, List<UnresolvedType> typeArguments, Arguments arguments, |
| {bool isTypeArgumentsInForest = false}) { |
| return _helper.buildMethodInvocation( |
| _forest.createThisExpression(fileOffset), name, arguments, offset); |
| } |
| |
| @override |
| Generator buildIndexedAccess(Expression index, Token token, |
| {bool isNullAware}) { |
| assert(isNullAware != null); |
| return new IndexedAccessGenerator(_helper, token, buildSimpleRead(), index, |
| isNullAware: isNullAware); |
| } |
| |
| @override |
| void printOn(StringSink sink) { |
| sink.write(", name: "); |
| sink.write(name.text); |
| } |
| } |
| |
| class NullAwarePropertyAccessGenerator extends Generator { |
| final VariableDeclaration receiver; |
| |
| final Expression receiverExpression; |
| |
| final Name name; |
| |
| NullAwarePropertyAccessGenerator(ExpressionGeneratorHelper helper, |
| Token token, this.receiverExpression, this.name) |
| : this.receiver = |
| helper.forest.createVariableDeclarationForValue(receiverExpression), |
| super(helper, token); |
| |
| @override |
| String get _debugName => "NullAwarePropertyAccessGenerator"; |
| |
| Expression receiverAccess() => new VariableGet(receiver); |
| |
| @override |
| String get _plainNameForRead => name.text; |
| |
| @override |
| Expression buildSimpleRead() { |
| VariableDeclaration variable = |
| _helper.forest.createVariableDeclarationForValue(receiverExpression); |
| PropertyGet read = _forest.createPropertyGet( |
| fileOffset, |
| _helper.createVariableGet(variable, receiverExpression.fileOffset, |
| forNullGuardedAccess: true), |
| name); |
| return new NullAwarePropertyGet(variable, read) |
| ..fileOffset = receiverExpression.fileOffset; |
| } |
| |
| @override |
| Expression buildAssignment(Expression value, {bool voidContext = false}) { |
| VariableDeclaration variable = |
| _helper.forest.createVariableDeclarationForValue(receiverExpression); |
| PropertySet read = _helper.forest.createPropertySet( |
| fileOffset, |
| _helper.createVariableGet(variable, receiverExpression.fileOffset, |
| forNullGuardedAccess: true), |
| name, |
| value, |
| forEffect: voidContext, |
| readOnlyReceiver: true); |
| return new NullAwarePropertySet(variable, read) |
| ..fileOffset = receiverExpression.fileOffset; |
| } |
| |
| Expression buildIfNullAssignment(Expression value, DartType type, int offset, |
| {bool voidContext: false}) { |
| return new NullAwareIfNullSet(receiverExpression, name, value, |
| forEffect: voidContext, |
| readOffset: fileOffset, |
| testOffset: offset, |
| writeOffset: fileOffset) |
| ..fileOffset = offset; |
| } |
| |
| Expression buildCompoundAssignment(Name binaryOperator, Expression value, |
| {int offset: TreeNode.noOffset, |
| bool voidContext: false, |
| bool isPreIncDec: false, |
| bool isPostIncDec: false}) { |
| return new NullAwareCompoundSet( |
| receiverExpression, name, binaryOperator, value, |
| readOffset: fileOffset, |
| binaryOffset: offset, |
| writeOffset: fileOffset, |
| forEffect: voidContext, |
| forPostIncDec: isPostIncDec); |
| } |
| |
| Expression buildPostfixIncrement(Name binaryOperator, |
| {int offset: TreeNode.noOffset, bool voidContext: false}) { |
| return buildCompoundAssignment( |
| binaryOperator, _forest.createIntLiteral(offset, 1), |
| offset: offset, voidContext: voidContext, isPostIncDec: true); |
| } |
| |
| @override |
| Expression doInvocation( |
| int offset, List<UnresolvedType> typeArguments, Arguments arguments, |
| {bool isTypeArgumentsInForest = false}) { |
| return unsupported("doInvocation", offset, _uri); |
| } |
| |
| @override |
| Generator buildIndexedAccess(Expression index, Token token, |
| {bool isNullAware}) { |
| assert(isNullAware != null); |
| return new IndexedAccessGenerator(_helper, token, buildSimpleRead(), index, |
| isNullAware: isNullAware); |
| } |
| |
| @override |
| void printOn(StringSink sink) { |
| NameSystem syntheticNames = new NameSystem(); |
| sink.write(", receiver: "); |
| printNodeOn(receiver, sink, syntheticNames: syntheticNames); |
| sink.write(", receiverExpression: "); |
| printNodeOn(receiverExpression, sink, syntheticNames: syntheticNames); |
| sink.write(", name: "); |
| sink.write(name.text); |
| } |
| } |
| |
| class SuperPropertyAccessGenerator extends Generator { |
| final Name name; |
| |
| final Member getter; |
| |
| final Member setter; |
| |
| SuperPropertyAccessGenerator(ExpressionGeneratorHelper helper, Token token, |
| this.name, this.getter, this.setter) |
| : super(helper, token); |
| |
| @override |
| String get _debugName => "SuperPropertyAccessGenerator"; |
| |
| @override |
| String get _plainNameForRead => name.text; |
| |
| @override |
| Expression buildSimpleRead() { |
| return _createRead(); |
| } |
| |
| Expression _createRead() { |
| if (getter == null) { |
| _helper.warnUnresolvedGet(name, fileOffset, isSuper: true); |
| } |
| return new SuperPropertyGet(name, getter)..fileOffset = fileOffset; |
| } |
| |
| @override |
| Expression buildAssignment(Expression value, {bool voidContext = false}) { |
| return _createWrite(fileOffset, value); |
| } |
| |
| Expression _createWrite(int offset, Expression value) { |
| if (setter == null) { |
| _helper.warnUnresolvedSet(name, offset, isSuper: true); |
| } |
| SuperPropertySet write = new SuperPropertySet(name, value, setter) |
| ..fileOffset = offset; |
| return write; |
| } |
| |
| @override |
| Expression buildCompoundAssignment(Name binaryOperator, Expression value, |
| {int offset = TreeNode.noOffset, |
| bool voidContext = false, |
| bool isPreIncDec = false, |
| bool isPostIncDec = false}) { |
| Expression binary = _helper.forest |
| .createBinary(offset, _createRead(), binaryOperator, value); |
| return _createWrite(fileOffset, binary); |
| } |
| |
| @override |
| Expression buildPostfixIncrement(Name binaryOperator, |
| {int offset = TreeNode.noOffset, bool voidContext = false}) { |
| Expression value = _forest.createIntLiteral(offset, 1); |
| if (voidContext) { |
| return buildCompoundAssignment(binaryOperator, value, |
| offset: offset, voidContext: voidContext, isPostIncDec: true); |
| } |
| VariableDeclaration read = |
| _helper.forest.createVariableDeclarationForValue(_createRead()); |
| Expression binary = _helper.forest.createBinary(offset, |
| _helper.createVariableGet(read, fileOffset), binaryOperator, value); |
| VariableDeclaration write = _helper.forest |
| .createVariableDeclarationForValue(_createWrite(fileOffset, binary)); |
| return new StaticPostIncDec(read, write)..fileOffset = offset; |
| } |
| |
| @override |
| Expression buildIfNullAssignment(Expression value, DartType type, int offset, |
| {bool voidContext: false}) { |
| return new IfNullSet(_createRead(), _createWrite(fileOffset, value), |
| forEffect: voidContext) |
| ..fileOffset = offset; |
| } |
| |
| @override |
| Expression doInvocation( |
| int offset, List<UnresolvedType> typeArguments, Arguments arguments, |
| {bool isTypeArgumentsInForest = false}) { |
| if (_helper.constantContext != ConstantContext.none) { |
| // TODO(brianwilkerson) Fix the length |
| _helper.addProblem(messageNotAConstantExpression, offset, 1); |
| } |
| if (getter == null || isFieldOrGetter(getter)) { |
| return _helper.forest |
| .createExpressionInvocation(offset, buildSimpleRead(), arguments); |
| } else { |
| // TODO(ahe): This could be something like "super.property(...)" where |
| // property is a setter. |
| return unhandled("${getter.runtimeType}", "doInvocation", offset, _uri); |
| } |
| } |
| |
| @override |
| Generator buildIndexedAccess(Expression index, Token token, |
| {bool isNullAware}) { |
| assert(isNullAware != null); |
| return new IndexedAccessGenerator(_helper, token, buildSimpleRead(), index, |
| isNullAware: isNullAware); |
| } |
| |
| @override |
| void printOn(StringSink sink) { |
| NameSystem syntheticNames = new NameSystem(); |
| sink.write(", name: "); |
| sink.write(name.text); |
| sink.write(", getter: "); |
| printQualifiedNameOn(getter, sink, syntheticNames: syntheticNames); |
| sink.write(", setter: "); |
| printQualifiedNameOn(setter, sink, syntheticNames: syntheticNames); |
| } |
| } |
| |
| class IndexedAccessGenerator extends Generator { |
| final Expression receiver; |
| |
| final Expression index; |
| |
| final bool isNullAware; |
| |
| IndexedAccessGenerator( |
| ExpressionGeneratorHelper helper, Token token, this.receiver, this.index, |
| {this.isNullAware}) |
| : assert(isNullAware != null), |
| super(helper, token); |
| |
| @override |
| String get _plainNameForRead => "[]"; |
| |
| @override |
| String get _debugName => "IndexedAccessGenerator"; |
| |
| @override |
| Expression buildSimpleRead() { |
| VariableDeclaration variable; |
| Expression receiverValue; |
| if (isNullAware) { |
| variable = _forest.createVariableDeclarationForValue(receiver); |
| receiverValue = _helper.createVariableGet(variable, fileOffset, |
| forNullGuardedAccess: true); |
| } else { |
| receiverValue = receiver; |
| } |
| Expression result = |
| _forest.createIndexGet(fileOffset, receiverValue, index); |
| if (isNullAware) { |
| result = new NullAwareMethodInvocation(variable, result) |
| ..fileOffset = fileOffset; |
| } |
| return result; |
| } |
| |
| @override |
| Expression buildAssignment(Expression value, {bool voidContext: false}) { |
| VariableDeclaration variable; |
| Expression receiverValue; |
| bool readOnlyReceiver; |
| if (isNullAware) { |
| variable = _forest.createVariableDeclarationForValue(receiver); |
| receiverValue = _helper.createVariableGet(variable, fileOffset, |
| forNullGuardedAccess: true); |
| readOnlyReceiver = true; |
| } else { |
| receiverValue = receiver; |
| readOnlyReceiver = false; |
| } |
| Expression result = _forest.createIndexSet( |
| fileOffset, receiverValue, index, value, |
| forEffect: voidContext, readOnlyReceiver: readOnlyReceiver); |
| if (isNullAware) { |
| result = new NullAwareMethodInvocation(variable, result) |
| ..fileOffset = fileOffset; |
| } |
| return result; |
| } |
| |
| @override |
| Expression buildIfNullAssignment(Expression value, DartType type, int offset, |
| {bool voidContext: false}) { |
| VariableDeclaration variable; |
| Expression receiverValue; |
| bool readOnlyReceiver; |
| if (isNullAware) { |
| variable = _forest.createVariableDeclarationForValue(receiver); |
| receiverValue = _helper.createVariableGet(variable, fileOffset, |
| forNullGuardedAccess: true); |
| readOnlyReceiver = true; |
| } else { |
| receiverValue = receiver; |
| readOnlyReceiver = false; |
| } |
| |
| Expression result = new IfNullIndexSet(receiverValue, index, value, |
| readOffset: fileOffset, |
| testOffset: offset, |
| writeOffset: fileOffset, |
| forEffect: voidContext, |
| readOnlyReceiver: readOnlyReceiver) |
| ..fileOffset = offset; |
| if (isNullAware) { |
| result = new NullAwareMethodInvocation(variable, result) |
| ..fileOffset = fileOffset; |
| } |
| return result; |
| } |
| |
| Expression buildCompoundAssignment(Name binaryOperator, Expression value, |
| {int offset: TreeNode.noOffset, |
| bool voidContext: false, |
| bool isPreIncDec: false, |
| bool isPostIncDec: false}) { |
| VariableDeclaration variable; |
| Expression receiverValue; |
| bool readOnlyReceiver; |
| if (isNullAware) { |
| variable = _forest.createVariableDeclarationForValue(receiver); |
| receiverValue = _helper.createVariableGet(variable, fileOffset, |
| forNullGuardedAccess: true); |
| readOnlyReceiver = true; |
| } else { |
| receiverValue = receiver; |
| readOnlyReceiver = false; |
| } |
| |
| Expression result = new CompoundIndexSet( |
| receiverValue, index, binaryOperator, value, |
| readOffset: fileOffset, |
| binaryOffset: offset, |
| writeOffset: fileOffset, |
| forEffect: voidContext, |
| forPostIncDec: isPostIncDec, |
| readOnlyReceiver: readOnlyReceiver); |
| if (isNullAware) { |
| result = new NullAwareMethodInvocation(variable, result) |
| ..fileOffset = fileOffset; |
| } |
| return result; |
| } |
| |
| @override |
| Expression buildPostfixIncrement(Name binaryOperator, |
| {int offset = TreeNode.noOffset, bool voidContext = false}) { |
| Expression value = _forest.createIntLiteral(offset, 1); |
| return buildCompoundAssignment(binaryOperator, value, |
| offset: offset, voidContext: voidContext, isPostIncDec: true); |
| } |
| |
| @override |
| Expression doInvocation( |
| int offset, List<UnresolvedType> typeArguments, Arguments arguments, |
| {bool isTypeArgumentsInForest = false}) { |
| return _helper.forest.createExpressionInvocation( |
| arguments.fileOffset, buildSimpleRead(), arguments); |
| } |
| |
| @override |
| Generator buildIndexedAccess(Expression index, Token token, |
| {bool isNullAware}) { |
| assert(isNullAware != null); |
| return new IndexedAccessGenerator(_helper, token, buildSimpleRead(), index, |
| isNullAware: isNullAware); |
| } |
| |
| @override |
| void printOn(StringSink sink) { |
| NameSystem syntheticNames = new NameSystem(); |
| sink.write(", receiver: "); |
| printNodeOn(receiver, sink, syntheticNames: syntheticNames); |
| sink.write(", index: "); |
| printNodeOn(index, sink, syntheticNames: syntheticNames); |
| sink.write(", isNullAware: ${isNullAware}"); |
| } |
| |
| static Generator make(ExpressionGeneratorHelper helper, Token token, |
| Expression receiver, Expression index, |
| {bool isNullAware}) { |
| assert(isNullAware != null); |
| if (helper.forest.isThisExpression(receiver)) { |
| return new ThisIndexedAccessGenerator(helper, token, index); |
| } else { |
| return new IndexedAccessGenerator(helper, token, receiver, index, |
| isNullAware: isNullAware); |
| } |
| } |
| } |
| |
| /// Special case of [IndexedAccessGenerator] to avoid creating an indirect |
| /// access to 'this'. |
| class ThisIndexedAccessGenerator extends Generator { |
| final Expression index; |
| |
| ThisIndexedAccessGenerator( |
| ExpressionGeneratorHelper helper, Token token, this.index) |
| : super(helper, token); |
| |
| @override |
| String get _plainNameForRead => "[]"; |
| |
| @override |
| String get _debugName => "ThisIndexedAccessGenerator"; |
| |
| @override |
| Expression buildSimpleRead() { |
| Expression receiver = _helper.forest.createThisExpression(fileOffset); |
| return _forest.createIndexGet(fileOffset, receiver, index); |
| } |
| |
| @override |
| Expression buildAssignment(Expression value, {bool voidContext: false}) { |
| Expression receiver = _helper.forest.createThisExpression(fileOffset); |
| return _forest.createIndexSet(fileOffset, receiver, index, value, |
| forEffect: voidContext, readOnlyReceiver: true); |
| } |
| |
| @override |
| Expression buildIfNullAssignment(Expression value, DartType type, int offset, |
| {bool voidContext: false}) { |
| Expression receiver = _helper.forest.createThisExpression(fileOffset); |
| return new IfNullIndexSet(receiver, index, value, |
| readOffset: fileOffset, |
| testOffset: offset, |
| writeOffset: fileOffset, |
| forEffect: voidContext, |
| readOnlyReceiver: true) |
| ..fileOffset = offset; |
| } |
| |
| Expression buildCompoundAssignment(Name binaryOperator, Expression value, |
| {int offset: TreeNode.noOffset, |
| bool voidContext: false, |
| bool isPreIncDec: false, |
| bool isPostIncDec: false}) { |
| Expression receiver = _helper.forest.createThisExpression(fileOffset); |
| return new CompoundIndexSet(receiver, index, binaryOperator, value, |
| readOffset: fileOffset, |
| binaryOffset: offset, |
| writeOffset: fileOffset, |
| forEffect: voidContext, |
| forPostIncDec: isPostIncDec, |
| readOnlyReceiver: true); |
| } |
| |
| @override |
| Expression buildPostfixIncrement(Name binaryOperator, |
| {int offset = TreeNode.noOffset, bool voidContext = false}) { |
| Expression value = _forest.createIntLiteral(offset, 1); |
| return buildCompoundAssignment(binaryOperator, value, |
| offset: offset, voidContext: voidContext, isPostIncDec: true); |
| } |
| |
| @override |
| Expression doInvocation( |
| int offset, List<UnresolvedType> typeArguments, Arguments arguments, |
| {bool isTypeArgumentsInForest = false}) { |
| return _helper.forest |
| .createExpressionInvocation(offset, buildSimpleRead(), arguments); |
| } |
| |
| @override |
| Generator buildIndexedAccess(Expression index, Token token, |
| {bool isNullAware}) { |
| assert(isNullAware != null); |
| return new IndexedAccessGenerator(_helper, token, buildSimpleRead(), index, |
| isNullAware: isNullAware); |
| } |
| |
| @override |
| void printOn(StringSink sink) { |
| NameSystem syntheticNames = new NameSystem(); |
| sink.write(", index: "); |
| printNodeOn(index, sink, syntheticNames: syntheticNames); |
| } |
| } |
| |
| class SuperIndexedAccessGenerator extends Generator { |
| final Expression index; |
| |
| final Member getter; |
| |
| final Member setter; |
| |
| SuperIndexedAccessGenerator(ExpressionGeneratorHelper helper, Token token, |
| this.index, this.getter, this.setter) |
| : super(helper, token); |
| |
| String get _plainNameForRead => "[]"; |
| |
| String get _debugName => "SuperIndexedAccessGenerator"; |
| |
| @override |
| Expression buildSimpleRead() { |
| if (getter == null) { |
| _helper.warnUnresolvedMethod(indexGetName, fileOffset, isSuper: true); |
| } |
| return _helper.forest.createSuperMethodInvocation( |
| fileOffset, |
| indexGetName, |
| getter, |
| _helper.forest.createArguments(fileOffset, <Expression>[index])); |
| } |
| |
| @override |
| Expression buildAssignment(Expression value, {bool voidContext: false}) { |
| if (voidContext) { |
| if (setter == null) { |
| _helper.warnUnresolvedMethod(indexSetName, fileOffset, isSuper: true); |
| } |
| return _helper.forest.createSuperMethodInvocation( |
| fileOffset, |
| indexSetName, |
| setter, |
| _helper.forest |
| .createArguments(fileOffset, <Expression>[index, value])); |
| } else { |
| return new SuperIndexSet(setter, index, value)..fileOffset = fileOffset; |
| } |
| } |
| |
| @override |
| Expression buildIfNullAssignment(Expression value, DartType type, int offset, |
| {bool voidContext: false}) { |
| return new IfNullSuperIndexSet(getter, setter, index, value, |
| readOffset: fileOffset, |
| testOffset: offset, |
| writeOffset: fileOffset, |
| forEffect: voidContext) |
| ..fileOffset = offset; |
| } |
| |
| Expression buildCompoundAssignment(Name binaryOperator, Expression value, |
| {int offset: TreeNode.noOffset, |
| bool voidContext: false, |
| bool isPreIncDec: false, |
| bool isPostIncDec: false}) { |
| return new CompoundSuperIndexSet( |
| getter, setter, index, binaryOperator, value, |
| readOffset: fileOffset, |
| binaryOffset: offset, |
| writeOffset: fileOffset, |
| forEffect: voidContext, |
| forPostIncDec: isPostIncDec); |
| } |
| |
| @override |
| Expression buildPostfixIncrement(Name binaryOperator, |
| {int offset = TreeNode.noOffset, bool voidContext = false}) { |
| Expression value = _forest.createIntLiteral(offset, 1); |
| return buildCompoundAssignment(binaryOperator, value, |
| offset: offset, voidContext: voidContext, isPostIncDec: true); |
| } |
| |
| @override |
| Expression doInvocation( |
| int offset, List<UnresolvedType> typeArguments, Arguments arguments, |
| {bool isTypeArgumentsInForest = false}) { |
| return _helper.forest |
| .createExpressionInvocation(offset, buildSimpleRead(), arguments); |
| } |
| |
| @override |
| Generator buildIndexedAccess(Expression index, Token token, |
| {bool isNullAware}) { |
| assert(isNullAware != null); |
| return new IndexedAccessGenerator(_helper, token, buildSimpleRead(), index, |
| isNullAware: isNullAware); |
| } |
| |
| @override |
| void printOn(StringSink sink) { |
| NameSystem syntheticNames = new NameSystem(); |
| sink.write(", index: "); |
| printNodeOn(index, sink, syntheticNames: syntheticNames); |
| sink.write(", getter: "); |
| printQualifiedNameOn(getter, sink, syntheticNames: syntheticNames); |
| sink.write(", setter: "); |
| printQualifiedNameOn(setter, sink, syntheticNames: syntheticNames); |
| } |
| } |
| |
| /// A [StaticAccessGenerator] represents a subexpression whose prefix is |
| /// a static or top-level member, including static extension members. |
| /// |
| /// For instance |
| /// |
| /// get property => 0; |
| /// set property(_) {} |
| /// var field; |
| /// method() {} |
| /// |
| /// main() { |
| /// property; // a StaticAccessGenerator is created for `property`. |
| /// property = 0; // a StaticAccessGenerator is created for `property`. |
| /// field = 0; // a StaticAccessGenerator is created for `field`. |
| /// method; // a StaticAccessGenerator is created for `method`. |
| /// method(); // a StaticAccessGenerator is created for `method`. |
| /// } |
| /// |
| /// class A {} |
| /// extension B on A { |
| /// static get property => 0; |
| /// static set property(_) {} |
| /// static var field; |
| /// static method() { |
| /// property; // this StaticAccessGenerator is created for `property`. |
| /// property = 0; // this StaticAccessGenerator is created for `property`. |
| /// field = 0; // this StaticAccessGenerator is created for `field`. |
| /// method; // this StaticAccessGenerator is created for `method`. |
| /// method(); // this StaticAccessGenerator is created for `method`. |
| /// } |
| /// } |
| /// |
| class StaticAccessGenerator extends Generator { |
| /// The name of the original target; |
| final String targetName; |
| |
| /// The static [Member] used for performing a read or invocation on this |
| /// subexpression. |
| /// |
| /// This can be `null` if the subexpression doesn't have a readable target. |
| /// For instance if the subexpression is a setter without a corresponding |
| /// getter. |
| final Member readTarget; |
| |
| /// The static [Member] used for performing a write on this subexpression. |
| /// |
| /// This can be `null` if the subexpression doesn't have a writable target. |
| /// For instance if the subexpression is a final field, a method, or a getter |
| /// without a corresponding setter. |
| final Member writeTarget; |
| |
| StaticAccessGenerator(ExpressionGeneratorHelper helper, Token token, |
| this.targetName, this.readTarget, this.writeTarget) |
| : assert(targetName != null), |
| assert(readTarget != null || writeTarget != null), |
| super(helper, token); |
| |
| factory StaticAccessGenerator.fromBuilder( |
| ExpressionGeneratorHelper helper, |
| String targetName, |
| Token token, |
| MemberBuilder getterBuilder, |
| MemberBuilder setterBuilder) { |
| return new StaticAccessGenerator(helper, token, targetName, |
| getterBuilder?.readTarget, setterBuilder?.writeTarget); |
| } |
| |
| @override |
| String get _debugName => "StaticAccessGenerator"; |
| |
| @override |
| String get _plainNameForRead => targetName; |
| |
| @override |
| Expression buildSimpleRead() { |
| return _createRead(); |
| } |
| |
| Expression _createRead() { |
| Expression read; |
| if (readTarget == null) { |
| read = _makeInvalidRead(); |
| } else { |
| read = _helper.makeStaticGet(readTarget, token); |
| } |
| return read; |
| } |
| |
| @override |
| Expression buildAssignment(Expression value, {bool voidContext = false}) { |
| return _createWrite(fileOffset, value); |
| } |
| |
| Expression _createWrite(int offset, Expression value) { |
| Expression write; |
| if (writeTarget == null) { |
| write = _makeInvalidWrite(value); |
| } else { |
| write = new StaticSet(writeTarget, value)..fileOffset = offset; |
| } |
| return write; |
| } |
| |
| @override |
| Expression buildIfNullAssignment(Expression value, DartType type, int offset, |
| {bool voidContext: false}) { |
| return new IfNullSet(_createRead(), _createWrite(offset, value), |
| forEffect: voidContext) |
| ..fileOffset = offset; |
| } |
| |
| @override |
| Expression buildCompoundAssignment(Name binaryOperator, Expression value, |
| {int offset = TreeNode.noOffset, |
| bool voidContext = false, |
| bool isPreIncDec = false, |
| bool isPostIncDec = false}) { |
| Expression binary = _helper.forest |
| .createBinary(offset, _createRead(), binaryOperator, value); |
| return _createWrite(fileOffset, binary); |
| } |
| |
| @override |
| Expression buildPostfixIncrement(Name binaryOperator, |
| {int offset = TreeNode.noOffset, bool voidContext = false}) { |
| Expression value = _forest.createIntLiteral(offset, 1); |
| if (voidContext) { |
| return buildCompoundAssignment(binaryOperator, value, |
| offset: offset, voidContext: voidContext, isPostIncDec: true); |
| } |
| VariableDeclaration read = |
| _helper.forest.createVariableDeclarationForValue(_createRead()); |
| Expression binary = _helper.forest.createBinary(offset, |
| _helper.createVariableGet(read, fileOffset), binaryOperator, value); |
| VariableDeclaration write = _helper.forest |
| .createVariableDeclarationForValue(_createWrite(offset, binary)); |
| return new StaticPostIncDec(read, write)..fileOffset = offset; |
| } |
| |
| @override |
| Expression doInvocation( |
| int offset, List<UnresolvedType> typeArguments, Arguments arguments, |
| {bool isTypeArgumentsInForest = false}) { |
| if (_helper.constantContext != ConstantContext.none && |
| !_helper.isIdentical(readTarget)) { |
| return _helper.buildProblem( |
| templateNotConstantExpression.withArguments('Method invocation'), |
| offset, |
| readTarget?.name?.text?.length ?? 0); |
| } |
| if (readTarget == null || isFieldOrGetter(readTarget)) { |
| return _helper.forest.createExpressionInvocation( |
| offset + (readTarget?.name?.text?.length ?? 0), |
| buildSimpleRead(), |
| arguments); |
| } else { |
| return _helper.buildStaticInvocation(readTarget, arguments, |
| charOffset: offset); |
| } |
| } |
| |
| @override |
| Generator buildIndexedAccess(Expression index, Token token, |
| {bool isNullAware}) { |
| assert(isNullAware != null); |
| return new IndexedAccessGenerator(_helper, token, buildSimpleRead(), index, |
| isNullAware: isNullAware); |
| } |
| |
| @override |
| void printOn(StringSink sink) { |
| NameSystem syntheticNames = new NameSystem(); |
| sink.write(", targetName: "); |
| sink.write(targetName); |
| sink.write(", readTarget: "); |
| printQualifiedNameOn(readTarget, sink, syntheticNames: syntheticNames); |
| sink.write(", writeTarget: "); |
| printQualifiedNameOn(writeTarget, sink, syntheticNames: syntheticNames); |
| } |
| } |
| |
| /// An [ExtensionInstanceAccessGenerator] represents a subexpression whose |
| /// prefix is an extension instance member. |
| /// |
| /// For instance |
| /// |
| /// class A {} |
| /// extension B on A { |
| /// get property => 0; |
| /// set property(_) {} |
| /// method() { |
| /// property; // this generator is created for `property`. |
| /// property = 0; // this generator is created for `property`. |
| /// method; // this generator is created for `method`. |
| /// method(); // this generator is created for `method`. |
| /// } |
| /// } |
| /// |
| /// These can only occur within an extension instance member. |
| class ExtensionInstanceAccessGenerator extends Generator { |
| final Extension extension; |
| |
| /// The original name of the target. |
| final String targetName; |
| |
| /// The static [Member] generated for an instance extension member which is |
| /// used for performing a read on this subexpression. |
| /// |
| /// This can be `null` if the subexpression doesn't have a readable target. |
| /// For instance if the subexpression is a setter without a corresponding |
| /// getter. |
| final Procedure readTarget; |
| |
| /// The static [Member] generated for an instance extension member which is |
| /// used for performing an invocation on this subexpression. |
| /// |
| /// This can be `null` if the subexpression doesn't have an invokable target. |
| /// For instance if the subexpression is a getter or setter. |
| final Procedure invokeTarget; |
| |
| /// The static [Member] generated for an instance extension member which is |
| /// used for performing a write on this subexpression. |
| /// |
| /// This can be `null` if the subexpression doesn't have a writable target. |
| /// For instance if the subexpression is a final field, a method, or a getter |
| /// without a corresponding setter. |
| final Procedure writeTarget; |
| |
| /// The parameter holding the value for `this` within the current extension |
| /// instance method. |
| // TODO(johnniwinther): Handle static access to extension instance members, |
| // in which case the access is erroneous and [extensionThis] is `null`. |
| final VariableDeclaration extensionThis; |
| |
| /// The type parameters synthetically added to the current extension |
| /// instance method. |
| final List<TypeParameter> extensionTypeParameters; |
| |
| ExtensionInstanceAccessGenerator( |
| ExpressionGeneratorHelper helper, |
| Token token, |
| this.extension, |
| this.targetName, |
| this.readTarget, |
| this.invokeTarget, |
| this.writeTarget, |
| this.extensionThis, |
| this.extensionTypeParameters) |
| : assert(targetName != null), |
| assert( |
| readTarget != null || invokeTarget != null || writeTarget != null), |
| assert(extensionThis != null), |
| super(helper, token); |
| |
| factory ExtensionInstanceAccessGenerator.fromBuilder( |
| ExpressionGeneratorHelper helper, |
| Token token, |
| Extension extension, |
| String targetName, |
| VariableDeclaration extensionThis, |
| List<TypeParameter> extensionTypeParameters, |
| MemberBuilder getterBuilder, |
| MemberBuilder setterBuilder) { |
| Procedure readTarget; |
| Procedure invokeTarget; |
| if (getterBuilder != null) { |
| if (getterBuilder.isGetter) { |
| assert(!getterBuilder.isStatic); |
| readTarget = getterBuilder.readTarget; |
| } else if (getterBuilder.isRegularMethod) { |
| assert(!getterBuilder.isStatic); |
| readTarget = getterBuilder.readTarget; |
| invokeTarget = getterBuilder.invokeTarget; |
| } else if (getterBuilder.isOperator) { |
| assert(!getterBuilder.isStatic); |
| invokeTarget = getterBuilder.invokeTarget; |
| } |
| } |
| Procedure writeTarget; |
| if (setterBuilder != null) { |
| if (setterBuilder.isSetter) { |
| assert(!setterBuilder.isStatic); |
| writeTarget = setterBuilder.writeTarget; |
| targetName ??= setterBuilder.name; |
| } else { |
| return unhandled( |
| "${setterBuilder.runtimeType}", |
| "ExtensionInstanceAccessGenerator.fromBuilder", |
| offsetForToken(token), |
| helper.uri); |
| } |
| } |
| return new ExtensionInstanceAccessGenerator( |
| helper, |
| token, |
| extension, |
| targetName, |
| readTarget, |
| invokeTarget, |
| writeTarget, |
| extensionThis, |
| extensionTypeParameters); |
| } |
| |
| @override |
| String get _debugName => "InstanceExtensionAccessGenerator"; |
| |
| @override |
| String get _plainNameForRead => targetName; |
| |
| int get _extensionTypeParameterCount => extensionTypeParameters?.length ?? 0; |
| |
| List<DartType> _createExtensionTypeArguments() { |
| List<DartType> extensionTypeArguments = const <DartType>[]; |
| if (extensionTypeParameters != null) { |
| extensionTypeArguments = []; |
| for (TypeParameter typeParameter in extensionTypeParameters) { |
| extensionTypeArguments.add( |
| _forest.createTypeParameterTypeWithDefaultNullabilityForLibrary( |
| typeParameter, extension.enclosingLibrary)); |
| } |
| } |
| return extensionTypeArguments; |
| } |
| |
| @override |
| Expression buildSimpleRead() { |
| return _createRead(); |
| } |
| |
| Expression _createRead() { |
| Expression read; |
| if (readTarget == null) { |
| read = _makeInvalidRead(); |
| } else { |
| read = _helper.buildExtensionMethodInvocation( |
| fileOffset, |
| readTarget, |
| _helper.forest.createArgumentsForExtensionMethod( |
| fileOffset, |
| _extensionTypeParameterCount, |
| 0, |
| _helper.createVariableGet(extensionThis, fileOffset), |
| extensionTypeArguments: _createExtensionTypeArguments()), |
| isTearOff: invokeTarget != null); |
| } |
| return read; |
| } |
| |
| @override |
| Expression buildAssignment(Expression value, {bool voidContext: false}) { |
| return _createWrite(fileOffset, value, forEffect: voidContext); |
| } |
| |
| Expression _createWrite(int offset, Expression value, {bool forEffect}) { |
| Expression write; |
| if (writeTarget == null) { |
| write = _makeInvalidWrite(value); |
| } else { |
| write = new ExtensionSet( |
| extension, |
| _createExtensionTypeArguments(), |
| _helper.createVariableGet(extensionThis, fileOffset), |
| writeTarget, |
| value, |
| forEffect: forEffect, |
| readOnlyReceiver: true); |
| } |
| write.fileOffset = offset; |
| return write; |
| } |
| |
| @override |
| Expression buildIfNullAssignment(Expression value, DartType type, int offset, |
| {bool voidContext: false}) { |
| return new IfNullSet( |
| _createRead(), _createWrite(fileOffset, value, forEffect: voidContext), |
| forEffect: voidContext) |
| ..fileOffset = offset; |
| } |
| |
| @override |
| Expression buildCompoundAssignment(Name binaryOperator, Expression value, |
| {int offset: TreeNode.noOffset, |
| bool voidContext: false, |
| bool isPreIncDec: false, |
| bool isPostIncDec: false}) { |
| Expression binary = _helper.forest |
| .createBinary(offset, _createRead(), binaryOperator, value); |
| return _createWrite(fileOffset, binary, forEffect: voidContext); |
| } |
| |
| @override |
| Expression buildPostfixIncrement(Name binaryOperator, |
| {int offset = TreeNode.noOffset, bool voidContext = false}) { |
| Expression value = _forest.createIntLiteral(offset, 1); |
| if (voidContext) { |
| return buildCompoundAssignment(binaryOperator, value, |
| offset: offset, voidContext: voidContext, isPostIncDec: true); |
| } |
| VariableDeclaration read = |
| _helper.forest.createVariableDeclarationForValue(_createRead()); |
| Expression binary = _helper.forest.createBinary(offset, |
| _helper.createVariableGet(read, fileOffset), binaryOperator, value); |
| VariableDeclaration write = _helper.forest |
| .createVariableDeclarationForValue( |
| _createWrite(fileOffset, binary, forEffect: true)); |
| return new PropertyPostIncDec.onReadOnly(read, write)..fileOffset = offset; |
| } |
| |
| @override |
| Expression doInvocation( |
| int offset, List<UnresolvedType> typeArguments, Arguments arguments, |
| {bool isTypeArgumentsInForest = false}) { |
| if (invokeTarget != null) { |
| return _helper.buildExtensionMethodInvocation( |
| offset, |
| invokeTarget, |
| _forest.createArgumentsForExtensionMethod( |
| fileOffset, |
| _extensionTypeParameterCount, |
| invokeTarget.function.typeParameters.length - |
| _extensionTypeParameterCount, |
| _helper.createVariableGet(extensionThis, offset), |
| extensionTypeArguments: _createExtensionTypeArguments(), |
| typeArguments: arguments.types, |
| positionalArguments: arguments.positional, |
| namedArguments: arguments.named), |
| isTearOff: false); |
| } else { |
| return _helper.forest.createExpressionInvocation( |
| adjustForImplicitCall(_plainNameForRead, offset), |
| buildSimpleRead(), |
| arguments); |
| } |
| } |
| |
| @override |
| Generator buildIndexedAccess(Expression index, Token token, |
| {bool isNullAware}) { |
| assert(isNullAware != null); |
| return new IndexedAccessGenerator(_helper, token, buildSimpleRead(), index, |
| isNullAware: isNullAware); |
| } |
| |
| @override |
| void printOn(StringSink sink) { |
| NameSystem syntheticNames = new NameSystem(); |
| sink.write(", targetName: "); |
| sink.write(targetName); |
| sink.write(", readTarget: "); |
| printQualifiedNameOn(readTarget, sink, syntheticNames: syntheticNames); |
| sink.write(", writeTarget: "); |
| printQualifiedNameOn(writeTarget, sink, syntheticNames: syntheticNames); |
| } |
| } |
| |
| /// A [ExplicitExtensionInstanceAccessGenerator] represents a subexpression |
| /// whose prefix is a forced extension instance member access. |
| /// |
| /// For instance |
| /// |
| /// class A<T> {} |
| /// extension B on A<int> { |
| /// method() {} |
| /// } |
| /// extension C<T> { |
| /// T get field => 0; |
| /// set field(T _) {} |
| /// } |
| /// |
| /// method(A a) { |
| /// B(a).method; // this generator is created for `B(a).method`. |
| /// B(a).method(); // this generator is created for `B(a).method`. |
| /// C<int>(a).field; // this generator is created for `C<int>(a).field`. |
| /// C(a).field = 0; // this generator is created for `C(a).field`. |
| /// } |
| /// |
| class ExplicitExtensionInstanceAccessGenerator extends Generator { |
| /// The file offset used for the explicit extension application type |
| /// arguments. |
| final int extensionTypeArgumentOffset; |
| |
| final Extension extension; |
| |
| /// The name of the original target; |
| final Name targetName; |
| |
| /// The static [Member] generated for an instance extension member which is |
| /// used for performing a read on this subexpression. |
| /// |
| /// This can be `null` if the subexpression doesn't have a readable target. |
| /// For instance if the subexpression is a setter without a corresponding |
| /// getter. |
| final Procedure readTarget; |
| |
| /// The static [Member] generated for an instance extension member which is |
| /// used for performing an invocation on this subexpression. |
| /// |
| /// This can be `null` if the subexpression doesn't have an invokable target. |
| /// For instance if the subexpression is a getter or setter. |
| final Procedure invokeTarget; |
| |
| /// The static [Member] generated for an instance extension member which is |
| /// used for performing a write on this subexpression. |
| /// |
| /// This can be `null` if the subexpression doesn't have a writable target. |
| /// For instance if the subexpression is a final field, a method, or a getter |
| /// without a corresponding setter. |
| final Procedure writeTarget; |
| |
| /// The expression holding the receiver value for the explicit extension |
| /// access, that is, `a` in `Extension<int>(a).method<String>()`. |
| final Expression receiver; |
| |
| /// The type arguments explicitly passed to the explicit extension access, |
| /// like `<int>` in `Extension<int>(a).method<String>()`. |
| final List<DartType> explicitTypeArguments; |
| |
| /// The number of type parameters declared on the extension declaration. |
| final int extensionTypeParameterCount; |
| |
| /// If `true` the access is null-aware, like `Extension(c)?.foo`. |
| final bool isNullAware; |
| |
| ExplicitExtensionInstanceAccessGenerator( |
| ExpressionGeneratorHelper helper, |
| Token token, |
| this.extensionTypeArgumentOffset, |
| this.extension, |
| this.targetName, |
| this.readTarget, |
| this.invokeTarget, |
| this.writeTarget, |
| this.receiver, |
| this.explicitTypeArguments, |
| this.extensionTypeParameterCount, |
| {this.isNullAware}) |
| : assert(targetName != null), |
| assert( |
| readTarget != null || invokeTarget != null || writeTarget != null), |
| assert(receiver != null), |
| assert(isNullAware != null), |
| super(helper, token); |
| |
| factory ExplicitExtensionInstanceAccessGenerator.fromBuilder( |
| ExpressionGeneratorHelper helper, |
| Token token, |
| int extensionTypeArgumentOffset, |
| Extension extension, |
| Name targetName, |
| Builder getterBuilder, |
| Builder setterBuilder, |
| Expression receiver, |
| List<DartType> explicitTypeArguments, |
| int extensionTypeParameterCount, |
| {bool isNullAware}) { |
| assert(getterBuilder != null || setterBuilder != null); |
| Procedure readTarget; |
| Procedure invokeTarget; |
| if (getterBuilder != null) { |
| assert(!getterBuilder.isStatic); |
| if (getterBuilder is AccessErrorBuilder) { |
| AccessErrorBuilder error = getterBuilder; |
| getterBuilder = error.builder; |
| // We should only see an access error here if we've looked up a setter |
| // when not explicitly looking for a setter. |
| assert(getterBuilder.isSetter); |
| } else if (getterBuilder.isGetter) { |
| assert(!getterBuilder.isStatic); |
| MemberBuilder memberBuilder = getterBuilder; |
| readTarget = memberBuilder.readTarget; |
| } else if (getterBuilder.isRegularMethod) { |
| assert(!getterBuilder.isStatic); |
| MemberBuilder procedureBuilder = getterBuilder; |
| readTarget = procedureBuilder.readTarget; |
| invokeTarget = procedureBuilder.invokeTarget; |
| } else if (getterBuilder.isOperator) { |
| assert(!getterBuilder.isStatic); |
| MemberBuilder memberBuilder = getterBuilder; |
| invokeTarget = memberBuilder.invokeTarget; |
| } else { |
| return unhandled( |
| "$getterBuilder (${getterBuilder.runtimeType})", |
| "InstanceExtensionAccessGenerator.fromBuilder", |
| offsetForToken(token), |
| helper.uri); |
| } |
| } |
| Procedure writeTarget; |
| if (setterBuilder != null) { |
| assert(!setterBuilder.isStatic); |
| if (setterBuilder is AccessErrorBuilder) { |
| // No setter. |
| } else if (setterBuilder.isSetter) { |
| assert(!setterBuilder.isStatic); |
| MemberBuilder memberBuilder = setterBuilder; |
| writeTarget = memberBuilder.writeTarget; |
| } else { |
| return unhandled( |
| "$setterBuilder (${setterBuilder.runtimeType})", |
| "InstanceExtensionAccessGenerator.fromBuilder", |
| offsetForToken(token), |
| helper.uri); |
| } |
| } |
| return new ExplicitExtensionInstanceAccessGenerator( |
| helper, |
| token, |
| extensionTypeArgumentOffset, |
| extension, |
| targetName, |
| readTarget, |
| invokeTarget, |
| writeTarget, |
| receiver, |
| explicitTypeArguments, |
| extensionTypeParameterCount, |
| isNullAware: isNullAware); |
| } |
| |
| @override |
| String get _debugName => "ExplicitExtensionIndexedAccessGenerator"; |
| |
| @override |
| String get _plainNameForRead => targetName.text; |
| |
| List<DartType> _createExtensionTypeArguments() { |
| return explicitTypeArguments ?? const <DartType>[]; |
| } |
| |
| /// Returns `true` if performing a read operation is a tear off. |
| /// |
| /// This is the case if [invokeTarget] is non-null, since extension methods |
| /// have both a [readTarget] and an [invokeTarget], whereas extension getters |
| /// only have a [readTarget]. |
| bool get isReadTearOff => invokeTarget != null; |
| |
| @override |
| Expression buildSimpleRead() { |
| if (isNullAware) { |
| VariableDeclaration variable = |
| _helper.forest.createVariableDeclarationForValue(receiver); |
| return new NullAwareExtension( |
| variable, |
| _createRead(_helper.createVariableGet(variable, variable.fileOffset, |
| forNullGuardedAccess: true))) |
| ..fileOffset = fileOffset; |
| } else { |
| return _createRead(receiver); |
| } |
| } |
| |
| Expression _createRead(Expression receiver) { |
| Expression read; |
| if (readTarget == null) { |
| read = _makeInvalidRead(); |
| } else { |
| read = _helper.buildExtensionMethodInvocation( |
| fileOffset, |
| readTarget, |
| _helper.forest.createArgumentsForExtensionMethod( |
| fileOffset, extensionTypeParameterCount, 0, receiver, |
| extensionTypeArguments: _createExtensionTypeArguments(), |
| extensionTypeArgumentOffset: extensionTypeArgumentOffset), |
| isTearOff: isReadTearOff); |
| } |
| return read; |
| } |
| |
| @override |
| Expression buildAssignment(Expression value, {bool voidContext: false}) { |
| if (isNullAware) { |
| VariableDeclaration variable = |
| _helper.forest.createVariableDeclarationForValue(receiver); |
| return new NullAwareExtension( |
| variable, |
| _createWrite( |
| fileOffset, |
| _helper.createVariableGet(variable, variable.fileOffset, |
| forNullGuardedAccess: true), |
| value, |
| forEffect: voidContext, |
| readOnlyReceiver: true)) |
| ..fileOffset = fileOffset; |
| } else { |
| return _createWrite(fileOffset, receiver, value, |
| forEffect: voidContext, readOnlyReceiver: false); |
| } |
| } |
| |
| Expression _createWrite(int offset, Expression receiver, Expression value, |
| {bool readOnlyReceiver, bool forEffect}) { |
| Expression write; |
| if (writeTarget == null) { |
| write = _makeInvalidWrite(value); |
| } else { |
| write = new ExtensionSet( |
| extension, explicitTypeArguments, receiver, writeTarget, value, |
| readOnlyReceiver: readOnlyReceiver, forEffect: forEffect); |
| } |
| write.fileOffset = offset; |
| return write; |
| } |
| |
| @override |
| Expression buildIfNullAssignment(Expression value, DartType type, int offset, |
| {bool voidContext: false}) { |
| if (isNullAware) { |
| VariableDeclaration variable = |
| _helper.forest.createVariableDeclarationForValue(receiver); |
| Expression read = _createRead(_helper.createVariableGet( |
| variable, receiver.fileOffset, |
| forNullGuardedAccess: true)); |
| Expression write = _createWrite( |
| fileOffset, |
| _helper.createVariableGet(variable, receiver.fileOffset, |
| forNullGuardedAccess: true), |
| value, |
| forEffect: voidContext, |
| readOnlyReceiver: true); |
| return new NullAwareExtension( |
| variable, |
| new IfNullSet(read, write, forEffect: voidContext) |
| ..fileOffset = offset) |
| ..fileOffset = fileOffset; |
| } else { |
| return new IfNullPropertySet(receiver, targetName, value, |
| forEffect: voidContext, |
| readOffset: fileOffset, |
| writeOffset: fileOffset) |
| ..fileOffset = offset; |
| } |
| } |
| |
| @override |
| Expression buildCompoundAssignment(Name binaryOperator, Expression value, |
| {int offset: TreeNode.noOffset, |
| bool voidContext: false, |
| bool isPreIncDec: false, |
| bool isPostIncDec: false}) { |
| if (isNullAware) { |
| VariableDeclaration variable = |
| _helper.forest.createVariableDeclarationForValue(receiver); |
| Expression binary = _helper.forest.createBinary( |
| offset, |
| _createRead(_helper.createVariableGet(variable, receiver.fileOffset, |
| forNullGuardedAccess: true)), |
| binaryOperator, |
| value); |
| Expression write = _createWrite( |
| fileOffset, |
| _helper.createVariableGet(variable, receiver.fileOffset, |
| forNullGuardedAccess: true), |
| binary, |
| forEffect: voidContext, |
| readOnlyReceiver: true); |
| return new NullAwareExtension(variable, write)..fileOffset = offset; |
| } else { |
| return new CompoundExtensionSet(extension, explicitTypeArguments, |
| receiver, targetName, readTarget, binaryOperator, value, writeTarget, |
| readOnlyReceiver: false, |
| forEffect: voidContext, |
| readOffset: fileOffset, |
| binaryOffset: offset, |
| writeOffset: fileOffset) |
| ..fileOffset = offset; |
| } |
| } |
| |
| @override |
| Expression buildPostfixIncrement(Name binaryOperator, |
| {int offset = TreeNode.noOffset, bool voidContext = false}) { |
| Expression value = _forest.createIntLiteral(offset, 1); |
| if (voidContext) { |
| return buildCompoundAssignment(binaryOperator, value, |
| offset: offset, voidContext: voidContext, isPostIncDec: true); |
| } else if (isNullAware) { |
| VariableDeclaration variable = |
| _helper.forest.createVariableDeclarationForValue(receiver); |
| VariableDeclaration read = _helper.forest |
| .createVariableDeclarationForValue(_createRead( |
| _helper.createVariableGet(variable, receiver.fileOffset, |
| forNullGuardedAccess: true))); |
| Expression binary = _helper.forest.createBinary(offset, |
| _helper.createVariableGet(read, fileOffset), binaryOperator, value); |
| VariableDeclaration write = _helper.forest |
| .createVariableDeclarationForValue(_createWrite( |
| fileOffset, |
| _helper.createVariableGet(variable, receiver.fileOffset, |
| forNullGuardedAccess: true), |
| binary, |
| forEffect: voidContext, |
| readOnlyReceiver: true) |
| ..fileOffset = fileOffset); |
| return new NullAwareExtension( |
| variable, new LocalPostIncDec(read, write)..fileOffset = offset) |
| ..fileOffset = fileOffset; |
| } else { |
| VariableDeclaration variable = |
| _helper.forest.createVariableDeclarationForValue(receiver); |
| VariableDeclaration read = _helper.forest |
| .createVariableDeclarationForValue(_createRead( |
| _helper.createVariableGet(variable, receiver.fileOffset))); |
| Expression binary = _helper.forest.createBinary(offset, |
| _helper.createVariableGet(read, fileOffset), binaryOperator, value); |
| VariableDeclaration write = _helper.forest |
| .createVariableDeclarationForValue(_createWrite(fileOffset, |
| _helper.createVariableGet(variable, receiver.fileOffset), binary, |
| forEffect: voidContext, readOnlyReceiver: true) |
| ..fileOffset = fileOffset); |
| return new PropertyPostIncDec(variable, read, write)..fileOffset = offset; |
| } |
| } |
| |
| @override |
| Expression doInvocation( |
| int offset, List<UnresolvedType> typeArguments, Arguments arguments, |
| {bool isTypeArgumentsInForest = false}) { |
| VariableDeclaration receiverVariable; |
| Expression receiverExpression = receiver; |
| if (isNullAware) { |
| receiverVariable = |
| _helper.forest.createVariableDeclarationForValue(receiver); |
| receiverExpression = _helper.createVariableGet( |
| receiverVariable, receiverVariable.fileOffset, |
| forNullGuardedAccess: true); |
| } |
| Expression invocation; |
| if (invokeTarget != null) { |
| invocation = _helper.buildExtensionMethodInvocation( |
| fileOffset, |
| invokeTarget, |
| _forest.createArgumentsForExtensionMethod( |
| fileOffset, |
| extensionTypeParameterCount, |
| invokeTarget.function.typeParameters.length - |
| extensionTypeParameterCount, |
| receiverExpression, |
| extensionTypeArguments: _createExtensionTypeArguments(), |
| extensionTypeArgumentOffset: extensionTypeArgumentOffset, |
| typeArguments: arguments.types, |
| positionalArguments: arguments.positional, |
| namedArguments: arguments.named), |
| isTearOff: false); |
| } else { |
| invocation = _helper.forest.createExpressionInvocation( |
| adjustForImplicitCall(_plainNameForRead, offset), |
| _createRead(receiverExpression), |
| arguments); |
| } |
| if (isNullAware) { |
| assert(receiverVariable != null); |
| return new NullAwareExtension(receiverVariable, invocation) |
| ..fileOffset = fileOffset; |
| } else { |
| return invocation; |
| } |
| } |
| |
| @override |
| Generator buildIndexedAccess(Expression index, Token token, |
| {bool isNullAware}) { |
| assert(isNullAware != null); |
| return new IndexedAccessGenerator(_helper, token, buildSimpleRead(), index, |
| isNullAware: isNullAware); |
| } |
| |
| @override |
| void printOn(StringSink sink) { |
| NameSystem syntheticNames = new NameSystem(); |
| sink.write(", targetName: "); |
| sink.write(targetName); |
| sink.write(", readTarget: "); |
| printQualifiedNameOn(readTarget, sink, syntheticNames: syntheticNames); |
| sink.write(", writeTarget: "); |
| printQualifiedNameOn(writeTarget, sink, syntheticNames: syntheticNames); |
| } |
| } |
| |
| class ExplicitExtensionIndexedAccessGenerator extends Generator { |
| /// The file offset used for the explicit extension application type |
| /// arguments. |
| final int extensionTypeArgumentOffset; |
| |
| final Extension extension; |
| |
| /// The static [Member] generated for the [] operation. |
| /// |
| /// This can be `null` if the extension doesn't have an [] method. |
| final Procedure readTarget; |
| |
| /// The static [Member] generated for the []= operation. |
| /// |
| /// This can be `null` if the extension doesn't have an []= method. |
| final Procedure writeTarget; |
| |
| /// The expression holding the receiver value for the explicit extension |
| /// access, that is, `a` in `Extension<int>(a)[index]`. |
| final Expression receiver; |
| |
| /// The index expression; |
| final Expression index; |
| |
| /// The type arguments explicitly passed to the explicit extension access, |
| /// like `<int>` in `Extension<int>(a)[b]`. |
| final List<DartType> explicitTypeArguments; |
| |
| /// The number of type parameters declared on the extension declaration. |
| final int extensionTypeParameterCount; |
| |
| final bool isNullAware; |
| |
| ExplicitExtensionIndexedAccessGenerator( |
| ExpressionGeneratorHelper helper, |
| Token token, |
| this.extensionTypeArgumentOffset, |
| this.extension, |
| this.readTarget, |
| this.writeTarget, |
| this.receiver, |
| this.index, |
| this.explicitTypeArguments, |
| this.extensionTypeParameterCount, |
| {this.isNullAware}) |
| : assert(readTarget != null || writeTarget != null), |
| assert(receiver != null), |
| assert(isNullAware != null), |
| super(helper, token); |
| |
| factory ExplicitExtensionIndexedAccessGenerator.fromBuilder( |
| ExpressionGeneratorHelper helper, |
| Token token, |
| int extensionTypeArgumentOffset, |
| Extension extension, |
| Builder getterBuilder, |
| Builder setterBuilder, |
| Expression receiver, |
| Expression index, |
| List<DartType> explicitTypeArguments, |
| int extensionTypeParameterCount, |
| {bool isNullAware}) { |
| assert(isNullAware != null); |
| Procedure readTarget; |
| if (getterBuilder != null) { |
| if (getterBuilder is AccessErrorBuilder) { |
| AccessErrorBuilder error = getterBuilder; |
| getterBuilder = error.builder; |
| // We should only see an access error here if we've looked up a setter |
| // when not explicitly looking for a setter. |
| assert(getterBuilder is MemberBuilder); |
| } else if (getterBuilder is MemberBuilder) { |
| MemberBuilder procedureBuilder = getterBuilder; |
| readTarget = procedureBuilder.member; |
| } else { |
| return unhandled( |
| "${getterBuilder.runtimeType}", |
| "InstanceExtensionAccessGenerator.fromBuilder", |
| offsetForToken(token), |
| helper.uri); |
| } |
| } |
| Procedure writeTarget; |
| if (setterBuilder is MemberBuilder) { |
| MemberBuilder memberBuilder = setterBuilder; |
| writeTarget = memberBuilder.member; |
| } |
| return new ExplicitExtensionIndexedAccessGenerator( |
| helper, |
| token, |
| extensionTypeArgumentOffset, |
| extension, |
| readTarget, |
| writeTarget, |
| receiver, |
| index, |
| explicitTypeArguments, |
| extensionTypeParameterCount, |
| isNullAware: isNullAware); |
| } |
| |
| List<DartType> _createExtensionTypeArguments() { |
| return explicitTypeArguments ?? const <DartType>[]; |
| } |
| |
| String get _plainNameForRead => "[]"; |
| |
| String get _debugName => "ExplicitExtensionIndexedAccessGenerator"; |
| |
| @override |
| Expression buildSimpleRead() { |
| if (readTarget == null) { |
| return _makeInvalidRead(); |
| } |
| VariableDeclaration variable; |
| Expression receiverValue; |
| if (isNullAware) { |
| variable = _forest.createVariableDeclarationForValue(receiver); |
| receiverValue = _helper.createVariableGet(variable, fileOffset, |
| forNullGuardedAccess: true); |
| } else { |
| receiverValue = receiver; |
| } |
| Expression result = _helper.buildExtensionMethodInvocation( |
| fileOffset, |
| readTarget, |
| _forest.createArgumentsForExtensionMethod( |
| fileOffset, extensionTypeParameterCount, 0, receiverValue, |
| extensionTypeArguments: _createExtensionTypeArguments(), |
| extensionTypeArgumentOffset: extensionTypeArgumentOffset, |
| positionalArguments: <Expression>[index]), |
| isTearOff: false); |
| if (isNullAware) { |
| result = new NullAwareMethodInvocation(variable, result) |
| ..fileOffset = fileOffset; |
| } |
| return result; |
| } |
| |
| @override |
| Expression buildAssignment(Expression value, {bool voidContext: false}) { |
| if (writeTarget == null) { |
| return _makeInvalidWrite(value); |
| } |
| VariableDeclaration variable; |
| Expression receiverValue; |
| if (isNullAware) { |
| variable = _forest.createVariableDeclarationForValue(receiver); |
| receiverValue = _helper.createVariableGet(variable, fileOffset, |
| forNullGuardedAccess: true); |
| } else { |
| receiverValue = receiver; |
| } |
| Expression result; |
| if (voidContext) { |
| result = _helper.buildExtensionMethodInvocation( |
| fileOffset, |
| writeTarget, |
| _forest.createArgumentsForExtensionMethod( |
| fileOffset, extensionTypeParameterCount, 0, receiverValue, |
| extensionTypeArguments: _createExtensionTypeArguments(), |
| extensionTypeArgumentOffset: extensionTypeArgumentOffset, |
| positionalArguments: <Expression>[index, value]), |
| isTearOff: false); |
| } else { |
| result = new ExtensionIndexSet(extension, explicitTypeArguments, |
| receiverValue, writeTarget, index, value) |
| ..fileOffset = fileOffset; |
| } |
| if (isNullAware) { |
| result = new NullAwareMethodInvocation(variable, result) |
| ..fileOffset = fileOffset; |
| } |
| return result; |
| } |
| |
| @override |
| Expression buildIfNullAssignment(Expression value, DartType type, int offset, |
| {bool voidContext: false}) { |
| VariableDeclaration variable; |
| Expression receiverValue; |
| bool readOnlyReceiver; |
| if (isNullAware) { |
| variable = _forest.createVariableDeclarationForValue(receiver); |
| receiverValue = _helper.createVariableGet(variable, fileOffset, |
| forNullGuardedAccess: true); |
| readOnlyReceiver = true; |
| } else { |
| receiverValue = receiver; |
| readOnlyReceiver = false; |
| } |
| Expression result = new IfNullExtensionIndexSet( |
| extension, |
| explicitTypeArguments, |
| receiverValue, |
| readTarget, |
| writeTarget, |
| index, |
| value, |
| readOffset: fileOffset, |
| testOffset: offset, |
| writeOffset: fileOffset, |
| forEffect: voidContext, |
| readOnlyReceiver: readOnlyReceiver) |
| ..fileOffset = offset; |
| if (isNullAware) { |
| result = new NullAwareMethodInvocation(variable, result) |
| ..fileOffset = fileOffset; |
| } |
| return result; |
| } |
| |
| Expression buildCompoundAssignment(Name binaryOperator, Expression value, |
| {int offset: TreeNode.noOffset, |
| bool voidContext: false, |
| bool isPreIncDec: false, |
| bool isPostIncDec: false}) { |
| VariableDeclaration variable; |
| Expression receiverValue; |
| bool readOnlyReceiver; |
| if (isNullAware) { |
| variable = _forest.createVariableDeclarationForValue(receiver); |
| receiverValue = _helper.createVariableGet(variable, fileOffset, |
| forNullGuardedAccess: true); |
| readOnlyReceiver = true; |
| } else { |
| receiverValue = receiver; |
| readOnlyReceiver = false; |
| } |
| Expression result = new CompoundExtensionIndexSet( |
| extension, |
| explicitTypeArguments, |
| receiverValue, |
| readTarget, |
| writeTarget, |
| index, |
| binaryOperator, |
| value, |
| readOffset: fileOffset, |
| binaryOffset: offset, |
| writeOffset: fileOffset, |
| forEffect: voidContext, |
| forPostIncDec: isPostIncDec, |
| readOnlyReceiver: readOnlyReceiver); |
| if (isNullAware) { |
| result = new NullAwareMethodInvocation(variable, result) |
| ..fileOffset = fileOffset; |
| } |
| return result; |
| } |
| |
| @override |
| Expression buildPostfixIncrement(Name binaryOperator, |
| {int offset = TreeNode.noOffset, bool voidContext = false}) { |
| Expression value = _forest.createIntLiteral(offset, 1); |
| return buildCompoundAssignment(binaryOperator, value, |
| offset: offset, voidContext: voidContext, isPostIncDec: true); |
| } |
| |
| @override |
| Expression doInvocation( |
| int offset, List<UnresolvedType> typeArguments, Arguments arguments, |
| {bool isTypeArgumentsInForest = false}) { |
| return _helper.forest |
| .createExpressionInvocation(offset, buildSimpleRead(), arguments); |
| } |
| |
| @override |
| Generator buildIndexedAccess(Expression index, Token token, |
| {bool isNullAware}) { |
| assert(isNullAware != null); |
| return new IndexedAccessGenerator(_helper, token, buildSimpleRead(), index, |
| isNullAware: isNullAware); |
| } |
| |
| @override |
| void printOn(StringSink sink) { |
| NameSystem syntheticNames = new NameSystem(); |
| sink.write(", index: "); |
| printNodeOn(index, sink, syntheticNames: syntheticNames); |
| sink.write(", readTarget: "); |
| printQualifiedNameOn(readTarget, sink, syntheticNames: syntheticNames); |
| sink.write(", writeTarget: "); |
| printQualifiedNameOn(writeTarget, sink, syntheticNames: syntheticNames); |
| } |
| } |
| |
| /// A [ExplicitExtensionAccessGenerator] represents a subexpression whose |
| /// prefix is an explicit extension application. |
| /// |
| /// For instance |
| /// |
| /// class A<T> {} |
| /// extension B on A<int> { |
| /// method() {} |
| /// } |
| /// extension C<T> on A<T> { |
| /// T get field => 0; |
| /// set field(T _) {} |
| /// } |
| /// |
| /// method(A a) { |
| /// B(a).method; // this generator is created for `B(a)`. |
| /// B(a).method(); // this generator is created for `B(a)`. |
| /// C<int>(a).field; // this generator is created for `C<int>(a)`. |
| /// C(a).field = 0; // this generator is created for `C(a)`. |
| /// } |
| /// |
| /// When an access is performed on this generator a |
| /// [ExplicitExtensionInstanceAccessGenerator] is created. |
| class ExplicitExtensionAccessGenerator extends Generator { |
| final ExtensionBuilder extensionBuilder; |
| final Expression receiver; |
| final List<DartType> explicitTypeArguments; |
| |
| ExplicitExtensionAccessGenerator( |
| ExpressionGeneratorHelper helper, |
| Token token, |
| this.extensionBuilder, |
| this.receiver, |
| this.explicitTypeArguments) |
| : super(helper, token); |
| |
| @override |
| String get _plainNameForRead { |
| return unsupported( |
| "ExplicitExtensionAccessGenerator.plainNameForRead", fileOffset, _uri); |
| } |
| |
| @override |
| String get _debugName => "ExplicitExtensionAccessGenerator"; |
| |
| @override |
| Expression buildSimpleRead() { |
| return _makeInvalidRead(); |
| } |
| |
| @override |
| Expression buildAssignment(Expression value, {bool voidContext: false}) { |
| return _makeInvalidWrite(value); |
| } |
| |
| @override |
| Expression buildIfNullAssignment(Expression value, DartType type, int offset, |
| {bool voidContext: false}) { |
| return _makeInvalidRead(); |
| } |
| |
| Expression buildCompoundAssignment(Name binaryOperator, Expression value, |
| {int offset: TreeNode.noOffset, |
| bool voidContext: false, |
| bool isPreIncDec: false, |
| bool isPostIncDec: false}) { |
| return _makeInvalidRead(); |
| } |
| |
| Expression buildPostfixIncrement(Name binaryOperator, |
| {int offset: TreeNode.noOffset, bool voidContext: false}) { |
| return _makeInvalidRead(); |
| } |
| |
| Generator _createInstanceAccess(Token token, Name name, |
| {bool isNullAware: false}) { |
| Builder getter = extensionBuilder.lookupLocalMemberByName(name); |
| if (getter != null && (getter.isStatic || getter.isField)) { |
| getter = null; |
| } |
| Builder setter = |
| extensionBuilder.lookupLocalMemberByName(name, setter: true); |
| if (setter != null && setter.isStatic) { |
| setter = null; |
| } |
| if (getter == null && setter == null) { |
| return new UnresolvedNameGenerator(_helper, token, name); |
| } |
| return new ExplicitExtensionInstanceAccessGenerator.fromBuilder( |
| _helper, |
| token, |
| // TODO(johnniwinther): Improve this. This is the name of the extension |
| // and not the type arguments (or arguments if type arguments are |
| // omitted). |
| fileOffset, |
| extensionBuilder.extension, |
| name, |
| getter, |
| setter, |
| receiver, |
| explicitTypeArguments, |
| extensionBuilder.typeParameters?.length ?? 0, |
| isNullAware: isNullAware); |
| } |
| |
| /* Expression | Generator */ buildPropertyAccess( |
| IncompleteSendGenerator send, int operatorOffset, bool isNullAware) { |
| if (_helper.constantContext != ConstantContext.none) { |
| _helper.addProblem( |
| messageNotAConstantExpression, fileOffset, token.length); |
| } |
| Generator generator = |
| _createInstanceAccess(send.token, send.name, isNullAware: isNullAware); |
| if (send.arguments != null) { |
| return generator.doInvocation( |
| offsetForToken(send.token), send.typeArguments, send.arguments, |
| isTypeArgumentsInForest: send.isTypeArgumentsInForest); |
| } else { |
| return generator; |
| } |
| } |
| |
| @override |
| buildBinaryOperation(Token token, Name binaryName, Expression right) { |
| int fileOffset = offsetForToken(token); |
| Generator generator = _createInstanceAccess(token, binaryName); |
| return generator.doInvocation(fileOffset, null, |
| _forest.createArguments(fileOffset, <Expression>[right])); |
| } |
| |
| @override |
| buildUnaryOperation(Token token, Name unaryName) { |
| int fileOffset = offsetForToken(token); |
| Generator generator = _createInstanceAccess(token, unaryName); |
| return generator.doInvocation( |
| fileOffset, null, _forest.createArgumentsEmpty(fileOffset)); |
| } |
| |
| @override |
| doInvocation( |
| int offset, List<UnresolvedType> typeArguments, Arguments arguments, |
| {bool isTypeArgumentsInForest = false}) { |
| Generator generator = _createInstanceAccess(token, callName); |
| return generator.doInvocation(offset, typeArguments, arguments, |
| isTypeArgumentsInForest: isTypeArgumentsInForest); |
| } |
| |
| @override |
| Expression _makeInvalidRead() { |
| return _helper.buildProblem(messageExplicitExtensionAsExpression, |
| fileOffset, lengthForToken(token)); |
| } |
| |
| @override |
| Expression _makeInvalidWrite(Expression value) { |
| return _helper.buildProblem( |
| messageExplicitExtensionAsLvalue, fileOffset, lengthForToken(token)); |
| } |
| |
| @override |
| Generator buildIndexedAccess(Expression index, Token token, |
| {bool isNullAware}) { |
| assert(isNullAware != null); |
| Builder getter = extensionBuilder.lookupLocalMemberByName(indexGetName); |
| Builder setter = extensionBuilder.lookupLocalMemberByName(indexSetName); |
| if (getter == null && setter == null) { |
| return new UnresolvedNameGenerator(_helper, token, indexGetName); |
| } |
| |
| return new ExplicitExtensionIndexedAccessGenerator.fromBuilder( |
| _helper, |
| token, |
| // TODO(johnniwinther): Improve this. This is the name of the extension |
| // and not the type arguments (or arguments if type arguments are |
| // omitted). |
| fileOffset, |
| extensionBuilder.extension, |
| getter, |
| setter, |
| receiver, |
| index, |
| explicitTypeArguments, |
| extensionBuilder.typeParameters?.length ?? 0, |
| isNullAware: isNullAware); |
| } |
| |
| @override |
| void printOn(StringSink sink) { |
| sink.write(", extensionBuilder: "); |
| sink.write(extensionBuilder); |
| sink.write(", receiver: "); |
| sink.write(receiver); |
| } |
| } |
| |
| class LoadLibraryGenerator extends Generator { |
| final LoadLibraryBuilder builder; |
| |
| LoadLibraryGenerator( |
| ExpressionGeneratorHelper helper, Token token, this.builder) |
| : super(helper, token); |
| |
| @override |
| String get _plainNameForRead => 'loadLibrary'; |
| |
| @override |
| String get _debugName => "LoadLibraryGenerator"; |
| |
| @override |
| Expression buildSimpleRead() { |
| builder.importDependency.targetLibrary; |
| LoadLibraryTearOff read = new LoadLibraryTearOff( |
| builder.importDependency, builder.createTearoffMethod(_helper.forest)) |
| ..fileOffset = fileOffset; |
| return read; |
| } |
| |
| @override |
| Expression buildAssignment(Expression value, {bool voidContext: false}) { |
| return _makeInvalidWrite(value); |
| } |
| |
| @override |
| Expression buildIfNullAssignment(Expression value, DartType type, int offset, |
| {bool voidContext: false}) { |
| Expression read = buildSimpleRead(); |
| Expression write = _makeInvalidWrite(value); |
| return new IfNullSet(read, write, forEffect: voidContext) |
| ..fileOffset = offset; |
| } |
| |
| @override |
| Expression buildCompoundAssignment(Name binaryOperator, Expression value, |
| {int offset: TreeNode.noOffset, |
| bool voidContext: false, |
| bool isPreIncDec: false, |
| bool isPostIncDec: false}) { |
| Expression binary = _helper.forest |
| .createBinary(offset, buildSimpleRead(), binaryOperator, value); |
| return _makeInvalidWrite(binary); |
| } |
| |
| @override |
| Expression buildPostfixIncrement(Name binaryOperator, |
| {int offset = TreeNode.noOffset, bool voidContext = false}) { |
| Expression value = _forest.createIntLiteral(offset, 1); |
| return buildCompoundAssignment(binaryOperator, value, |
| offset: offset, voidContext: voidContext, isPostIncDec: true); |
| } |
| |
| @override |
| Expression doInvocation( |
| int offset, List<UnresolvedType> typeArguments, Arguments arguments, |
| {bool isTypeArgumentsInForest = false}) { |
| if (_forest.argumentsPositional(arguments).length > 0 || |
| _forest.argumentsNamed(arguments).length > 0) { |
| _helper.addProblemErrorIfConst( |
| messageLoadLibraryTakesNoArguments, offset, 'loadLibrary'.length); |
| } |
| return builder.createLoadLibrary(offset, _forest, arguments); |
| } |
| |
| @override |
| Generator buildIndexedAccess(Expression index, Token token, |
| {bool isNullAware}) { |
| assert(isNullAware != null); |
| return new IndexedAccessGenerator(_helper, token, buildSimpleRead(), index, |
| isNullAware: isNullAware); |
| } |
| |
| @override |
| void printOn(StringSink sink) { |
| sink.write(", builder: "); |
| sink.write(builder); |
| } |
| } |
| |
| class DeferredAccessGenerator extends Generator { |
| final PrefixUseGenerator prefixGenerator; |
| |
| final Generator suffixGenerator; |
| |
| DeferredAccessGenerator(ExpressionGeneratorHelper helper, Token token, |
| this.prefixGenerator, this.suffixGenerator) |
| : super(helper, token); |
| |
| @override |
| Expression buildSimpleRead() { |
| return _helper.wrapInDeferredCheck(suffixGenerator.buildSimpleRead(), |
| prefixGenerator.prefix, token.charOffset); |
| } |
| |
| @override |
| Expression buildAssignment(Expression value, {bool voidContext: false}) { |
| return _helper.wrapInDeferredCheck( |
| suffixGenerator.buildAssignment(value, voidContext: voidContext), |
| prefixGenerator.prefix, |
| token.charOffset); |
| } |
| |
| @override |
| Expression buildIfNullAssignment(Expression value, DartType type, int offset, |
| {bool voidContext: false}) { |
| return _helper.wrapInDeferredCheck( |
| suffixGenerator.buildIfNullAssignment(value, type, offset, |
| voidContext: voidContext), |
| prefixGenerator.prefix, |
| token.charOffset); |
| } |
| |
| @override |
| Expression buildCompoundAssignment(Name binaryOperator, Expression value, |
| {int offset: TreeNode.noOffset, |
| bool voidContext: false, |
| bool isPreIncDec: false, |
| bool isPostIncDec: false}) { |
| return _helper.wrapInDeferredCheck( |
| suffixGenerator.buildCompoundAssignment(binaryOperator, value, |
| offset: offset, |
| voidContext: voidContext, |
| isPreIncDec: isPreIncDec, |
| isPostIncDec: isPostIncDec), |
| prefixGenerator.prefix, |
| token.charOffset); |
| } |
| |
| @override |
| Expression buildPostfixIncrement(Name binaryOperator, |
| {int offset: TreeNode.noOffset, bool voidContext: false}) { |
| return _helper.wrapInDeferredCheck( |
| suffixGenerator.buildPostfixIncrement(binaryOperator, |
| offset: offset, voidContext: voidContext), |
| prefixGenerator.prefix, |
| token.charOffset); |
| } |
| |
| @override |
| buildPropertyAccess( |
| IncompleteSendGenerator send, int operatorOffset, bool isNullAware) { |
| Object propertyAccess = |
| suffixGenerator.buildPropertyAccess(send, operatorOffset, isNullAware); |
| if (propertyAccess is Generator) { |
| return new DeferredAccessGenerator( |
| _helper, token, prefixGenerator, propertyAccess); |
| } else { |
| Expression expression = propertyAccess; |
| return _helper.wrapInDeferredCheck( |
| expression, prefixGenerator.prefix, token.charOffset); |
| } |
| } |
| |
| @override |
| String get _plainNameForRead { |
| return unsupported("deferredAccessor.plainNameForRead", fileOffset, _uri); |
| } |
| |
| @override |
| String get _debugName => "DeferredAccessGenerator"; |
| |
| @override |
| TypeBuilder buildTypeWithResolvedArguments( |
| NullabilityBuilder nullabilityBuilder, List<UnresolvedType> arguments) { |
| String name = "${prefixGenerator._plainNameForRead}." |
| "${suffixGenerator._plainNameForRead}"; |
| TypeBuilder type = suffixGenerator.buildTypeWithResolvedArguments( |
| nullabilityBuilder, arguments); |
| LocatedMessage message; |
| if (type is NamedTypeBuilder && |
| type.declaration is InvalidTypeDeclarationBuilder) { |
| InvalidTypeDeclarationBuilder declaration = type.declaration; |
| message = declaration.message; |
| } else { |
| int charOffset = offsetForToken(prefixGenerator.token); |
| message = templateDeferredTypeAnnotation |
| .withArguments( |
| _helper.buildDartType(new UnresolvedType(type, charOffset, _uri)), |
| prefixGenerator._plainNameForRead, |
| _helper.libraryBuilder.isNonNullableByDefault) |
| .withLocation( |
| _uri, charOffset, lengthOfSpan(prefixGenerator.token, token)); |
| } |
| // TODO(johnniwinther): Could we use a FixedTypeBuilder(InvalidType()) here? |
| NamedTypeBuilder result = new NamedTypeBuilder( |
| name, |
| nullabilityBuilder, |
| /* arguments = */ null, |
| /* fileUri = */ null, |
| /* charOffset = */ null); |
| _helper.libraryBuilder.addProblem( |
| message.messageObject, message.charOffset, message.length, message.uri); |
| result.bind(result.buildInvalidTypeDeclarationBuilder(message)); |
| return result; |
| } |
| |
| @override |
| doInvocation( |
| int offset, List<UnresolvedType> typeArguments, Arguments arguments, |
| {bool isTypeArgumentsInForest = false}) { |
| Object suffix = suffixGenerator.doInvocation( |
| offset, typeArguments, arguments, |
| isTypeArgumentsInForest: isTypeArgumentsInForest); |
| if (suffix is Expression) { |
| return _helper.wrapInDeferredCheck( |
| suffix, prefixGenerator.prefix, fileOffset); |
| } else { |
| return new DeferredAccessGenerator( |
| _helper, token, prefixGenerator, suffix); |
| } |
| } |
| |
| @override |
| Expression invokeConstructor( |
| List<UnresolvedType> typeArguments, |
| String name, |
| Arguments arguments, |
| Token nameToken, |
| Token nameLastToken, |
| Constness constness) { |
| return _helper.wrapInDeferredCheck( |
| suffixGenerator.invokeConstructor(typeArguments, name, arguments, |
| nameToken, nameLastToken, constness), |
| prefixGenerator.prefix, |
| offsetForToken(suffixGenerator.token)); |
| } |
| |
| @override |
| Generator buildIndexedAccess(Expression index, Token token, |
| {bool isNullAware}) { |
| assert(isNullAware != null); |
| return new IndexedAccessGenerator(_helper, token, buildSimpleRead(), index, |
| isNullAware: isNullAware); |
| } |
| |
| @override |
| void printOn(StringSink sink) { |
| sink.write(", prefixGenerator: "); |
| sink.write(prefixGenerator); |
| sink.write(", suffixGenerator: "); |
| sink.write(suffixGenerator); |
| } |
| } |
| |
| /// [TypeUseGenerator] represents the subexpression whose prefix is the name of |
| /// a class, enum, type variable, typedef, mixin declaration, extension |
| /// declaration or built-in type, like dynamic and void. |
| /// |
| /// For instance: |
| /// |
| /// class A<T> {} |
| /// typedef B = Function(); |
| /// mixin C<T> on A<T> {} |
| /// extension D<T> on A<T> {} |
| /// |
| /// method<T>() { |
| /// C<B> // a TypeUseGenerator is created for `C` and `B`. |
| /// B b; // a TypeUseGenerator is created for `B`. |
| /// D.foo(); // a TypeUseGenerator is created for `D`. |
| /// new A<T>(); // a TypeUseGenerator is created for `A` and `T`. |
| /// T(); // a TypeUseGenerator is created for `T`. |
| /// } |
| /// |
| class TypeUseGenerator extends ReadOnlyAccessGenerator { |
| final TypeDeclarationBuilder declaration; |
| |
| TypeUseGenerator(ExpressionGeneratorHelper helper, Token token, |
| this.declaration, String targetName) |
| : super( |
| helper, |
| token, |
| null, |
| targetName, |
| // TODO(johnniwinther): InvalidTypeDeclarationBuilder is currently |
| // misused for import conflict. |
| declaration is InvalidTypeDeclarationBuilder |
| ? ReadOnlyAccessKind.InvalidDeclaration |
| : ReadOnlyAccessKind.TypeLiteral); |
| |
| @override |
| String get _debugName => "TypeUseGenerator"; |
| |
| @override |
| TypeBuilder buildTypeWithResolvedArguments( |
| NullabilityBuilder nullabilityBuilder, List<UnresolvedType> arguments) { |
| if (declaration.isExtension) { |
| // Extension declarations cannot be used as types. |
| return super |
| .buildTypeWithResolvedArguments(nullabilityBuilder, arguments); |
| } |
| if (arguments != null) { |
| int expected = declaration.typeVariablesCount; |
| if (arguments.length != expected) { |
| // Build the type arguments to report any errors they may have. |
| _helper.buildDartTypeArguments(arguments); |
| _helper.warnTypeArgumentsMismatch( |
| declaration.name, expected, fileOffset); |
| // We ignore the provided arguments, which will in turn return the |
| // raw type below. |
| // TODO(sigmund): change to use an InvalidType and include the raw type |
| // as a recovery node once the IR can represent it (Issue #29840). |
| arguments = null; |
| } |
| } else if (declaration.typeVariablesCount != 0) { |
| _helper.addProblem( |
| templateMissingExplicitTypeArguments |
| .withArguments(declaration.typeVariablesCount), |
| fileOffset, |
| lengthForToken(token)); |
| } |
| |
| List<TypeBuilder> argumentBuilders; |
| if (arguments != null) { |
| argumentBuilders = new List<TypeBuilder>(arguments.length); |
| for (int i = 0; i < argumentBuilders.length; i++) { |
| argumentBuilders[i] = _helper |
| .validateTypeUse(arguments[i], |
| nonInstanceAccessIsError: false, |
| allowPotentiallyConstantType: |
| _helper.libraryBuilder.isNonNullableByDefault && |
| _helper.inIsOrAsOperatorType) |
| .builder; |
| } |
| } |
| return new NamedTypeBuilder( |
| targetName, nullabilityBuilder, argumentBuilders, _uri, fileOffset) |
| ..bind(declaration); |
| } |
| |
| @override |
| Expression invokeConstructor( |
| List<UnresolvedType> typeArguments, |
| String name, |
| Arguments arguments, |
| Token nameToken, |
| Token nameLastToken, |
| Constness constness) { |
| return _helper.buildConstructorInvocation( |
| declaration, |
| nameToken, |
| nameLastToken, |
| arguments, |
| name, |
| typeArguments, |
| offsetForToken(nameToken ?? token), |
| constness); |
| } |
| |
| @override |
| void printOn(StringSink sink) { |
| sink.write(", declaration: "); |
| sink.write(declaration); |
| sink.write(", plainNameForRead: "); |
| sink.write(_plainNameForRead); |
| } |
| |
| @override |
| Expression get expression { |
| if (super.expression == null) { |
| if (declaration is InvalidTypeDeclarationBuilder) { |
| InvalidTypeDeclarationBuilder declaration = this.declaration; |
| super.expression = _helper.buildProblemErrorIfConst( |
| declaration.message.messageObject, fileOffset, token.length); |
| } else { |
| super.expression = _forest.createTypeLiteral( |
| offsetForToken(token), |
| _helper.buildDartType( |
| new UnresolvedType( |
| buildTypeWithResolvedArguments( |
| _helper.libraryBuilder.nonNullableBuilder, null), |
| fileOffset, |
| _uri), |
| nonInstanceAccessIsError: true)); |
| } |
| } |
| return super.expression; |
| } |
| |
| @override |
| buildPropertyAccess( |
| IncompleteSendGenerator send, int operatorOffset, bool isNullAware) { |
| // `SomeType?.toString` is the same as `SomeType.toString`, not |
| // `(SomeType).toString`. |
| isNullAware = false; |
| |
| Name name = send.name; |
| Arguments arguments = send.arguments; |
| |
| TypeDeclarationBuilder declarationBuilder = declaration; |
| if (declarationBuilder is TypeAliasBuilder) { |
| TypeAliasBuilder aliasBuilder = declarationBuilder; |
| declarationBuilder = aliasBuilder.unaliasDeclaration(null, |
| isInvocation: true, |
| invocationCharOffset: this.fileOffset, |
| invocationFileUri: _uri); |
| } |
| if (declarationBuilder is DeclarationBuilder) { |
| DeclarationBuilder declaration = declarationBuilder; |
| Builder member = declaration.findStaticBuilder( |
| name.text, offsetForToken(send.token), _uri, _helper.libraryBuilder); |
| |
| Generator generator; |
| if (member == null) { |
| // If we find a setter, [member] is an [AccessErrorBuilder], not null. |
| if (send is IncompletePropertyAccessGenerator) { |
| generator = new UnresolvedNameGenerator(_helper, send.token, name); |
| } else { |
| return _helper.buildConstructorInvocation( |
| declaration, |
| send.token, |
| send.token, |
| arguments, |
| name.text, |
| send.typeArguments, |
| token.charOffset, |
| Constness.implicit, |
| isTypeArgumentsInForest: send.isTypeArgumentsInForest); |
| } |
| } else if (member is AmbiguousBuilder) { |
| return _helper.buildProblem( |
| member.message, member.charOffset, name.text.length); |
<