blob: 75a487cb0ad6021615b35e29d7480e0100e23428 [file] [log] [blame]
// 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.
library serialization.summarize_const_expr;
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].
*/
UnlinkedConstructorInitializer 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 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 [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 [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 [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 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);
} 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) {
if (invocation.target != null ||
invocation.methodName.name != 'identical') {
isValidConst = false;
}
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));
}
}
}
}