| // 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/dart/element/type.dart' show DartType; |
| 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}'); |
| } |
| |
| /** |
| * 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); |
| |
| /** |
| * 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]. It is expected that [type] |
| * corresponds to the given [typeName] and [typeArguments]. The parameter |
| * [type] might be `null` if the type is not resolved. |
| */ |
| EntityRefBuilder serializeConstructorRef(DartType type, 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?.type, type?.name, type?.typeArguments); |
| } |
| 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]. It is expected that [type] corresponds to the |
| * given [name] and [arguments]. The parameter [type] might be `null` if the |
| * type is not resolved. |
| */ |
| EntityRefBuilder serializeTypeName( |
| DartType type, Identifier name, TypeArgumentList arguments); |
| |
| /** |
| * Return the [UnlinkedExprBuilder] that corresponds to the state of this |
| * serializer. |
| */ |
| UnlinkedExprBuilder toBuilder() { |
| return new UnlinkedExprBuilder( |
| isValidConst: isValidConst, |
| operations: operations, |
| assignmentOperators: assignmentOperators, |
| ints: ints, |
| doubles: doubles, |
| strings: strings, |
| references: references); |
| } |
| |
| /** |
| * 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 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) { |
| 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.type, typeName.name, |
| typeName.typeArguments, expr.constructorName.name), |
| expr.argumentList, |
| typeName.typeArguments != null); |
| } else if (expr is ListLiteral) { |
| _serializeListLiteral(expr); |
| } else if (expr is MapLiteral) { |
| _serializeMapLiteral(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; |
| // TODO(scheglov) implement |
| operations.add(UnlinkedExprOperation.pushNull); |
| } 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.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 _serializeListLiteral(ListLiteral expr) { |
| if (forConst || expr.typeArguments == null) { |
| List<Expression> elements = expr.elements; |
| elements.forEach(_serialize); |
| 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 _serializeMapLiteral(MapLiteral expr) { |
| if (forConst || expr.typeArguments == null) { |
| for (MapLiteralEntry entry in expr.entries) { |
| _serialize(entry.key); |
| _serialize(entry.value); |
| } |
| ints.add(expr.entries.length); |
| } else { |
| ints.add(0); |
| } |
| if (expr.typeArguments != null && |
| expr.typeArguments.arguments.length == 2) { |
| references.add(serializeType(expr.typeArguments.arguments[0])); |
| references.add(serializeType(expr.typeArguments.arguments[1])); |
| operations.add(UnlinkedExprOperation.makeTypedMap); |
| } else { |
| operations.add(UnlinkedExprOperation.makeUntypedMap); |
| } |
| } |
| |
| 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 _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)); |
| } |
| } |
| } |
| } |