| // 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. | |
| import 'package:analyzer/dart/ast/ast.dart'; | |
| import 'package:analyzer/dart/ast/token.dart'; | |
| import 'package:analyzer/src/summary/format.dart'; | |
| import 'package:analyzer/src/summary/idl.dart'; | |
| /// Serialize the given constructor initializer [node]. | |
| UnlinkedConstructorInitializerBuilder serializeConstructorInitializer( | |
| ConstructorInitializer node, | |
| UnlinkedExprBuilder serializeConstExpr(Expression expr)) { | |
| if (node is ConstructorFieldInitializer) { | |
| return new UnlinkedConstructorInitializerBuilder( | |
| kind: UnlinkedConstructorInitializerKind.field, | |
| name: node.fieldName.name, | |
| expression: serializeConstExpr(node.expression)); | |
| } | |
| List<UnlinkedExprBuilder> arguments = <UnlinkedExprBuilder>[]; | |
| List<String> argumentNames = <String>[]; | |
| void serializeArguments(List<Expression> args) { | |
| for (Expression arg in args) { | |
| if (arg is NamedExpression) { | |
| NamedExpression namedExpression = arg; | |
| argumentNames.add(namedExpression.name.label.name); | |
| arg = namedExpression.expression; | |
| } | |
| arguments.add(serializeConstExpr(arg)); | |
| } | |
| } | |
| if (node is AssertInitializer) { | |
| serializeArguments(node.message != null | |
| ? [node.condition, node.message] | |
| : [node.condition]); | |
| return new UnlinkedConstructorInitializerBuilder( | |
| kind: UnlinkedConstructorInitializerKind.assertInvocation, | |
| arguments: arguments); | |
| } | |
| if (node is RedirectingConstructorInvocation) { | |
| serializeArguments(node.argumentList.arguments); | |
| return new UnlinkedConstructorInitializerBuilder( | |
| kind: UnlinkedConstructorInitializerKind.thisInvocation, | |
| name: node?.constructorName?.name, | |
| arguments: arguments, | |
| argumentNames: argumentNames); | |
| } | |
| if (node is SuperConstructorInvocation) { | |
| serializeArguments(node.argumentList.arguments); | |
| return new UnlinkedConstructorInitializerBuilder( | |
| kind: UnlinkedConstructorInitializerKind.superInvocation, | |
| name: node?.constructorName?.name, | |
| arguments: arguments, | |
| argumentNames: argumentNames); | |
| } | |
| throw new StateError('Unexpected initializer type ${node.runtimeType}'); | |
| } | |
| /// Converts all the tokens between [startToken] and [endToken] (inclusive) into | |
| /// a string which, when scanned, will yield the same tokens back. | |
| String tokensToString(Token startToken, Token endToken) { | |
| var buffer = StringBuffer(); | |
| var token = startToken; | |
| var spaceNeeded = false; | |
| while (true) { | |
| if (token.type == TokenType.STRING_INTERPOLATION_EXPRESSION) { | |
| buffer.write(token.lexeme); | |
| buffer.write(tokensToString(token.next, token.endGroup)); | |
| spaceNeeded = false; | |
| token = token.endGroup; | |
| } else if (token.type == TokenType.STRING_INTERPOLATION_IDENTIFIER) { | |
| buffer.write(token.lexeme); | |
| buffer.write(token.next.lexeme); | |
| spaceNeeded = false; | |
| token = token.next; | |
| } else { | |
| if (spaceNeeded) buffer.write(' '); | |
| buffer.write(token.lexeme); | |
| spaceNeeded = true; | |
| } | |
| if (identical(token, endToken)) break; | |
| token = token.next; | |
| } | |
| return buffer.toString(); | |
| } | |
| /// Instances of this class keep track of intermediate state during | |
| /// serialization of a single constant [Expression]. | |
| abstract class AbstractConstExprSerializer { | |
| /// Whether an expression that should be a constant is being serialized. | |
| /// | |
| /// For constants we need to store more than we need just for type inference, | |
| /// because we need to be able to restore these AST to evaluate actual values | |
| /// of constants. So, we need to store constructor arguments, elements for | |
| /// list and map literals even if these literals are typed. | |
| final bool forConst; | |
| /// See [UnlinkedExprBuilder.isValidConst]. | |
| bool isValidConst = true; | |
| /// See [UnlinkedExprBuilder.name]. | |
| String name = null; | |
| /// See [UnlinkedExprBuilder.operations]. | |
| final List<UnlinkedExprOperation> operations = <UnlinkedExprOperation>[]; | |
| /// See [UnlinkedExprBuilder.assignmentOperators]. | |
| final List<UnlinkedExprAssignOperator> assignmentOperators = | |
| <UnlinkedExprAssignOperator>[]; | |
| /// See [UnlinkedExprBuilder.ints]. | |
| final List<int> ints = <int>[]; | |
| /// See [UnlinkedExprBuilder.doubles]. | |
| final List<double> doubles = <double>[]; | |
| /// See [UnlinkedExprBuilder.strings]. | |
| final List<String> strings = <String>[]; | |
| /// See [UnlinkedExprBuilder.references]. | |
| final List<EntityRefBuilder> references = <EntityRefBuilder>[]; | |
| AbstractConstExprSerializer(this.forConst); | |
| /// Return `true` if the given [name] is a parameter reference. | |
| bool isParameterName(String name); | |
| /// Removes the [count] variables that were most recently added to the scope | |
| /// by [pushVariableName]. | |
| void popVariableNames(int count); | |
| /// Pushes a variable with the given [name] onto the current scope. | |
| void pushVariableName(String name); | |
| /// Serialize the given [expr] expression into this serializer state. | |
| void serialize(Expression expr) { | |
| try { | |
| if (expr is NamedExpression) { | |
| NamedExpression namedExpression = expr; | |
| name = namedExpression.name.label.name; | |
| expr = namedExpression.expression; | |
| } | |
| _serialize(expr); | |
| } on StateError { | |
| isValidConst = false; | |
| operations.clear(); | |
| assignmentOperators.clear(); | |
| ints.clear(); | |
| doubles.clear(); | |
| strings.clear(); | |
| references.clear(); | |
| } | |
| } | |
| /// Serialize the given [annotation] into this serializer state. | |
| void serializeAnnotation(Annotation annotation); | |
| /// Return [EntityRefBuilder] that corresponds to the constructor having name | |
| /// [name] in the class identified by [typeName]. | |
| EntityRefBuilder serializeConstructorRef(Identifier typeName, | |
| TypeArgumentList typeArguments, SimpleIdentifier name); | |
| /// Return a pair of ints showing how the given [functionExpression] is nested | |
| /// within the constant currently being serialized. The first int indicates | |
| /// how many levels of function nesting must be popped in order to reach the | |
| /// parent of the [functionExpression]. The second int is the index of the | |
| /// [functionExpression] within its parent element. | |
| /// | |
| /// If the constant being summarized is in a context where local function | |
| /// references are not allowed, return `null`. | |
| List<int> serializeFunctionExpression(FunctionExpression functionExpression); | |
| /// Return [EntityRefBuilder] that corresponds to the [type], which is defined | |
| /// using generic function type syntax. These may appear as the type arguments | |
| /// of a const list, etc. | |
| EntityRefBuilder serializeGenericFunctionType(GenericFunctionType type); | |
| /// Return [EntityRefBuilder] that corresponds to the given [identifier]. | |
| EntityRefBuilder serializeIdentifier(Identifier identifier); | |
| /// Return [EntityRefBuilder] that corresponds to the given [expr], which | |
| /// must be a sequence of identifiers. | |
| EntityRefBuilder serializeIdentifierSequence(Expression expr); | |
| void serializeInstanceCreation(EntityRefBuilder constructor, | |
| ArgumentList argumentList, bool typeArgumentsProvided) { | |
| _serializeArguments(argumentList, typeArgumentsProvided); | |
| references.add(constructor); | |
| operations.add(UnlinkedExprOperation.invokeConstructor); | |
| } | |
| /// Return [EntityRefBuilder] that corresponds to the given [type]. | |
| EntityRefBuilder serializeType(TypeAnnotation type) { | |
| if (type is TypeName) { | |
| return serializeTypeName(type?.name, type?.typeArguments, | |
| computeNullabilitySuffix(type.question)); | |
| } | |
| if (type is GenericFunctionType) { | |
| return serializeGenericFunctionType(type); | |
| } | |
| throw new ArgumentError( | |
| 'Cannot serialize an instance of ${type.runtimeType}'); | |
| } | |
| /// Return [EntityRefBuilder] that corresponds to the type with the given | |
| /// [name] and [arguments]. | |
| EntityRefBuilder serializeTypeName(Identifier name, | |
| TypeArgumentList arguments, EntityRefNullabilitySuffix nullabilitySuffix); | |
| /// Return the [UnlinkedExprBuilder] that corresponds to the state of this | |
| /// serializer. | |
| UnlinkedExprBuilder toBuilder(Token startToken, Token endToken) { | |
| return new UnlinkedExprBuilder( | |
| isValidConst: isValidConst, | |
| operations: operations, | |
| assignmentOperators: assignmentOperators, | |
| ints: ints, | |
| doubles: doubles, | |
| strings: strings, | |
| references: references, | |
| sourceRepresentation: tokensToString(startToken, endToken)); | |
| } | |
| /// Return `true` if the given [expr] is a sequence of identifiers. | |
| bool _isIdentifierSequence(Expression expr) { | |
| while (expr != null) { | |
| if (expr is SimpleIdentifier) { | |
| AstNode parent = expr.parent; | |
| if (parent is MethodInvocation && parent.methodName == expr) { | |
| if (parent.isCascaded) { | |
| return false; | |
| } | |
| return parent.target == null || _isIdentifierSequence(parent.target); | |
| } | |
| if (isParameterName(expr.name)) { | |
| return false; | |
| } | |
| return true; | |
| } else if (expr is PrefixedIdentifier) { | |
| expr = (expr as PrefixedIdentifier).prefix; | |
| } else if (expr is PropertyAccess) { | |
| expr = (expr as PropertyAccess).target; | |
| } else { | |
| return false; | |
| } | |
| } | |
| return false; | |
| } | |
| /// Push the operation for the given assignable [expr]. | |
| void _pushAssignable(Expression expr) { | |
| if (_isIdentifierSequence(expr)) { | |
| EntityRefBuilder ref = serializeIdentifierSequence(expr); | |
| references.add(ref); | |
| operations.add(UnlinkedExprOperation.assignToRef); | |
| } else if (expr is SimpleIdentifier && isParameterName(expr.name)) { | |
| strings.add(expr.name); | |
| operations.add(UnlinkedExprOperation.assignToParameter); | |
| } else if (expr is PropertyAccess) { | |
| if (!expr.isCascaded) { | |
| _serialize(expr.target); | |
| } | |
| strings.add(expr.propertyName.name); | |
| operations.add(UnlinkedExprOperation.assignToProperty); | |
| } else if (expr is IndexExpression) { | |
| if (!expr.isCascaded) { | |
| _serialize(expr.target); | |
| } | |
| _serialize(expr.index); | |
| operations.add(UnlinkedExprOperation.assignToIndex); | |
| } else if (expr is PrefixedIdentifier) { | |
| strings.add(expr.prefix.name); | |
| operations.add(UnlinkedExprOperation.pushParameter); | |
| strings.add(expr.identifier.name); | |
| operations.add(UnlinkedExprOperation.assignToProperty); | |
| } else { | |
| throw new StateError('Unsupported assignable: $expr'); | |
| } | |
| } | |
| void _pushInt(int value) { | |
| value ??= 0; | |
| assert(value >= 0); | |
| if (value >= 0x100000000) { | |
| int numOfComponents = 0; | |
| ints.add(numOfComponents); | |
| void pushComponents(int value) { | |
| if (value >= 0x100000000) { | |
| pushComponents(value ~/ 0x100000000); | |
| } | |
| numOfComponents++; | |
| ints.add(value & 0xFFFFFFFF); | |
| } | |
| pushComponents(value); | |
| ints[ints.length - 1 - numOfComponents] = numOfComponents; | |
| operations.add(UnlinkedExprOperation.pushLongInt); | |
| } else { | |
| operations.add(UnlinkedExprOperation.pushInt); | |
| ints.add(value); | |
| } | |
| } | |
| /// Serialize the given [expr] expression into this serializer state. | |
| void _serialize(Expression expr, {bool emptyExpressionPermitted: false}) { | |
| if (emptyExpressionPermitted && expr == null) { | |
| operations.add(UnlinkedExprOperation.pushEmptyExpression); | |
| } else if (expr is IntegerLiteral) { | |
| int value = expr.value ?? 0; | |
| if (value >= 0) { | |
| _pushInt(value); | |
| } else { | |
| _pushInt(-value); | |
| operations.add(UnlinkedExprOperation.negate); | |
| } | |
| } else if (expr is DoubleLiteral) { | |
| operations.add(UnlinkedExprOperation.pushDouble); | |
| doubles.add(expr.value); | |
| } else if (expr is BooleanLiteral) { | |
| if (expr.value) { | |
| operations.add(UnlinkedExprOperation.pushTrue); | |
| } else { | |
| operations.add(UnlinkedExprOperation.pushFalse); | |
| } | |
| } else if (expr is StringLiteral) { | |
| _serializeString(expr); | |
| } else if (expr is SymbolLiteral) { | |
| strings.add(expr.components.map((token) => token.lexeme).join('.')); | |
| operations.add(UnlinkedExprOperation.makeSymbol); | |
| } else if (expr is NullLiteral) { | |
| operations.add(UnlinkedExprOperation.pushNull); | |
| } else if (expr is Identifier) { | |
| if (expr is SimpleIdentifier && isParameterName(expr.name)) { | |
| strings.add(expr.name); | |
| operations.add(UnlinkedExprOperation.pushParameter); | |
| } else if (expr is PrefixedIdentifier && | |
| isParameterName(expr.prefix.name)) { | |
| strings.add(expr.prefix.name); | |
| operations.add(UnlinkedExprOperation.pushParameter); | |
| strings.add(expr.identifier.name); | |
| operations.add(UnlinkedExprOperation.extractProperty); | |
| } else { | |
| references.add(serializeIdentifier(expr)); | |
| operations.add(UnlinkedExprOperation.pushReference); | |
| } | |
| } else if (expr is InstanceCreationExpression) { | |
| if (!expr.isConst) { | |
| isValidConst = false; | |
| } | |
| TypeName typeName = expr.constructorName.type; | |
| serializeInstanceCreation( | |
| serializeConstructorRef( | |
| typeName.name, typeName.typeArguments, expr.constructorName.name), | |
| expr.argumentList, | |
| typeName.typeArguments != null); | |
| } else if (expr is ListLiteral) { | |
| _serializeListLiteral(expr); | |
| } else if (expr is SetOrMapLiteral) { | |
| _serializeSetOrMapLiteral(expr); | |
| } else if (expr is MethodInvocation) { | |
| _serializeMethodInvocation(expr); | |
| } else if (expr is BinaryExpression) { | |
| _serializeBinaryExpression(expr); | |
| } else if (expr is ConditionalExpression) { | |
| _serialize(expr.condition); | |
| _serialize(expr.thenExpression); | |
| _serialize(expr.elseExpression); | |
| operations.add(UnlinkedExprOperation.conditional); | |
| } else if (expr is PrefixExpression) { | |
| _serializePrefixExpression(expr); | |
| } else if (expr is PostfixExpression) { | |
| _serializePostfixExpression(expr); | |
| } else if (expr is PropertyAccess) { | |
| _serializePropertyAccess(expr); | |
| } else if (expr is ParenthesizedExpression) { | |
| _serialize(expr.expression); | |
| } else if (expr is IndexExpression) { | |
| isValidConst = false; | |
| _serialize(expr.target); | |
| _serialize(expr.index); | |
| operations.add(UnlinkedExprOperation.extractIndex); | |
| } else if (expr is AssignmentExpression) { | |
| _serializeAssignment(expr); | |
| } else if (expr is CascadeExpression) { | |
| isValidConst = false; | |
| _serialize(expr.target); | |
| } else if (expr is FunctionExpression) { | |
| isValidConst = false; | |
| List<int> indices = serializeFunctionExpression(expr); | |
| if (indices != null) { | |
| ints.addAll(serializeFunctionExpression(expr)); | |
| operations.add(UnlinkedExprOperation.pushLocalFunctionReference); | |
| } else { | |
| // Invalid expression; just push null. | |
| operations.add(UnlinkedExprOperation.pushNull); | |
| } | |
| } else if (expr is FunctionExpressionInvocation) { | |
| isValidConst = false; | |
| _serialize(expr.function); | |
| _serializeArguments(expr.argumentList, expr.typeArguments != null); | |
| strings.add('call'); | |
| _serializeTypeArguments(expr.typeArguments); | |
| operations.add(UnlinkedExprOperation.invokeMethod); | |
| } else if (expr is AsExpression) { | |
| isValidConst = false; | |
| _serialize(expr.expression); | |
| references.add(serializeType(expr.type)); | |
| operations.add(UnlinkedExprOperation.typeCast); | |
| } else if (expr is IsExpression) { | |
| isValidConst = false; | |
| _serialize(expr.expression); | |
| references.add(serializeType(expr.type)); | |
| operations.add(UnlinkedExprOperation.typeCheck); | |
| if (expr.notOperator != null) { | |
| operations.add(UnlinkedExprOperation.not); | |
| } | |
| } else if (expr is SuperExpression) { | |
| operations.add(UnlinkedExprOperation.pushSuper); | |
| } else if (expr is ThisExpression) { | |
| operations.add(UnlinkedExprOperation.pushThis); | |
| } else if (expr is ThrowExpression) { | |
| isValidConst = false; | |
| _serialize(expr.expression); | |
| operations.add(UnlinkedExprOperation.throwException); | |
| } else if (expr is AwaitExpression) { | |
| isValidConst = false; | |
| _serialize(expr.expression); | |
| operations.add(UnlinkedExprOperation.await); | |
| } else { | |
| throw new StateError('Unknown expression type: $expr'); | |
| } | |
| } | |
| void _serializeArguments( | |
| ArgumentList argumentList, bool typeArgumentsProvided) { | |
| if (forConst || !typeArgumentsProvided) { | |
| List<Expression> arguments = argumentList.arguments; | |
| // Serialize the arguments. | |
| List<String> argumentNames = <String>[]; | |
| arguments.forEach((arg) { | |
| if (arg is NamedExpression) { | |
| argumentNames.add(arg.name.label.name); | |
| _serialize(arg.expression); | |
| } else { | |
| _serialize(arg); | |
| } | |
| }); | |
| // Add numbers of named and positional arguments, and the op-code. | |
| ints.add(argumentNames.length); | |
| strings.addAll(argumentNames); | |
| ints.add(arguments.length - argumentNames.length); | |
| } else { | |
| ints.add(0); | |
| ints.add(0); | |
| } | |
| } | |
| void _serializeAssignment(AssignmentExpression expr) { | |
| isValidConst = false; | |
| // Push the value. | |
| _serialize(expr.rightHandSide); | |
| // Push the assignment operator. | |
| TokenType operator = expr.operator.type; | |
| UnlinkedExprAssignOperator assignmentOperator; | |
| if (operator == TokenType.EQ) { | |
| assignmentOperator = UnlinkedExprAssignOperator.assign; | |
| } else if (operator == TokenType.QUESTION_QUESTION_EQ) { | |
| assignmentOperator = UnlinkedExprAssignOperator.ifNull; | |
| } else if (operator == TokenType.STAR_EQ) { | |
| assignmentOperator = UnlinkedExprAssignOperator.multiply; | |
| } else if (operator == TokenType.SLASH_EQ) { | |
| assignmentOperator = UnlinkedExprAssignOperator.divide; | |
| } else if (operator == TokenType.TILDE_SLASH_EQ) { | |
| assignmentOperator = UnlinkedExprAssignOperator.floorDivide; | |
| } else if (operator == TokenType.PERCENT_EQ) { | |
| assignmentOperator = UnlinkedExprAssignOperator.modulo; | |
| } else if (operator == TokenType.PLUS_EQ) { | |
| assignmentOperator = UnlinkedExprAssignOperator.plus; | |
| } else if (operator == TokenType.MINUS_EQ) { | |
| assignmentOperator = UnlinkedExprAssignOperator.minus; | |
| } else if (operator == TokenType.LT_LT_EQ) { | |
| assignmentOperator = UnlinkedExprAssignOperator.shiftLeft; | |
| } else if (operator == TokenType.GT_GT_EQ) { | |
| assignmentOperator = UnlinkedExprAssignOperator.shiftRight; | |
| } else if (operator == TokenType.AMPERSAND_EQ) { | |
| assignmentOperator = UnlinkedExprAssignOperator.bitAnd; | |
| } else if (operator == TokenType.CARET_EQ) { | |
| assignmentOperator = UnlinkedExprAssignOperator.bitXor; | |
| } else if (operator == TokenType.BAR_EQ) { | |
| assignmentOperator = UnlinkedExprAssignOperator.bitOr; | |
| } else { | |
| throw new StateError('Unknown assignment operator: $operator'); | |
| } | |
| assignmentOperators.add(assignmentOperator); | |
| // Push the assignment to the LHS. | |
| _pushAssignable(expr.leftHandSide); | |
| } | |
| void _serializeBinaryExpression(BinaryExpression expr) { | |
| _serialize(expr.leftOperand); | |
| _serialize(expr.rightOperand); | |
| TokenType operator = expr.operator.type; | |
| if (operator == TokenType.EQ_EQ) { | |
| operations.add(UnlinkedExprOperation.equal); | |
| } else if (operator == TokenType.BANG_EQ) { | |
| operations.add(UnlinkedExprOperation.notEqual); | |
| } else if (operator == TokenType.AMPERSAND_AMPERSAND) { | |
| operations.add(UnlinkedExprOperation.and); | |
| } else if (operator == TokenType.BAR_BAR) { | |
| operations.add(UnlinkedExprOperation.or); | |
| } else if (operator == TokenType.CARET) { | |
| operations.add(UnlinkedExprOperation.bitXor); | |
| } else if (operator == TokenType.AMPERSAND) { | |
| operations.add(UnlinkedExprOperation.bitAnd); | |
| } else if (operator == TokenType.BAR) { | |
| operations.add(UnlinkedExprOperation.bitOr); | |
| } else if (operator == TokenType.GT_GT) { | |
| operations.add(UnlinkedExprOperation.bitShiftRight); | |
| } else if (operator == TokenType.GT_GT_GT) { | |
| operations.add(UnlinkedExprOperation.bitShiftRightLogical); | |
| } else if (operator == TokenType.LT_LT) { | |
| operations.add(UnlinkedExprOperation.bitShiftLeft); | |
| } else if (operator == TokenType.PLUS) { | |
| operations.add(UnlinkedExprOperation.add); | |
| } else if (operator == TokenType.MINUS) { | |
| operations.add(UnlinkedExprOperation.subtract); | |
| } else if (operator == TokenType.STAR) { | |
| operations.add(UnlinkedExprOperation.multiply); | |
| } else if (operator == TokenType.SLASH) { | |
| operations.add(UnlinkedExprOperation.divide); | |
| } else if (operator == TokenType.TILDE_SLASH) { | |
| operations.add(UnlinkedExprOperation.floorDivide); | |
| } else if (operator == TokenType.GT) { | |
| operations.add(UnlinkedExprOperation.greater); | |
| } else if (operator == TokenType.LT) { | |
| operations.add(UnlinkedExprOperation.less); | |
| } else if (operator == TokenType.GT_EQ) { | |
| operations.add(UnlinkedExprOperation.greaterEqual); | |
| } else if (operator == TokenType.LT_EQ) { | |
| operations.add(UnlinkedExprOperation.lessEqual); | |
| } else if (operator == TokenType.PERCENT) { | |
| operations.add(UnlinkedExprOperation.modulo); | |
| } else if (operator == TokenType.QUESTION_QUESTION) { | |
| operations.add(UnlinkedExprOperation.ifNull); | |
| } else { | |
| throw new StateError('Unknown operator: $operator'); | |
| } | |
| } | |
| void _serializeCollectionElement(CollectionElement element) { | |
| if (element is Expression) { | |
| _serialize(element); | |
| } else if (element is MapLiteralEntry) { | |
| _serialize(element.key); | |
| _serialize(element.value); | |
| operations.add(UnlinkedExprOperation.makeMapLiteralEntry); | |
| } else if (element is SpreadElement) { | |
| _serialize(element.expression); | |
| bool isNullAware = element.spreadOperator.type == | |
| TokenType.PERIOD_PERIOD_PERIOD_QUESTION; | |
| operations.add(isNullAware | |
| ? UnlinkedExprOperation.nullAwareSpreadElement | |
| : UnlinkedExprOperation.spreadElement); | |
| } else if (element is IfElement) { | |
| _serialize(element.condition); | |
| _serializeCollectionElement(element.thenElement); | |
| var elseElement = element.elseElement; | |
| if (elseElement == null) { | |
| operations.add(UnlinkedExprOperation.ifElement); | |
| } else { | |
| _serializeCollectionElement(elseElement); | |
| operations.add(UnlinkedExprOperation.ifElseElement); | |
| } | |
| } else if (element is ForElement) { | |
| isValidConst = false; | |
| var parts = element.forLoopParts; | |
| int numVariablesToPop = 0; | |
| if (parts is ForParts) { | |
| if (parts is ForPartsWithExpression) { | |
| _serialize(parts.initialization, emptyExpressionPermitted: true); | |
| } else if (parts is ForPartsWithDeclarations) { | |
| for (var variable in parts.variables.variables) { | |
| operations.add(UnlinkedExprOperation.variableDeclarationStart); | |
| var name = variable.name.name; | |
| strings.add(name); | |
| pushVariableName(name); | |
| ++numVariablesToPop; | |
| _serialize(variable.initializer, emptyExpressionPermitted: true); | |
| operations.add(UnlinkedExprOperation.variableDeclaration); | |
| ints.add(0); | |
| } | |
| var type = parts.variables.type; | |
| if (type == null) { | |
| operations | |
| .add(UnlinkedExprOperation.forInitializerDeclarationsUntyped); | |
| } else { | |
| references.add(serializeType(type)); | |
| operations | |
| .add(UnlinkedExprOperation.forInitializerDeclarationsTyped); | |
| } | |
| ints.add(parts.variables.variables.length); | |
| } else { | |
| throw StateError('Unrecognized for parts'); | |
| } | |
| _serialize(parts.condition, emptyExpressionPermitted: true); | |
| for (var updater in parts.updaters) { | |
| _serialize(updater); | |
| } | |
| operations.add(UnlinkedExprOperation.forParts); | |
| ints.add(parts.updaters.length); | |
| } else if (parts is ForEachParts) { | |
| if (parts is ForEachPartsWithIdentifier) { | |
| _serialize(parts.identifier); | |
| _serialize(parts.iterable); | |
| operations.add(UnlinkedExprOperation.forEachPartsWithIdentifier); | |
| } else if (parts is ForEachPartsWithDeclaration) { | |
| _serialize(parts.iterable); | |
| var type = parts.loopVariable.type; | |
| if (type == null) { | |
| operations | |
| .add(UnlinkedExprOperation.forEachPartsWithUntypedDeclaration); | |
| } else { | |
| references.add(serializeType(type)); | |
| operations | |
| .add(UnlinkedExprOperation.forEachPartsWithTypedDeclaration); | |
| } | |
| var name = parts.loopVariable.identifier.name; | |
| strings.add(name); | |
| pushVariableName(name); | |
| ++numVariablesToPop; | |
| } else { | |
| throw StateError('Unrecognized for parts'); | |
| } | |
| } else { | |
| throw StateError('Unrecognized for parts'); | |
| } | |
| _serializeCollectionElement(element.body); | |
| popVariableNames(numVariablesToPop); | |
| operations.add(element.awaitKeyword == null | |
| ? UnlinkedExprOperation.forElement | |
| : UnlinkedExprOperation.forElementWithAwait); | |
| } else { | |
| throw new StateError('Unsupported CollectionElement: $element'); | |
| } | |
| } | |
| void _serializeListLiteral(ListLiteral expr) { | |
| if (forConst || expr.typeArguments == null) { | |
| List<CollectionElement> elements = expr.elements; | |
| elements.forEach(_serializeCollectionElement); | |
| ints.add(elements.length); | |
| } else { | |
| ints.add(0); | |
| } | |
| if (expr.typeArguments != null && | |
| expr.typeArguments.arguments.length == 1) { | |
| references.add(serializeType(expr.typeArguments.arguments[0])); | |
| operations.add(UnlinkedExprOperation.makeTypedList); | |
| } else { | |
| operations.add(UnlinkedExprOperation.makeUntypedList); | |
| } | |
| } | |
| void _serializeMethodInvocation(MethodInvocation invocation) { | |
| Expression target = invocation.target; | |
| SimpleIdentifier methodName = invocation.methodName; | |
| ArgumentList argumentList = invocation.argumentList; | |
| if (_isIdentifierSequence(methodName)) { | |
| EntityRefBuilder ref = serializeIdentifierSequence(methodName); | |
| _serializeArguments(argumentList, invocation.typeArguments != null); | |
| references.add(ref); | |
| _serializeTypeArguments(invocation.typeArguments); | |
| operations.add(UnlinkedExprOperation.invokeMethodRef); | |
| } else { | |
| if (!invocation.isCascaded) { | |
| _serialize(target); | |
| } | |
| _serializeArguments(argumentList, invocation.typeArguments != null); | |
| strings.add(methodName.name); | |
| _serializeTypeArguments(invocation.typeArguments); | |
| operations.add(UnlinkedExprOperation.invokeMethod); | |
| } | |
| } | |
| void _serializePostfixExpression(PostfixExpression expr) { | |
| TokenType operator = expr.operator.type; | |
| Expression operand = expr.operand; | |
| if (operator == TokenType.PLUS_PLUS) { | |
| _serializePrefixPostfixIncDec( | |
| operand, UnlinkedExprAssignOperator.postfixIncrement); | |
| } else if (operator == TokenType.MINUS_MINUS) { | |
| _serializePrefixPostfixIncDec( | |
| operand, UnlinkedExprAssignOperator.postfixDecrement); | |
| } else { | |
| throw new StateError('Unknown operator: $operator'); | |
| } | |
| } | |
| void _serializePrefixExpression(PrefixExpression expr) { | |
| TokenType operator = expr.operator.type; | |
| Expression operand = expr.operand; | |
| if (operator == TokenType.BANG) { | |
| _serialize(operand); | |
| operations.add(UnlinkedExprOperation.not); | |
| } else if (operator == TokenType.MINUS) { | |
| _serialize(operand); | |
| operations.add(UnlinkedExprOperation.negate); | |
| } else if (operator == TokenType.TILDE) { | |
| _serialize(operand); | |
| operations.add(UnlinkedExprOperation.complement); | |
| } else if (operator == TokenType.PLUS_PLUS) { | |
| _serializePrefixPostfixIncDec( | |
| operand, UnlinkedExprAssignOperator.prefixIncrement); | |
| } else if (operator == TokenType.MINUS_MINUS) { | |
| _serializePrefixPostfixIncDec( | |
| operand, UnlinkedExprAssignOperator.prefixDecrement); | |
| } else { | |
| throw new StateError('Unknown operator: $operator'); | |
| } | |
| } | |
| void _serializePrefixPostfixIncDec( | |
| Expression operand, UnlinkedExprAssignOperator operator) { | |
| isValidConst = false; | |
| assignmentOperators.add(operator); | |
| _pushAssignable(operand); | |
| } | |
| void _serializePropertyAccess(PropertyAccess expr) { | |
| if (_isIdentifierSequence(expr)) { | |
| EntityRefBuilder ref = serializeIdentifierSequence(expr); | |
| references.add(ref); | |
| operations.add(UnlinkedExprOperation.pushReference); | |
| } else { | |
| if (!expr.isCascaded) { | |
| _serialize(expr.target); | |
| } | |
| strings.add(expr.propertyName.name); | |
| operations.add(UnlinkedExprOperation.extractProperty); | |
| } | |
| } | |
| void _serializeSetOrMapLiteral(SetOrMapLiteral expr) { | |
| if (forConst || expr.typeArguments == null) { | |
| for (CollectionElement element in expr.elements) { | |
| _serializeCollectionElement(element); | |
| } | |
| ints.add(expr.elements.length); | |
| } else { | |
| ints.add(0); | |
| } | |
| List<TypeAnnotation> typeArguments = expr.typeArguments?.arguments; | |
| if (typeArguments != null && typeArguments.length == 2) { | |
| references.add(serializeType(typeArguments[0])); | |
| references.add(serializeType(typeArguments[1])); | |
| operations.add(UnlinkedExprOperation.makeTypedMap2); | |
| } else if (typeArguments != null && typeArguments.length == 1) { | |
| references.add(serializeType(typeArguments[0])); | |
| operations.add(UnlinkedExprOperation.makeTypedSet); | |
| } else { | |
| operations.add(UnlinkedExprOperation.makeUntypedSetOrMap); | |
| } | |
| } | |
| void _serializeString(StringLiteral expr) { | |
| if (expr is AdjacentStrings) { | |
| if (expr.strings.every((string) => string is SimpleStringLiteral)) { | |
| operations.add(UnlinkedExprOperation.pushString); | |
| strings.add(expr.stringValue); | |
| } else { | |
| expr.strings.forEach(_serializeString); | |
| operations.add(UnlinkedExprOperation.concatenate); | |
| ints.add(expr.strings.length); | |
| } | |
| } else if (expr is SimpleStringLiteral) { | |
| operations.add(UnlinkedExprOperation.pushString); | |
| strings.add(expr.value); | |
| } else { | |
| StringInterpolation interpolation = expr as StringInterpolation; | |
| for (InterpolationElement element in interpolation.elements) { | |
| if (element is InterpolationString) { | |
| operations.add(UnlinkedExprOperation.pushString); | |
| strings.add(element.value); | |
| } else { | |
| _serialize((element as InterpolationExpression).expression); | |
| } | |
| } | |
| operations.add(UnlinkedExprOperation.concatenate); | |
| ints.add(interpolation.elements.length); | |
| } | |
| } | |
| void _serializeTypeArguments(TypeArgumentList typeArguments) { | |
| if (typeArguments == null) { | |
| ints.add(0); | |
| } else { | |
| ints.add(typeArguments.arguments.length); | |
| for (TypeAnnotation type in typeArguments.arguments) { | |
| references.add(serializeType(type)); | |
| } | |
| } | |
| } | |
| EntityRefNullabilitySuffix computeNullabilitySuffix(Token question); | |
| } |