Chromium Code Reviews| Index: pkg/analyzer/lib/src/summary/summarize_elements.dart |
| diff --git a/pkg/analyzer/lib/src/summary/summarize_elements.dart b/pkg/analyzer/lib/src/summary/summarize_elements.dart |
| index 13d568870817c9f97de5ac933963014ac6dd8554..d18f856829781a0b327ba261d743162f3e104444 100644 |
| --- a/pkg/analyzer/lib/src/summary/summarize_elements.dart |
| +++ b/pkg/analyzer/lib/src/summary/summarize_elements.dart |
| @@ -6,8 +6,11 @@ library serialization.elements; |
| import 'package:analyzer/dart/element/element.dart'; |
| import 'package:analyzer/dart/element/type.dart'; |
| +import 'package:analyzer/src/dart/element/element.dart'; |
| import 'package:analyzer/src/dart/element/type.dart'; |
| +import 'package:analyzer/src/generated/ast.dart'; |
| import 'package:analyzer/src/generated/resolver.dart'; |
| +import 'package:analyzer/src/generated/scanner.dart'; |
| import 'package:analyzer/src/generated/utilities_dart.dart'; |
| import 'package:analyzer/src/summary/format.dart'; |
| import 'package:analyzer/src/summary/name_filter.dart'; |
| @@ -50,8 +53,278 @@ class LibrarySerializationResult { |
| } |
| /** |
| + * Error that described a problem during a constant expression serialization. |
|
Paul Berry
2016/01/19 20:02:10
s/described/describes/
scheglov
2016/01/19 20:59:28
Done.
|
| + */ |
| +class _ConstExprSerializationError { |
| + final String message; |
| + |
| + _ConstExprSerializationError(this.message); |
| + |
| + @override |
| + String toString() => message; |
| +} |
| + |
| +/** |
| * Instances of this class keep track of intermediate state during |
| - * serialization of a single library.` |
| + * serialization of a single constant [Expression]. |
| + */ |
| +class _ConstExprSerializer { |
| + final _LibrarySerializer serializer; |
| + |
| + /** |
| + * 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<UnlinkedTypeRefBuilder> references = <UnlinkedTypeRefBuilder>[]; |
| + |
| + _ConstExprSerializer(this.serializer); |
| + |
| + /** |
| + * 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) { |
| + for (Token component in expr.components) { |
|
Paul Berry
2016/01/19 20:02:10
Nit: why not just concatenate all the components t
scheglov
2016/01/19 20:59:28
Done.
|
| + operations.add(UnlinkedConstOperation.pushString); |
| + strings.add(component.lexeme); |
| + } |
| + operations.add(UnlinkedConstOperation.makeSymbol); |
| + ints.add(expr.components.length); |
| + } else if (expr is NullLiteral) { |
| + operations.add(UnlinkedConstOperation.pushNull); |
| + } else if (expr is Identifier) { |
| + Element element = expr.staticElement; |
| + assert(element != null); |
| + // TODO(scheglov) how to serialize element references? |
|
Paul Berry
2016/01/19 20:02:10
Is this TODO still applicable? At first glance it
scheglov
2016/01/19 20:59:28
Yes, I don't like how we do this.
We discussed thi
|
| + operations.add(UnlinkedConstOperation.pushReference); |
| + references.add(_serializeIdentifier(element)); |
| + } 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; |
| + assert(name == 'identical'); |
|
Paul Berry
2016/01/19 20:02:10
Add a TODO noting that these assertions will fail
scheglov
2016/01/19 20:59:28
I've added checks that throw _ConstExprSerializati
|
| + assert(expr.argumentList != null); |
| + assert(expr.argumentList.arguments.length == 2); |
| + 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 && expr.propertyName.name == 'length') { |
| + serialize(expr.target); |
| + operations.add(UnlinkedConstOperation.length); |
| + } else if (expr is ParenthesizedExpression) { |
| + serialize(expr.expression); |
| + } else { |
| + throw new _ConstExprSerializationError('Unknown expression type: $expr'); |
| + } |
| + } |
| + |
| + /** |
| + * 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) { |
| + if (value >= (2 << 32)) { |
|
Paul Berry
2016/01/19 20:02:10
I don't think this code handles all possible ints
scheglov
2016/01/19 20:59:28
Done.
|
| + _pushInt(value >> 32); |
| + operations.add(UnlinkedConstOperation.shiftOr); |
| + ints.add(value & 0xFFFFFFFF); |
| + } else { |
| + operations.add(UnlinkedConstOperation.pushInt); |
| + ints.add(value & 0xFFFFFFFF); |
| + } |
| + } |
| + |
| + 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.equal); |
| + operations.add(UnlinkedConstOperation.not); |
| + } 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'); |
| + } |
| + } |
| + |
| + UnlinkedTypeRefBuilder _serializeIdentifier(Element element) { |
| + return new UnlinkedTypeRefBuilder( |
| + reference: serializer._getElementReferenceId(element)); |
| + } |
| + |
| + void _serializeInstanceCreation(InstanceCreationExpression expr) { |
| + List<Expression> arguments = expr.argumentList.arguments; |
| + arguments.forEach(serialize); |
| + ConstructorElement element = expr.staticElement; |
| + assert(element != null); |
| + operations.add(UnlinkedConstOperation.invokeConstructor); |
| + references.add(serializer.serializeTypeRef(element.returnType, null)); |
| + strings.add(element.name); |
| + // TODO(scheglov) named arguments? |
|
Paul Berry
2016/01/19 20:02:10
Oh yeah, good catch. Let me know if you want to b
|
| + ints.add(arguments.length); |
| + } |
| + |
| + void _serializeListLiteral(ListLiteral expr) { |
| + List<Expression> elements = expr.elements; |
| + elements.forEach(serialize); |
| + DartType typeArgument; |
| + if (expr.typeArguments != null && |
| + expr.typeArguments.arguments.length == 1) { |
| + typeArgument = expr.typeArguments.arguments[0].type; |
| + } else { |
| + typeArgument = serializer.typeProvider.dynamicType; |
| + } |
| + references.add(serializer.serializeTypeRef(typeArgument, null)); |
| + ints.add(elements.length); |
| + operations.add(UnlinkedConstOperation.makeList); |
| + } |
| + |
| + void _serializeMapLiteral(MapLiteral expr) { |
| + for (MapLiteralEntry entry in expr.entries) { |
| + serialize(entry.key); |
| + serialize(entry.value); |
| + } |
| + DartType keyTypeArgument; |
| + DartType valueTypeArgument; |
| + if (expr.typeArguments != null && |
| + expr.typeArguments.arguments.length == 2) { |
| + keyTypeArgument = expr.typeArguments.arguments[0].type; |
| + valueTypeArgument = expr.typeArguments.arguments[1].type; |
| + } else { |
| + keyTypeArgument = serializer.typeProvider.dynamicType; |
| + valueTypeArgument = serializer.typeProvider.dynamicType; |
| + } |
| + references.add(serializer.serializeTypeRef(keyTypeArgument, null)); |
| + references.add(serializer.serializeTypeRef(valueTypeArgument, null)); |
| + ints.add(expr.entries.length); |
| + operations.add(UnlinkedConstOperation.makeMap); |
| + } |
| + |
| + void _serializePrefixExpression(PrefixExpression expr) { |
| + serialize(expr.operand); |
| + TokenType operator = expr.operator.type; |
| + if (operator == TokenType.BANG) { |
| + operations.add(UnlinkedConstOperation.not); |
| + } else if (operator == TokenType.TILDE) { |
| + operations.add(UnlinkedConstOperation.complement); |
| + } else { |
| + throw new _ConstExprSerializationError('Unknown operator: $operator'); |
| + } |
| + } |
| + |
| + void _serializeString(StringLiteral expr) { |
| + if (expr is AdjacentStrings) { |
| + operations.add(UnlinkedConstOperation.pushString); |
|
Paul Berry
2016/01/19 20:02:10
I think this does the wrong thing if one or more o
scheglov
2016/01/19 20:59:28
Done.
|
| + strings.add(expr.stringValue); |
| + } 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); |
| + } |
| + } |
| +} |
| + |
| +/** |
| + * Instances of this class keep track of intermediate state during |
| + * serialization of a single library. |
| */ |
| class _LibrarySerializer { |
| /** |
| @@ -385,6 +658,15 @@ class _LibrarySerializer { |
| } |
| /** |
| + * Serialize the given [expression], creating an [UnlinkedConstBuilder]. |
| + */ |
| + UnlinkedConstBuilder serializeConstExpr(Expression expression) { |
| + _ConstExprSerializer serializer = new _ConstExprSerializer(this); |
| + serializer.serialize(expression); |
| + return serializer.toBuilder(); |
| + } |
| + |
| + /** |
| * Return the index of the entry in the dependency table |
| * ([LinkedLibrary.dependencies]) for the given [dependentLibrary]. A new |
| * entry is added to the table if necessary to satisfy the request. |
| @@ -564,18 +846,7 @@ class _LibrarySerializer { |
| element.getAncestor((Element e) => e is CompilationUnitElement); |
| int unit = dependentLibrary.units.indexOf(unitElement); |
| assert(unit != -1); |
| - ReferenceKind kind; |
| - if (element is PropertyAccessorElement) { |
| - kind = ReferenceKind.topLevelPropertyAccessor; |
| - } else if (element is FunctionTypeAliasElement) { |
| - kind = ReferenceKind.typedef; |
| - } else if (element is ClassElement) { |
| - kind = ReferenceKind.classOrEnum; |
| - } else if (element is FunctionElement) { |
| - kind = ReferenceKind.topLevelFunction; |
| - } else { |
| - throw new Exception('Unexpected element kind: ${element.runtimeType}'); |
| - } |
| + ReferenceKind kind = _getReferenceKind(element); |
| exportNames.add(new LinkedExportNameBuilder( |
| name: name, |
| dependency: serializeDependency(dependentLibrary), |
| @@ -694,38 +965,7 @@ class _LibrarySerializer { |
| b.reference = serializeDynamicReference(); |
| } |
| } else { |
| - b.reference = referenceMap.putIfAbsent(element, () { |
| - assert(unlinkedReferences.length == linkedReferences.length); |
| - CompilationUnitElement unitElement = |
| - element.getAncestor((Element e) => e is CompilationUnitElement); |
| - int unit = dependentLibrary.units.indexOf(unitElement); |
| - assert(unit != -1); |
| - int numTypeParameters = 0; |
| - if (element is TypeParameterizedElement) { |
| - numTypeParameters = element.typeParameters.length; |
| - } |
| - // Figure out a prefix that may be used to refer to the given type. |
| - // TODO(paulberry): to avoid subtle relinking inconsistencies we |
| - // should use the actual prefix from the AST (a given type may be |
| - // reachable via multiple prefixes), but sadly, this information is |
| - // not recorded in the element model. |
| - int prefixReference = 0; |
| - PrefixElement prefix = prefixMap[element]; |
| - if (prefix != null) { |
| - prefixReference = serializePrefix(prefix); |
| - } |
| - int index = unlinkedReferences.length; |
| - unlinkedReferences.add(new UnlinkedReferenceBuilder( |
| - name: element.name, prefixReference: prefixReference)); |
| - linkedReferences.add(new LinkedReferenceBuilder( |
| - dependency: serializeDependency(dependentLibrary), |
| - kind: element is FunctionTypeAliasElement |
| - ? ReferenceKind.typedef |
| - : ReferenceKind.classOrEnum, |
| - unit: unit, |
| - numTypeParameters: numTypeParameters)); |
| - return index; |
| - }); |
| + b.reference = _getElementReferenceId(element); |
| } |
| List<DartType> typeArguments; |
| if (type is InterfaceType) { |
| @@ -778,6 +1018,63 @@ class _LibrarySerializer { |
| b.isConst = variable.isConst; |
| b.hasImplicitType = variable.hasImplicitType; |
| b.documentationComment = serializeDocumentation(variable); |
| + if (variable.isConst && variable is ConstVariableElement) { |
| + ConstVariableElement constVariable = variable as ConstVariableElement; |
| + Expression initializer = constVariable.constantInitializer; |
| + if (initializer != null) { |
| + b.constExpr = serializeConstExpr(initializer); |
| + } |
| + } |
| return b; |
| } |
| + |
| + int _getElementReferenceId(Element element) { |
| + LibraryElement dependentLibrary = element.library; |
| + return referenceMap.putIfAbsent(element, () { |
| + assert(unlinkedReferences.length == linkedReferences.length); |
| + CompilationUnitElement unitElement = |
| + element.getAncestor((Element e) => e is CompilationUnitElement); |
| + int unit = dependentLibrary.units.indexOf(unitElement); |
| + assert(unit != -1); |
| + int numTypeParameters = 0; |
| + if (element is TypeParameterizedElement) { |
| + numTypeParameters = element.typeParameters.length; |
| + } |
| + // Figure out a prefix that may be used to refer to the given type. |
| + // TODO(paulberry): to avoid subtle relinking inconsistencies we |
| + // should use the actual prefix from the AST (a given type may be |
| + // reachable via multiple prefixes), but sadly, this information is |
| + // not recorded in the element model. |
| + int prefixReference = 0; |
| + PrefixElement prefix = prefixMap[element]; |
| + if (prefix != null) { |
| + prefixReference = serializePrefix(prefix); |
| + } |
| + int index = unlinkedReferences.length; |
| + unlinkedReferences.add(new UnlinkedReferenceBuilder( |
| + name: element.name, prefixReference: prefixReference)); |
| + linkedReferences.add(new LinkedReferenceBuilder( |
| + dependency: serializeDependency(dependentLibrary), |
| + kind: _getReferenceKind(element), |
| + unit: unit, |
| + numTypeParameters: numTypeParameters)); |
| + return index; |
| + }); |
| + } |
| + |
| + ReferenceKind _getReferenceKind(Element element) { |
| + ReferenceKind kind; |
| + if (element is PropertyAccessorElement) { |
| + kind = ReferenceKind.topLevelPropertyAccessor; |
| + } else if (element is FunctionTypeAliasElement) { |
| + kind = ReferenceKind.typedef; |
| + } else if (element is ClassElement) { |
| + kind = ReferenceKind.classOrEnum; |
| + } else if (element is FunctionElement) { |
| + kind = ReferenceKind.topLevelFunction; |
| + } else { |
| + throw new Exception('Unexpected element kind: ${element.runtimeType}'); |
| + } |
| + return kind; |
| + } |
| } |