blob: 8d4f97bcb202af9153f28c9a6149c9fcff625a06 [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/src/generated/ast.dart';
import 'package:analyzer/src/generated/scanner.dart';
import 'package:analyzer/src/summary/format.dart';
/**
* Instances of this class keep track of intermediate state during
* serialization of a single constant [Expression].
*/
abstract class AbstractConstExprSerializer {
/**
* See [UnlinkedConstBuilder.operations].
*/
final List<UnlinkedConstOperation> operations = <UnlinkedConstOperation>[];
/**
* See [UnlinkedConstBuilder.ints].
*/
final List<int> ints = <int>[];
/**
* See [UnlinkedConstBuilder.doubles].
*/
final List<double> doubles = <double>[];
/**
* See [UnlinkedConstBuilder.strings].
*/
final List<String> strings = <String>[];
/**
* See [UnlinkedConstBuilder.references].
*/
final List<EntityRefBuilder> references = <EntityRefBuilder>[];
/**
* Serialize the given [expr] expression into this serializer state.
*/
void serialize(Expression expr) {
if (expr is IntegerLiteral) {
_pushInt(expr.value);
} else if (expr is DoubleLiteral) {
operations.add(UnlinkedConstOperation.pushDouble);
doubles.add(expr.value);
} else if (expr is BooleanLiteral) {
if (expr.value) {
operations.add(UnlinkedConstOperation.pushTrue);
} else {
operations.add(UnlinkedConstOperation.pushFalse);
}
} else if (expr is StringLiteral) {
_serializeString(expr);
} else if (expr is SymbolLiteral) {
strings.add(expr.components.map((token) => token.lexeme).join('.'));
operations.add(UnlinkedConstOperation.makeSymbol);
} else if (expr is NullLiteral) {
operations.add(UnlinkedConstOperation.pushNull);
} else if (expr is Identifier) {
references.add(serializeIdentifier(expr));
operations.add(UnlinkedConstOperation.pushReference);
} else if (expr is InstanceCreationExpression) {
_serializeInstanceCreation(expr);
} else if (expr is ListLiteral) {
_serializeListLiteral(expr);
} else if (expr is MapLiteral) {
_serializeMapLiteral(expr);
} else if (expr is MethodInvocation) {
String name = expr.methodName.name;
if (name != 'identical') {
throw new _ConstExprSerializationError(
'Only "identity" function invocation is allowed.');
}
if (expr.argumentList == null ||
expr.argumentList.arguments.length != 2) {
throw new _ConstExprSerializationError(
'The function "identity" requires exactly 2 arguments.');
}
expr.argumentList.arguments.forEach(serialize);
operations.add(UnlinkedConstOperation.identical);
} else if (expr is BinaryExpression) {
_serializeBinaryExpression(expr);
} else if (expr is ConditionalExpression) {
serialize(expr.condition);
serialize(expr.thenExpression);
serialize(expr.elseExpression);
operations.add(UnlinkedConstOperation.conditional);
} else if (expr is PrefixExpression) {
_serializePrefixExpression(expr);
} else if (expr is PropertyAccess) {
if (expr.target is! PrefixedIdentifier &&
expr.propertyName.name == 'length') {
serialize(expr.target);
operations.add(UnlinkedConstOperation.length);
} else {
references.add(serializePropertyAccess(expr));
operations.add(UnlinkedConstOperation.pushReference);
}
} else if (expr is ParenthesizedExpression) {
serialize(expr.expression);
} else {
throw new _ConstExprSerializationError('Unknown expression type: $expr');
}
}
/**
* Return [EntityRefBuilder] that corresponds to the given [constructor].
*/
EntityRefBuilder serializeConstructorName(ConstructorName constructor);
/**
* Return [EntityRefBuilder] that corresponds to the given [identifier].
*/
EntityRefBuilder serializeIdentifier(Identifier identifier);
/**
* Return [EntityRefBuilder] that corresponds to the given [access].
*/
EntityRefBuilder serializePropertyAccess(PropertyAccess access);
/**
* Return [EntityRefBuilder] that corresponds to the given [type].
*/
EntityRefBuilder serializeType(TypeName type);
/**
* Return the [UnlinkedConstBuilder] that corresponds to the state of this
* serializer.
*/
UnlinkedConstBuilder toBuilder() {
return new UnlinkedConstBuilder(
operations: operations,
ints: ints,
doubles: doubles,
strings: strings,
references: references);
}
void _pushInt(int value) {
assert(value >= 0);
if (value >= (1 << 32)) {
int numOfComponents = 0;
ints.add(numOfComponents);
void pushComponents(int value) {
if (value >= (1 << 32)) {
pushComponents(value >> 32);
}
numOfComponents++;
ints.add(value & 0xFFFFFFFF);
}
pushComponents(value);
ints[ints.length - 1 - numOfComponents] = numOfComponents;
operations.add(UnlinkedConstOperation.pushLongInt);
} else {
operations.add(UnlinkedConstOperation.pushInt);
ints.add(value);
}
}
void _serializeBinaryExpression(BinaryExpression expr) {
serialize(expr.leftOperand);
serialize(expr.rightOperand);
TokenType operator = expr.operator.type;
if (operator == TokenType.EQ_EQ) {
operations.add(UnlinkedConstOperation.equal);
} else if (operator == TokenType.BANG_EQ) {
operations.add(UnlinkedConstOperation.notEqual);
} else if (operator == TokenType.AMPERSAND_AMPERSAND) {
operations.add(UnlinkedConstOperation.and);
} else if (operator == TokenType.BAR_BAR) {
operations.add(UnlinkedConstOperation.or);
} else if (operator == TokenType.CARET) {
operations.add(UnlinkedConstOperation.bitXor);
} else if (operator == TokenType.AMPERSAND) {
operations.add(UnlinkedConstOperation.bitAnd);
} else if (operator == TokenType.BAR) {
operations.add(UnlinkedConstOperation.bitOr);
} else if (operator == TokenType.GT_GT) {
operations.add(UnlinkedConstOperation.bitShiftRight);
} else if (operator == TokenType.LT_LT) {
operations.add(UnlinkedConstOperation.bitShiftLeft);
} else if (operator == TokenType.PLUS) {
operations.add(UnlinkedConstOperation.add);
} else if (operator == TokenType.MINUS) {
operations.add(UnlinkedConstOperation.subtract);
} else if (operator == TokenType.STAR) {
operations.add(UnlinkedConstOperation.multiply);
} else if (operator == TokenType.SLASH) {
operations.add(UnlinkedConstOperation.divide);
} else if (operator == TokenType.TILDE_SLASH) {
operations.add(UnlinkedConstOperation.floorDivide);
} else if (operator == TokenType.GT) {
operations.add(UnlinkedConstOperation.greater);
} else if (operator == TokenType.LT) {
operations.add(UnlinkedConstOperation.less);
} else if (operator == TokenType.GT_EQ) {
operations.add(UnlinkedConstOperation.greaterEqual);
} else if (operator == TokenType.LT_EQ) {
operations.add(UnlinkedConstOperation.lessEqual);
} else if (operator == TokenType.PERCENT) {
operations.add(UnlinkedConstOperation.modulo);
} else {
throw new _ConstExprSerializationError('Unknown operator: $operator');
}
}
void _serializeInstanceCreation(InstanceCreationExpression expr) {
ConstructorName constructor = expr.constructorName;
List<Expression> arguments = expr.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 the op-code and numbers of named and positional arguments.
operations.add(UnlinkedConstOperation.invokeConstructor);
ints.add(argumentNames.length);
strings.addAll(argumentNames);
ints.add(arguments.length - argumentNames.length);
// Serialize the reference.
references.add(serializeConstructorName(constructor));
}
void _serializeListLiteral(ListLiteral expr) {
List<Expression> elements = expr.elements;
elements.forEach(serialize);
ints.add(elements.length);
if (expr.typeArguments != null &&
expr.typeArguments.arguments.length == 1) {
references.add(serializeType(expr.typeArguments.arguments[0]));
operations.add(UnlinkedConstOperation.makeTypedList);
} else {
operations.add(UnlinkedConstOperation.makeUntypedList);
}
}
void _serializeMapLiteral(MapLiteral expr) {
for (MapLiteralEntry entry in expr.entries) {
serialize(entry.key);
serialize(entry.value);
}
ints.add(expr.entries.length);
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(UnlinkedConstOperation.makeTypedMap);
} else {
operations.add(UnlinkedConstOperation.makeUntypedMap);
}
}
void _serializePrefixExpression(PrefixExpression expr) {
serialize(expr.operand);
TokenType operator = expr.operator.type;
if (operator == TokenType.BANG) {
operations.add(UnlinkedConstOperation.not);
} else if (operator == TokenType.MINUS) {
operations.add(UnlinkedConstOperation.negate);
} else if (operator == TokenType.TILDE) {
operations.add(UnlinkedConstOperation.complement);
} else {
throw new _ConstExprSerializationError('Unknown operator: $operator');
}
}
void _serializeString(StringLiteral expr) {
if (expr is AdjacentStrings) {
if (expr.strings.every((string) => string is SimpleStringLiteral)) {
operations.add(UnlinkedConstOperation.pushString);
strings.add(expr.stringValue);
} else {
expr.strings.forEach(_serializeString);
operations.add(UnlinkedConstOperation.concatenate);
ints.add(expr.strings.length);
}
} else if (expr is SimpleStringLiteral) {
operations.add(UnlinkedConstOperation.pushString);
strings.add(expr.value);
} else {
StringInterpolation interpolation = expr as StringInterpolation;
for (InterpolationElement element in interpolation.elements) {
if (element is InterpolationString) {
operations.add(UnlinkedConstOperation.pushString);
strings.add(element.value);
} else {
serialize((element as InterpolationExpression).expression);
}
}
operations.add(UnlinkedConstOperation.concatenate);
ints.add(interpolation.elements.length);
}
}
}
/**
* Error that describes a problem during a constant expression serialization.
*/
class _ConstExprSerializationError {
final String message;
_ConstExprSerializationError(this.message);
@override
String toString() => message;
}