| // Copyright (c) 2018, 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/standard_ast_factory.dart'; |
| import 'package:analyzer/dart/ast/token.dart'; |
| import 'package:analyzer/dart/element/element.dart'; |
| import 'package:analyzer/dart/element/type.dart'; |
| import 'package:analyzer/src/dart/analysis/experiments.dart'; |
| import 'package:analyzer/src/dart/ast/ast.dart'; |
| import 'package:analyzer/src/dart/element/element.dart'; |
| import 'package:analyzer/src/generated/engine.dart'; |
| import 'package:analyzer/src/generated/resolver.dart' show AstRewriteVisitor; |
| import 'package:analyzer/src/generated/testing/ast_test_factory.dart'; |
| import 'package:analyzer/src/generated/testing/token_factory.dart'; |
| import 'package:analyzer/src/summary/idl.dart'; |
| import 'package:analyzer/src/summary/resynthesize.dart'; |
| |
| bool _isSpreadOrControlFlowEnabled(ExperimentStatus experimentStatus) => |
| experimentStatus.spread_collections || |
| experimentStatus.control_flow_collections; |
| |
| /** |
| * Builder of [Expression]s from [UnlinkedExpr]s. |
| */ |
| class ExprBuilder { |
| static const ARGUMENT_LIST = 'ARGUMENT_LIST'; |
| |
| final UnitResynthesizer resynthesizer; |
| final ElementImpl context; |
| final UnlinkedExpr _uc; |
| final bool requireValidConst; |
| final bool isSpreadOrControlFlowEnabled; |
| final bool becomeSetOrMap; |
| |
| int intPtr = 0; |
| int doublePtr = 0; |
| int stringPtr = 0; |
| int refPtr = 0; |
| int assignmentOperatorPtr = 0; |
| |
| // The stack of values. Note that they are usually [Expression]s, but may be |
| // any [CollectionElement] to support map/set/list literals. |
| final List<AstNode> stack = <AstNode>[]; |
| |
| final List<UnlinkedExecutable> localFunctions; |
| |
| final _VariablesInScope variablesInScope; |
| |
| ExprBuilder( |
| this.resynthesizer, |
| this.context, |
| this._uc, { |
| this.requireValidConst: true, |
| this.localFunctions, |
| _VariablesInScope variablesInScope, |
| this.becomeSetOrMap: true, |
| }) : this.variablesInScope = variablesInScope ?? _parametersInScope(context), |
| this.isSpreadOrControlFlowEnabled = _isSpreadOrControlFlowEnabled( |
| (resynthesizer.library.context.analysisOptions |
| as AnalysisOptionsImpl) |
| .experimentStatus); |
| |
| bool get hasNonEmptyExpr => _uc != null && _uc.operations.isNotEmpty; |
| |
| Expression build() { |
| if (requireValidConst && !_uc.isValidConst) { |
| return null; |
| } |
| int startingVariableCount = variablesInScope.count; |
| for (UnlinkedExprOperation operation in _uc.operations) { |
| switch (operation) { |
| case UnlinkedExprOperation.pushNull: |
| _push(AstTestFactory.nullLiteral()); |
| break; |
| // bool |
| case UnlinkedExprOperation.pushFalse: |
| _push(AstTestFactory.booleanLiteral(false)); |
| break; |
| case UnlinkedExprOperation.pushTrue: |
| _push(AstTestFactory.booleanLiteral(true)); |
| break; |
| // literals |
| case UnlinkedExprOperation.pushInt: |
| int value = _uc.ints[intPtr++]; |
| _push(AstTestFactory.integer(value)); |
| break; |
| case UnlinkedExprOperation.pushLongInt: |
| int value = 0; |
| int count = _uc.ints[intPtr++]; |
| for (int i = 0; i < count; i++) { |
| int next = _uc.ints[intPtr++]; |
| value = value << 32 | next; |
| } |
| _push(AstTestFactory.integer(value)); |
| break; |
| case UnlinkedExprOperation.pushDouble: |
| double value = _uc.doubles[doublePtr++]; |
| _push(AstTestFactory.doubleLiteral(value)); |
| break; |
| case UnlinkedExprOperation.makeSymbol: |
| String component = _uc.strings[stringPtr++]; |
| _push(AstTestFactory.symbolLiteral([component])); |
| break; |
| // String |
| case UnlinkedExprOperation.pushString: |
| String value = _uc.strings[stringPtr++]; |
| _push(AstTestFactory.string2(value)); |
| break; |
| case UnlinkedExprOperation.concatenate: |
| int count = _uc.ints[intPtr++]; |
| List<InterpolationElement> elements = <InterpolationElement>[]; |
| for (int i = 0; i < count; i++) { |
| Expression expr = _pop(); |
| InterpolationElement element = _newInterpolationElement(expr); |
| elements.insert(0, element); |
| } |
| _push(AstTestFactory.string(elements)); |
| break; |
| // binary |
| case UnlinkedExprOperation.equal: |
| _pushBinary(TokenType.EQ_EQ); |
| break; |
| case UnlinkedExprOperation.notEqual: |
| _pushBinary(TokenType.BANG_EQ); |
| break; |
| case UnlinkedExprOperation.and: |
| _pushBinary(TokenType.AMPERSAND_AMPERSAND); |
| break; |
| case UnlinkedExprOperation.or: |
| _pushBinary(TokenType.BAR_BAR); |
| break; |
| case UnlinkedExprOperation.bitXor: |
| _pushBinary(TokenType.CARET); |
| break; |
| case UnlinkedExprOperation.bitAnd: |
| _pushBinary(TokenType.AMPERSAND); |
| break; |
| case UnlinkedExprOperation.bitOr: |
| _pushBinary(TokenType.BAR); |
| break; |
| case UnlinkedExprOperation.bitShiftLeft: |
| _pushBinary(TokenType.LT_LT); |
| break; |
| case UnlinkedExprOperation.bitShiftRight: |
| _pushBinary(TokenType.GT_GT); |
| break; |
| case UnlinkedExprOperation.bitShiftRightLogical: |
| _pushBinary(TokenType.GT_GT_GT); |
| break; |
| case UnlinkedExprOperation.add: |
| _pushBinary(TokenType.PLUS); |
| break; |
| case UnlinkedExprOperation.subtract: |
| _pushBinary(TokenType.MINUS); |
| break; |
| case UnlinkedExprOperation.multiply: |
| _pushBinary(TokenType.STAR); |
| break; |
| case UnlinkedExprOperation.divide: |
| _pushBinary(TokenType.SLASH); |
| break; |
| case UnlinkedExprOperation.floorDivide: |
| _pushBinary(TokenType.TILDE_SLASH); |
| break; |
| case UnlinkedExprOperation.modulo: |
| _pushBinary(TokenType.PERCENT); |
| break; |
| case UnlinkedExprOperation.greater: |
| _pushBinary(TokenType.GT); |
| break; |
| case UnlinkedExprOperation.greaterEqual: |
| _pushBinary(TokenType.GT_EQ); |
| break; |
| case UnlinkedExprOperation.less: |
| _pushBinary(TokenType.LT); |
| break; |
| case UnlinkedExprOperation.lessEqual: |
| _pushBinary(TokenType.LT_EQ); |
| break; |
| // prefix |
| case UnlinkedExprOperation.complement: |
| _pushPrefix(TokenType.TILDE); |
| break; |
| case UnlinkedExprOperation.negate: |
| _pushPrefix(TokenType.MINUS); |
| break; |
| case UnlinkedExprOperation.not: |
| _pushPrefix(TokenType.BANG); |
| break; |
| // conditional |
| case UnlinkedExprOperation.conditional: |
| Expression elseExpr = _pop(); |
| Expression thenExpr = _pop(); |
| Expression condition = _pop(); |
| _push(AstTestFactory.conditionalExpression( |
| condition, thenExpr, elseExpr)); |
| break; |
| case UnlinkedExprOperation.invokeMethodRef: |
| _pushInvokeMethodRef(); |
| break; |
| case UnlinkedExprOperation.invokeMethod: |
| List<Expression> arguments = _buildArguments(); |
| TypeArgumentList typeArguments = _buildTypeArguments(); |
| Expression target = _pop(); |
| String name = _uc.strings[stringPtr++]; |
| _push(AstTestFactory.methodInvocation3( |
| target, name, typeArguments, arguments)); |
| break; |
| // containers |
| case UnlinkedExprOperation.makeUntypedList: |
| _pushList(null); |
| break; |
| case UnlinkedExprOperation.makeTypedList: |
| TypeAnnotation itemType = _newTypeName(); |
| _pushList( |
| AstTestFactory.typeArgumentList(<TypeAnnotation>[itemType])); |
| break; |
| case UnlinkedExprOperation.makeUntypedSetOrMap: |
| _pushSetOrMap(null); |
| break; |
| case UnlinkedExprOperation.makeUntypedMap: |
| _pushMap(null); |
| break; |
| case UnlinkedExprOperation.makeTypedMap: |
| TypeAnnotation keyType = _newTypeName(); |
| TypeAnnotation valueType = _newTypeName(); |
| _pushMap(AstTestFactory.typeArgumentList( |
| <TypeAnnotation>[keyType, valueType])); |
| break; |
| case UnlinkedExprOperation.makeMapLiteralEntry: |
| _pushMapLiteralEntry(); |
| break; |
| case UnlinkedExprOperation.makeTypedMap2: |
| TypeAnnotation keyType = _newTypeName(); |
| TypeAnnotation valueType = _newTypeName(); |
| _pushSetOrMap(AstTestFactory.typeArgumentList( |
| <TypeAnnotation>[keyType, valueType])); |
| break; |
| case UnlinkedExprOperation.makeUntypedSet: |
| _pushSet(null); |
| break; |
| case UnlinkedExprOperation.makeTypedSet: |
| TypeAnnotation itemType = _newTypeName(); |
| if (isSpreadOrControlFlowEnabled) { |
| _pushSetOrMap( |
| AstTestFactory.typeArgumentList(<TypeAnnotation>[itemType])); |
| } else { |
| _pushSet( |
| AstTestFactory.typeArgumentList(<TypeAnnotation>[itemType])); |
| } |
| break; |
| case UnlinkedExprOperation.pushReference: |
| _pushReference(); |
| break; |
| case UnlinkedExprOperation.extractProperty: |
| _pushExtractProperty(); |
| break; |
| case UnlinkedExprOperation.invokeConstructor: |
| _pushInstanceCreation(); |
| break; |
| case UnlinkedExprOperation.pushParameter: |
| String name = _uc.strings[stringPtr++]; |
| SimpleIdentifier identifier = AstTestFactory.identifier3(name); |
| identifier.staticElement = variablesInScope[name]; |
| _push(identifier); |
| break; |
| case UnlinkedExprOperation.ifNull: |
| _pushBinary(TokenType.QUESTION_QUESTION); |
| break; |
| case UnlinkedExprOperation.await: |
| Expression expression = _pop(); |
| _push(AstTestFactory.awaitExpression(expression)); |
| break; |
| case UnlinkedExprOperation.pushLocalFunctionReference: |
| _pushLocalFunctionReference(); |
| break; |
| case UnlinkedExprOperation.assignToRef: |
| var ref = _createReference(); |
| _push(_createAssignment(ref)); |
| break; |
| case UnlinkedExprOperation.typeCast: |
| Expression expression = _pop(); |
| TypeAnnotation type = _newTypeName(); |
| _push(AstTestFactory.asExpression(expression, type)); |
| break; |
| case UnlinkedExprOperation.typeCheck: |
| Expression expression = _pop(); |
| TypeAnnotation type = _newTypeName(); |
| _push(AstTestFactory.isExpression(expression, false, type)); |
| break; |
| case UnlinkedExprOperation.throwException: |
| Expression expression = _pop(); |
| _push(AstTestFactory.throwExpression2(expression)); |
| break; |
| case UnlinkedExprOperation.assignToProperty: |
| Expression target = _pop(); |
| String name = _uc.strings[stringPtr++]; |
| SimpleIdentifier propertyNode = AstTestFactory.identifier3(name); |
| PropertyAccess propertyAccess = |
| AstTestFactory.propertyAccess(target, propertyNode); |
| _push(_createAssignment(propertyAccess)); |
| break; |
| case UnlinkedExprOperation.assignToIndex: |
| Expression index = _pop(); |
| Expression target = _pop(); |
| IndexExpression indexExpression = |
| AstTestFactory.indexExpression(target, index); |
| _push(_createAssignment(indexExpression)); |
| break; |
| case UnlinkedExprOperation.extractIndex: |
| Expression index = _pop(); |
| Expression target = _pop(); |
| _push(AstTestFactory.indexExpression(target, index)); |
| break; |
| case UnlinkedExprOperation.pushSuper: |
| _push(AstTestFactory.superExpression()); |
| break; |
| case UnlinkedExprOperation.pushThis: |
| _push(AstTestFactory.thisExpression()); |
| break; |
| case UnlinkedExprOperation.spreadElement: |
| _pushSpread(TokenType.PERIOD_PERIOD_PERIOD); |
| break; |
| case UnlinkedExprOperation.nullAwareSpreadElement: |
| _pushSpread(TokenType.PERIOD_PERIOD_PERIOD_QUESTION); |
| break; |
| case UnlinkedExprOperation.ifElement: |
| _pushIfElement(false); |
| break; |
| case UnlinkedExprOperation.ifElseElement: |
| _pushIfElement(true); |
| break; |
| case UnlinkedExprOperation.forParts: |
| _pushForParts(); |
| break; |
| case UnlinkedExprOperation.forElement: |
| _pushForElement(false); |
| break; |
| case UnlinkedExprOperation.forElementWithAwait: |
| _pushForElement(true); |
| break; |
| case UnlinkedExprOperation.pushEmptyExpression: |
| _push(null); |
| break; |
| case UnlinkedExprOperation.variableDeclarationStart: |
| _variableDeclarationStart(); |
| break; |
| case UnlinkedExprOperation.variableDeclaration: |
| _variableDeclaration(); |
| break; |
| case UnlinkedExprOperation.forInitializerDeclarationsUntyped: |
| _forInitializerDeclarations(false); |
| break; |
| case UnlinkedExprOperation.forInitializerDeclarationsTyped: |
| _forInitializerDeclarations(true); |
| break; |
| case UnlinkedExprOperation.assignToParameter: |
| String name = _uc.strings[stringPtr++]; |
| SimpleIdentifier identifier = AstTestFactory.identifier3(name); |
| identifier.staticElement = variablesInScope[name]; |
| _push(_createAssignment(identifier)); |
| break; |
| case UnlinkedExprOperation.forEachPartsWithIdentifier: |
| _forEachPartsWithIdentifier(); |
| break; |
| case UnlinkedExprOperation.forEachPartsWithUntypedDeclaration: |
| _forEachPartsWithDeclaration(false); |
| break; |
| case UnlinkedExprOperation.forEachPartsWithTypedDeclaration: |
| _forEachPartsWithDeclaration(true); |
| break; |
| case UnlinkedExprOperation.cascadeSectionBegin: |
| case UnlinkedExprOperation.cascadeSectionEnd: |
| case UnlinkedExprOperation.pushLocalFunctionReference: |
| case UnlinkedExprOperation.pushError: |
| case UnlinkedExprOperation.pushTypedAbstract: |
| case UnlinkedExprOperation.pushUntypedAbstract: |
| throw new UnimplementedError( |
| 'Unexpected $operation in a constant expression.'); |
| } |
| } |
| assert(startingVariableCount == variablesInScope.count); |
| return stack.single; |
| } |
| |
| List<Expression> _buildArguments() { |
| List<Expression> arguments; |
| { |
| int numNamedArgs = _uc.ints[intPtr++]; |
| int numPositionalArgs = _uc.ints[intPtr++]; |
| int numArgs = numNamedArgs + numPositionalArgs; |
| arguments = _removeTopExpressions(numArgs); |
| // add names to the named arguments |
| for (int i = 0; i < numNamedArgs; i++) { |
| String name = _uc.strings[stringPtr++]; |
| int index = numPositionalArgs + i; |
| arguments[index] = |
| AstTestFactory.namedExpression2(name, arguments[index]); |
| } |
| } |
| return arguments; |
| } |
| |
| /// Given the sequence of identifiers in [node], and the [classElement] or |
| /// [constructorElement] to which this sequence resolves, build the |
| /// [InstanceCreationExpression]. |
| InstanceCreationExpression _buildCreation( |
| Expression node, |
| TypeArgumentList typeArguments, |
| ArgumentList argumentList, { |
| ClassElement classElement, |
| ConstructorElement constructorElement, |
| }) { |
| InstanceCreationExpression composeCreation( |
| DartType type, |
| ConstructorName constructorName, |
| ConstructorElement constructorElement, |
| ArgumentList argumentList) { |
| TypeName typeName = constructorName.type; |
| typeName.type = type; |
| |
| constructorName.name?.staticElement = constructorElement; |
| |
| var creation = astFactory.instanceCreationExpression( |
| _uc.isValidConst |
| ? TokenFactory.tokenFromKeyword(Keyword.CONST) |
| : TokenFactory.tokenFromKeyword(Keyword.NEW), |
| constructorName, |
| argumentList); |
| |
| creation.staticElement = constructorElement; |
| creation.staticType = type; |
| return creation; |
| } |
| |
| classElement ??= constructorElement?.enclosingElement; |
| var type = AstRewriteVisitor.getType( |
| resynthesizer.typeSystem, classElement, typeArguments); |
| |
| // C() |
| if (node is SimpleIdentifier && classElement != null) { |
| constructorElement = type.lookUpConstructor('', resynthesizer.library); |
| node.staticType = type; |
| return composeCreation( |
| type, |
| astFactory.constructorName( |
| astFactory.typeName(node, typeArguments), |
| null, |
| null, |
| ), |
| constructorElement, |
| argumentList, |
| ); |
| } |
| if (node is PrefixedIdentifier) { |
| // C.n() |
| if (constructorElement != null) { |
| constructorElement = |
| type.lookUpConstructor(node.identifier.name, resynthesizer.library); |
| node.prefix.staticType = type; |
| return composeCreation( |
| type, |
| astFactory.constructorName( |
| astFactory.typeName(node.prefix, typeArguments), |
| TokenFactory.tokenFromType(TokenType.PERIOD), |
| node.identifier, |
| ), |
| constructorElement, |
| argumentList, |
| ); |
| } |
| // p.C() |
| if (classElement != null) { |
| constructorElement = type.lookUpConstructor('', resynthesizer.library); |
| node.identifier.staticType = type; |
| return composeCreation( |
| type, |
| astFactory.constructorName( |
| astFactory.typeName(node.identifier, typeArguments), |
| null, |
| null, |
| ), |
| constructorElement, |
| argumentList, |
| ); |
| } |
| } |
| // p.C.n() |
| if (node is PropertyAccess && constructorElement != null) { |
| constructorElement = |
| type.lookUpConstructor(node.propertyName.name, resynthesizer.library); |
| var typeIdentifier = (node.target as PrefixedIdentifier).identifier; |
| typeIdentifier.staticType = type; |
| return composeCreation( |
| type, |
| astFactory.constructorName( |
| astFactory.typeName(typeIdentifier, typeArguments), |
| TokenFactory.tokenFromType(TokenType.PERIOD), |
| node.propertyName, |
| ), |
| constructorElement, |
| argumentList, |
| ); |
| } |
| |
| throw new UnimplementedError('For ${node?.runtimeType}: $node; ' |
| 'class: $classElement; constructor: $constructorElement'); |
| } |
| |
| /** |
| * Build the identifier sequence (a single or prefixed identifier, or a |
| * property access) corresponding to the given reference [info]. |
| */ |
| Expression _buildIdentifierSequence(ReferenceInfo info) { |
| Expression enclosing; |
| if (info.enclosing != null) { |
| enclosing = _buildIdentifierSequence(info.enclosing); |
| } |
| Element element = info.element; |
| if (element == null && info.name == 'length') { |
| element = _getStringLengthElement(); |
| } |
| if (enclosing == null) { |
| return AstTestFactory.identifier3(info.name)..staticElement = element; |
| } |
| if (enclosing is SimpleIdentifier) { |
| SimpleIdentifier identifier = AstTestFactory.identifier3(info.name) |
| ..staticElement = element; |
| return AstTestFactory.identifier(enclosing, identifier); |
| } |
| SimpleIdentifier property = AstTestFactory.identifier3(info.name) |
| ..staticElement = element; |
| return AstTestFactory.propertyAccess(enclosing, property); |
| } |
| |
| TypeArgumentList _buildTypeArguments() { |
| int numTypeArguments = _uc.ints[intPtr++]; |
| if (numTypeArguments == 0) { |
| return null; |
| } |
| |
| var typeNames = new List<TypeAnnotation>(numTypeArguments); |
| for (int i = 0; i < numTypeArguments; i++) { |
| typeNames[i] = _newTypeName(); |
| } |
| return AstTestFactory.typeArgumentList(typeNames); |
| } |
| |
| TypeAnnotation _buildTypeAst(DartType type) { |
| List<TypeAnnotation> argumentNodes; |
| if (type is ParameterizedType) { |
| if (!resynthesizer.doesTypeHaveImplicitArguments(type)) { |
| List<DartType> typeArguments = type.typeArguments; |
| argumentNodes = typeArguments.every((a) => a.isDynamic) |
| ? null |
| : typeArguments.map(_buildTypeAst).toList(); |
| } |
| } |
| TypeName node = AstTestFactory.typeName4(type.name, argumentNodes); |
| node.type = type; |
| (node.name as SimpleIdentifier).staticElement = type.element; |
| return node; |
| } |
| |
| Expression _createAssignment(Expression lhs) { |
| Expression binary(TokenType tokenType) { |
| return AstTestFactory.assignmentExpression(lhs, tokenType, _pop()); |
| } |
| |
| Expression prefix(TokenType tokenType) { |
| return AstTestFactory.prefixExpression(tokenType, lhs); |
| } |
| |
| Expression postfix(TokenType tokenType) { |
| return AstTestFactory.postfixExpression(lhs, tokenType); |
| } |
| |
| switch (_uc.assignmentOperators[assignmentOperatorPtr++]) { |
| case UnlinkedExprAssignOperator.assign: |
| return binary(TokenType.EQ); |
| case UnlinkedExprAssignOperator.ifNull: |
| return binary(TokenType.QUESTION_QUESTION_EQ); |
| case UnlinkedExprAssignOperator.multiply: |
| return binary(TokenType.STAR_EQ); |
| case UnlinkedExprAssignOperator.divide: |
| return binary(TokenType.SLASH_EQ); |
| case UnlinkedExprAssignOperator.floorDivide: |
| return binary(TokenType.TILDE_SLASH_EQ); |
| case UnlinkedExprAssignOperator.modulo: |
| return binary(TokenType.PERCENT_EQ); |
| case UnlinkedExprAssignOperator.plus: |
| return binary(TokenType.PLUS_EQ); |
| case UnlinkedExprAssignOperator.minus: |
| return binary(TokenType.MINUS_EQ); |
| case UnlinkedExprAssignOperator.shiftLeft: |
| return binary(TokenType.LT_LT_EQ); |
| case UnlinkedExprAssignOperator.shiftRight: |
| return binary(TokenType.GT_GT_EQ); |
| case UnlinkedExprAssignOperator.bitAnd: |
| return binary(TokenType.AMPERSAND_EQ); |
| case UnlinkedExprAssignOperator.bitXor: |
| return binary(TokenType.CARET_EQ); |
| case UnlinkedExprAssignOperator.bitOr: |
| return binary(TokenType.BAR_EQ); |
| case UnlinkedExprAssignOperator.prefixIncrement: |
| return prefix(TokenType.PLUS_PLUS); |
| case UnlinkedExprAssignOperator.prefixDecrement: |
| return prefix(TokenType.MINUS_MINUS); |
| case UnlinkedExprAssignOperator.postfixIncrement: |
| return postfix(TokenType.PLUS_PLUS); |
| case UnlinkedExprAssignOperator.postfixDecrement: |
| return postfix(TokenType.MINUS_MINUS); |
| default: |
| throw new UnimplementedError('Unexpected UnlinkedExprAssignOperator'); |
| } |
| } |
| |
| Expression _createReference() { |
| EntityRef ref = _uc.references[refPtr++]; |
| if (ref.paramReference != 0) { |
| // This is a reference to a type parameter. For type inference purposes |
| // we don't actually need to know which type parameter it's a reference |
| // to; we just need to know that it represents a type. So map it to |
| // `Object`. |
| return AstTestFactory.identifier3('Object') |
| ..staticElement = resynthesizer.typeProvider.objectType.element; |
| } |
| ReferenceInfo info = resynthesizer.getReferenceInfo(ref.reference); |
| return _buildIdentifierSequence(info); |
| } |
| |
| void _forEachPartsWithDeclaration(bool hasType) { |
| var iterable = _pop(); |
| var name = _uc.strings[stringPtr++]; |
| var element = LocalVariableElementImpl(name, -1); |
| var keyword = hasType ? null : Keyword.VAR; |
| var type = hasType ? _newTypeName() : null; |
| var loopVariable = AstTestFactory.declaredIdentifier2(keyword, type, name); |
| loopVariable.identifier.staticElement = element; |
| if (hasType) { |
| element.type = type.type; |
| } |
| _pushNode( |
| AstTestFactory.forEachPartsWithDeclaration(loopVariable, iterable)); |
| variablesInScope.push(element); |
| } |
| |
| void _forEachPartsWithIdentifier() { |
| var iterable = _pop(); |
| SimpleIdentifier identifier = _pop(); |
| _pushNode(AstTestFactory.forEachPartsWithIdentifier(identifier, iterable)); |
| } |
| |
| void _forInitializerDeclarations(bool hasType) { |
| var count = _uc.ints[intPtr++]; |
| var variables = List<VariableDeclaration>.filled(count, null); |
| for (int i = 0; i < count; i++) { |
| variables[count - 1 - i] = _popNode(); |
| } |
| var type = hasType ? _newTypeName() : null; |
| var keyword = hasType ? null : Keyword.VAR; |
| _pushNode(AstTestFactory.variableDeclarationList(keyword, type, variables)); |
| } |
| |
| PropertyAccessorElement _getStringLengthElement() => |
| resynthesizer.typeProvider.stringType.getGetter('length'); |
| |
| FormalParameter _makeParameter(ParameterElement param) { |
| SimpleFormalParameterImpl simpleParam = |
| AstTestFactory.simpleFormalParameter(null, param.name); |
| simpleParam.identifier.staticElement = param; |
| simpleParam.declaredElement = param; |
| var unlinkedParam = (param as ParameterElementImpl).unlinkedParam; |
| if (unlinkedParam.kind == UnlinkedParamKind.positional) { |
| return AstTestFactory.positionalFormalParameter(simpleParam, null); |
| } else if (unlinkedParam.kind == UnlinkedParamKind.named) { |
| return AstTestFactory.namedFormalParameter(simpleParam, null); |
| } else { |
| return simpleParam; |
| } |
| } |
| |
| InterpolationElement _newInterpolationElement(Expression expr) { |
| if (expr is SimpleStringLiteral) { |
| return astFactory.interpolationString(expr.literal, expr.value); |
| } else { |
| return astFactory.interpolationExpression( |
| TokenFactory.tokenFromType(TokenType.STRING_INTERPOLATION_EXPRESSION), |
| expr, |
| TokenFactory.tokenFromType(TokenType.CLOSE_CURLY_BRACKET)); |
| } |
| } |
| |
| /** |
| * Convert the next reference to the [DartType] and return the AST |
| * corresponding to this type. |
| */ |
| TypeAnnotation _newTypeName() { |
| EntityRef typeRef = _uc.references[refPtr++]; |
| DartType type = resynthesizer.buildType(context, typeRef); |
| return _buildTypeAst(type); |
| } |
| |
| Expression _pop() => stack.removeLast() as Expression; |
| |
| CollectionElement _popCollectionElement() => |
| stack.removeLast() as CollectionElement; |
| |
| AstNode _popNode() => stack.removeLast(); |
| |
| void _push(Expression expr) { |
| stack.add(expr); |
| } |
| |
| void _pushBinary(TokenType operator) { |
| Expression right = _pop(); |
| Expression left = _pop(); |
| _push(AstTestFactory.binaryExpression(left, operator, right)); |
| } |
| |
| void _pushCollectionElement(CollectionElement collectionElement) { |
| stack.add(collectionElement); |
| } |
| |
| void _pushExtractProperty() { |
| Expression target = _pop(); |
| String name = _uc.strings[stringPtr++]; |
| SimpleIdentifier propertyNode = AstTestFactory.identifier3(name); |
| // Only String.length property access can be potentially resolved. |
| if (name == 'length') { |
| propertyNode.staticElement = _getStringLengthElement(); |
| } |
| _push(AstTestFactory.propertyAccess(target, propertyNode)); |
| } |
| |
| void _pushForElement(bool hasAwait) { |
| var body = _popCollectionElement(); |
| var forLoopParts = _popNode() as ForLoopParts; |
| if (forLoopParts is ForPartsWithDeclarations) { |
| variablesInScope.pop(forLoopParts.variables.variables.length); |
| } else if (forLoopParts is ForEachPartsWithDeclaration) { |
| variablesInScope.pop(1); |
| } |
| _pushCollectionElement( |
| AstTestFactory.forElement(forLoopParts, body, hasAwait: hasAwait)); |
| } |
| |
| void _pushForParts() { |
| var updaterCount = _uc.ints[intPtr++]; |
| var updaters = <Expression>[]; |
| for (int i = 0; i < updaterCount; i++) { |
| updaters.insert(0, _pop()); |
| } |
| Expression condition = _pop(); |
| AstNode initialization = _popNode(); |
| if (initialization is Expression || initialization == null) { |
| _pushNode(AstTestFactory.forPartsWithExpression( |
| initialization, condition, updaters)); |
| } else if (initialization is VariableDeclarationList) { |
| _pushNode(AstTestFactory.forPartsWithDeclarations( |
| initialization, condition, updaters)); |
| } else { |
| throw StateError('Unrecognized for parts'); |
| } |
| } |
| |
| void _pushIfElement(bool hasElse) { |
| CollectionElement elseElement = hasElse ? _popCollectionElement() : null; |
| CollectionElement thenElement = _popCollectionElement(); |
| Expression condition = _pop(); |
| _pushCollectionElement( |
| AstTestFactory.ifElement(condition, thenElement, elseElement)); |
| } |
| |
| void _pushInstanceCreation() { |
| EntityRef ref = _uc.references[refPtr++]; |
| ReferenceInfo info = resynthesizer.getReferenceInfo(ref.reference); |
| // prepare ConstructorElement |
| TypeName typeNode; |
| String constructorName; |
| ConstructorElement constructorElement; |
| if (info.element != null) { |
| if (info.element is ConstructorElement) { |
| constructorName = info.name; |
| } else if (info.element is ClassElement) { |
| constructorName = null; |
| } else { |
| // Unexpected element, consider it unresolved. |
| _buildArguments(); |
| var identifier = AstTestFactory.identifier3('__unresolved__') |
| ..staticType = resynthesizer.typeProvider.dynamicType; |
| _push(identifier); |
| return; |
| } |
| InterfaceType definingType = resynthesizer.createConstructorDefiningType( |
| context, info, ref.typeArguments); |
| constructorElement = |
| resynthesizer.getConstructorForInfo(definingType, info); |
| typeNode = _buildTypeAst(definingType); |
| } else { |
| if (info.enclosing != null) { |
| if (info.enclosing.enclosing != null) { |
| PrefixedIdentifier typeName = AstTestFactory.identifier5( |
| info.enclosing.enclosing.name, info.enclosing.name); |
| typeName.prefix.staticElement = info.enclosing.enclosing.element; |
| typeName.identifier.staticElement = info.enclosing.element; |
| typeName.identifier.staticType = info.enclosing.type; |
| typeNode = AstTestFactory.typeName3(typeName); |
| typeNode.type = info.enclosing.type; |
| constructorName = info.name; |
| } else if (info.enclosing.element != null) { |
| SimpleIdentifier typeName = |
| AstTestFactory.identifier3(info.enclosing.name); |
| typeName.staticElement = info.enclosing.element; |
| typeName.staticType = info.enclosing.type; |
| typeNode = AstTestFactory.typeName3(typeName); |
| typeNode.type = info.enclosing.type; |
| constructorName = info.name; |
| } else { |
| typeNode = AstTestFactory.typeName3( |
| AstTestFactory.identifier5(info.enclosing.name, info.name)); |
| constructorName = null; |
| } |
| } else { |
| typeNode = AstTestFactory.typeName4(info.name); |
| } |
| } |
| // prepare arguments |
| List<Expression> arguments = _buildArguments(); |
| // create ConstructorName |
| ConstructorName constructorNode; |
| if (constructorName != null) { |
| constructorNode = |
| AstTestFactory.constructorName(typeNode, constructorName); |
| constructorNode.name.staticElement = constructorElement; |
| } else { |
| constructorNode = AstTestFactory.constructorName(typeNode, null); |
| } |
| constructorNode.staticElement = constructorElement; |
| // create InstanceCreationExpression |
| InstanceCreationExpression instanceCreation = |
| AstTestFactory.instanceCreationExpression( |
| requireValidConst ? Keyword.CONST : Keyword.NEW, |
| constructorNode, |
| arguments); |
| instanceCreation.staticElement = constructorElement; |
| _push(instanceCreation); |
| } |
| |
| void _pushInvokeMethodRef() { |
| List<Expression> arguments = _buildArguments(); |
| EntityRef ref = _uc.references[refPtr++]; |
| ReferenceInfo info = resynthesizer.getReferenceInfo(ref.reference); |
| Expression node = _buildIdentifierSequence(info); |
| TypeArgumentList typeArguments = _buildTypeArguments(); |
| var period = TokenFactory.tokenFromType(TokenType.PERIOD); |
| var argumentList = AstTestFactory.argumentList(arguments); |
| |
| // Check for optional new/const. |
| if (info.element is ClassElement) { |
| _push(_buildCreation(node, typeArguments, argumentList, |
| classElement: info.element)); |
| return; |
| } |
| if (info.element is ConstructorElement) { |
| _push(_buildCreation(node, typeArguments, argumentList, |
| constructorElement: info.element)); |
| return; |
| } |
| |
| if (node is SimpleIdentifier) { |
| _push(astFactory.methodInvocation( |
| null, period, node, typeArguments, argumentList)); |
| } else if (node is PropertyAccess) { |
| _push(astFactory.methodInvocation( |
| node.target, period, node.propertyName, typeArguments, argumentList)); |
| } else if (node is PrefixedIdentifier) { |
| _push(astFactory.methodInvocation( |
| node.prefix, period, node.identifier, typeArguments, argumentList)); |
| } else { |
| throw new UnimplementedError('For ${node?.runtimeType}: $node'); |
| } |
| } |
| |
| void _pushList(TypeArgumentList typeArguments) { |
| int count = _uc.ints[intPtr++]; |
| List<CollectionElement> elements = |
| isSpreadOrControlFlowEnabled ? <CollectionElement>[] : <Expression>[]; |
| for (int i = 0; i < count; i++) { |
| elements.insert(0, _popCollectionElement()); |
| } |
| var typeArg = typeArguments == null |
| ? resynthesizer.typeProvider.dynamicType |
| : typeArguments.arguments[0].type; |
| var staticType = resynthesizer.typeProvider.listType.instantiate([typeArg]); |
| _push(AstTestFactory.listLiteral2(Keyword.CONST, typeArguments, elements) |
| ..staticType = staticType); |
| } |
| |
| void _pushLocalFunctionReference() { |
| int popCount = _uc.ints[intPtr++]; |
| // Note: nonzero popCount is no longer used. |
| assert(popCount == 0); |
| int functionIndex = _uc.ints[intPtr++]; |
| var localFunction = localFunctions[functionIndex]; |
| var functionElement = |
| new FunctionElementImpl.forSerialized(localFunction, context); |
| for (ParameterElementImpl parameter in functionElement.parameters) { |
| variablesInScope.push(parameter); |
| if (parameter.unlinkedParam.type == null) { |
| // Store a type of `dynamic` for the parameter; this prevents |
| // resynthesis from trying to read a type out of the summary (which |
| // wouldn't work anyway, since nested functions don't have their |
| // parameter types stored in the summary anyhow). |
| parameter.type = resynthesizer.typeProvider.dynamicType; |
| } |
| } |
| var parameters = functionElement.parameters.map(_makeParameter).toList(); |
| var asyncKeyword = localFunction.isAsynchronous |
| ? TokenFactory.tokenFromKeyword(Keyword.ASYNC) |
| : null; |
| FunctionBody functionBody; |
| if (localFunction.bodyExpr == null) { |
| // Most likely the original source code contained a block function body |
| // here. Block function bodies aren't supported by the summary mechanism. |
| // But they are tolerated when their presence doesn't affect inferred |
| // types. |
| functionBody = AstTestFactory.blockFunctionBody(AstTestFactory.block()); |
| } else { |
| var bodyExpr = new ExprBuilder( |
| resynthesizer, functionElement, localFunction.bodyExpr, |
| requireValidConst: requireValidConst, |
| variablesInScope: variablesInScope, |
| localFunctions: localFunction.localFunctions) |
| .build(); |
| functionBody = astFactory.expressionFunctionBody(asyncKeyword, |
| TokenFactory.tokenFromType(TokenType.FUNCTION), bodyExpr, null); |
| } |
| variablesInScope.pop(functionElement.parameters.length); |
| FunctionExpressionImpl functionExpression = astFactory.functionExpression( |
| null, AstTestFactory.formalParameterList(parameters), functionBody); |
| functionExpression.declaredElement = functionElement; |
| _push(functionExpression); |
| } |
| |
| void _pushMap(TypeArgumentList typeArguments) { |
| int count = _uc.ints[intPtr++]; |
| List<MapLiteralEntry> entries = <MapLiteralEntry>[]; |
| for (int i = 0; i < count; i++) { |
| Expression value = _pop(); |
| Expression key = _pop(); |
| entries.insert(0, AstTestFactory.mapLiteralEntry2(key, value)); |
| } |
| var keyType = typeArguments == null |
| ? resynthesizer.typeProvider.dynamicType |
| : typeArguments.arguments[0].type; |
| var valueType = typeArguments == null |
| ? resynthesizer.typeProvider.dynamicType |
| : typeArguments.arguments[1].type; |
| var staticType = |
| resynthesizer.typeProvider.mapType.instantiate([keyType, valueType]); |
| SetOrMapLiteralImpl literal = |
| AstTestFactory.setOrMapLiteral(Keyword.CONST, typeArguments, entries); |
| literal.becomeMap(); |
| literal.staticType = staticType; |
| _push(literal); |
| } |
| |
| void _pushMapLiteralEntry() { |
| Expression value = _pop(); |
| Expression key = _pop(); |
| _pushCollectionElement(AstTestFactory.mapLiteralEntry2(key, value)); |
| } |
| |
| void _pushNode(AstNode node) { |
| stack.add(node); |
| } |
| |
| void _pushPrefix(TokenType operator) { |
| Expression operand = _pop(); |
| _push(AstTestFactory.prefixExpression(operator, operand)); |
| } |
| |
| void _pushReference() { |
| _push(_createReference()); |
| } |
| |
| void _pushSet(TypeArgumentList typeArguments) { |
| int count = _uc.ints[intPtr++]; |
| List<Expression> elements = <Expression>[]; |
| for (int i = 0; i < count; i++) { |
| elements.insert(0, _pop()); |
| } |
| SetOrMapLiteralImpl literal = |
| AstTestFactory.setOrMapLiteral(Keyword.CONST, typeArguments, elements); |
| literal.becomeSet(); |
| _push(literal); |
| } |
| |
| void _pushSetOrMap(TypeArgumentList typeArguments) { |
| int count = _uc.ints[intPtr++]; |
| List<CollectionElement> elements = <CollectionElement>[]; |
| for (int i = 0; i < count; i++) { |
| elements.insert(0, _popCollectionElement()); |
| } |
| |
| bool isMap = true; // assume Map unless can prove otherwise |
| DartType staticType; |
| if (typeArguments != null) { |
| if (typeArguments.arguments.length == 2) { |
| var keyType = typeArguments.arguments[0].type; |
| var valueType = typeArguments.arguments[1].type; |
| staticType = resynthesizer.typeProvider.mapType |
| .instantiate([keyType, valueType]); |
| } else if (typeArguments.arguments.length == 1) { |
| isMap = false; |
| var valueType = typeArguments == null |
| ? resynthesizer.typeProvider.dynamicType |
| : typeArguments.arguments[0].type; |
| staticType = |
| resynthesizer.typeProvider.setType.instantiate([valueType]); |
| } |
| } else { |
| for (var i = 0; i < elements.length; ++i) { |
| var element = elements[i]; |
| if (element is Expression) { |
| isMap = false; |
| } |
| } |
| } |
| |
| SetOrMapLiteral setOrMapLiteral = astFactory.setOrMapLiteral( |
| constKeyword: TokenFactory.tokenFromKeyword(Keyword.CONST), |
| typeArguments: typeArguments, |
| leftBracket: TokenFactory.tokenFromType(TokenType.OPEN_CURLY_BRACKET), |
| elements: elements, |
| rightBracket: TokenFactory.tokenFromType(TokenType.CLOSE_CURLY_BRACKET), |
| ); |
| if (becomeSetOrMap) { |
| if (isMap) { |
| (setOrMapLiteral as SetOrMapLiteralImpl).becomeMap(); |
| } else { |
| (setOrMapLiteral as SetOrMapLiteralImpl).becomeSet(); |
| } |
| } |
| _push(setOrMapLiteral..staticType = staticType); |
| } |
| |
| void _pushSpread(TokenType operator) { |
| Expression operand = _pop(); |
| _pushCollectionElement(AstTestFactory.spreadElement(operator, operand)); |
| } |
| |
| List<Expression> _removeTopExpressions(int count) { |
| int start = stack.length - count; |
| int end = stack.length; |
| List<Expression> items = List<Expression>.from(stack.getRange(start, end)); |
| stack.removeRange(start, end); |
| return items; |
| } |
| |
| void _variableDeclaration() { |
| var index = _uc.ints[intPtr++]; |
| var element = variablesInScope.recent(index); |
| var initializer = _pop(); |
| var variableDeclaration = |
| AstTestFactory.variableDeclaration2(element.name, initializer); |
| variableDeclaration.name.staticElement = element; |
| _pushNode(variableDeclaration); |
| } |
| |
| void _variableDeclarationStart() { |
| var name = _uc.strings[stringPtr++]; |
| variablesInScope.push(LocalVariableElementImpl(name, -1)); |
| } |
| |
| /// Figures out the default value of [parametersInScope] based on [context]. |
| /// |
| /// If [context] is (or contains) a constructor, then its parameters are used. |
| /// Otherwise, no parameters are considered to be in scope. |
| static _VariablesInScope _parametersInScope(Element context) { |
| var result = _VariablesInScope(); |
| for (Element e = context; e != null; e = e.enclosingElement) { |
| if (e is ConstructorElement) { |
| for (var parameter in e.parameters) { |
| result.push(parameter); |
| } |
| return result; |
| } |
| } |
| return result; |
| } |
| } |
| |
| /// Tracks the set of variables that are in scope while resynthesizing an |
| /// expression from a summary. |
| class _VariablesInScope { |
| final _variableElements = <VariableElement>[]; |
| |
| /// Returns the number of variables that have been pushed but not popped. |
| int get count => _variableElements.length; |
| |
| /// Looks up the variable with the given [name]. Returns `null` if no |
| /// variable is found. |
| VariableElement operator [](String name) { |
| for (int i = _variableElements.length - 1; i >= 0; i--) { |
| if (_variableElements[i].name == name) return _variableElements[i]; |
| } |
| return null; |
| } |
| |
| /// Un-does the effect of the last [count] calls to `push`. |
| void pop(int count) { |
| _variableElements.length -= count; |
| } |
| |
| /// Stores a new declaration based on the given [variableElement]. The |
| /// declaration shadows any previous declaration with the same name. |
| void push(VariableElement variableElement) { |
| _variableElements.add(variableElement); |
| } |
| |
| /// Retrieves the [index]th most recently pushed element (that hasn't been |
| /// popped). [index] counts from zero. |
| VariableElement recent(int index) => |
| _variableElements[_variableElements.length - 1 - index]; |
| } |