blob: bb2ccb2bdd6b83c3d8e4b6bcd414885bf1541052 [file] [log] [blame]
// 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 Map<String, ParameterElement> parametersInScope;
ExprBuilder(
this.resynthesizer,
this.context,
this._uc, {
this.requireValidConst: true,
this.localFunctions,
Map<String, ParameterElement> parametersInScope,
this.becomeSetOrMap: true,
}) : this.parametersInScope =
parametersInScope ?? _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;
}
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.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 = parametersInScope[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();
break;
case UnlinkedExprOperation.pushEmptyExpression:
_push(null);
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.');
}
}
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);
}
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() {
var body = _popCollectionElement();
var forLoopParts = _popNode() as ForLoopParts;
_pushCollectionElement(AstTestFactory.forElement(forLoopParts, body));
}
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 {
throw UnimplementedError('TODO(paulberry)');
}
}
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 parametersInScope =
new Map<String, ParameterElement>.from(this.parametersInScope);
var functionElement =
new FunctionElementImpl.forSerialized(localFunction, context);
for (ParameterElementImpl parameter in functionElement.parameters) {
parametersInScope[parameter.name] = 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,
parametersInScope: parametersInScope,
localFunctions: localFunction.localFunctions)
.build();
functionBody = astFactory.expressionFunctionBody(asyncKeyword,
TokenFactory.tokenFromType(TokenType.FUNCTION), bodyExpr, null);
}
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]);
if (isSpreadOrControlFlowEnabled) {
_push(
AstTestFactory.setOrMapLiteral(Keyword.CONST, typeArguments, entries)
..staticType = staticType);
} else {
// ignore: deprecated_member_use_from_same_package
_push(AstTestFactory.mapLiteral(Keyword.CONST, typeArguments, entries)
..staticType = staticType);
}
}
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());
}
// ignore: deprecated_member_use_from_same_package
_push(AstTestFactory.setLiteral(Keyword.CONST, typeArguments, elements));
}
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;
}
/// 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 Map<String, ParameterElement> _parametersInScope(Element context) {
var result = <String, ParameterElement>{};
for (Element e = context; e != null; e = e.enclosingElement) {
if (e is ConstructorElement) {
for (var parameter in e.parameters) {
result[parameter.name] = parameter;
}
return result;
}
}
return result;
}
}