blob: d5a37efb68f238a530e1f30dde31e3b0f419dd1d [file] [log] [blame]
// Copyright (c) 2019, 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 fasta.transform_collections;
import 'dart:core' hide MapEntry;
import 'package:kernel/ast.dart'
show
Arguments,
AsExpression,
Block,
BlockExpression,
Class,
ConditionalExpression,
DartType,
DynamicType,
Expression,
ExpressionStatement,
Field,
ForInStatement,
ForStatement,
IfStatement,
InterfaceType,
Let,
Library,
ListConcatenation,
ListLiteral,
MapConcatenation,
MapEntry,
MapLiteral,
MethodInvocation,
Name,
Not,
NullLiteral,
Procedure,
PropertyGet,
SetConcatenation,
SetLiteral,
Statement,
StaticInvocation,
transformList,
TreeNode,
VariableDeclaration,
VariableGet;
import 'package:kernel/core_types.dart' show CoreTypes;
import 'package:kernel/type_environment.dart'
show SubtypeCheckMode, TypeEnvironment;
import 'package:kernel/visitor.dart' show Transformer;
import 'collections.dart'
show
ControlFlowElement,
ControlFlowMapEntry,
ForElement,
ForInElement,
ForInMapEntry,
ForMapEntry,
IfElement,
IfMapEntry,
SpreadElement,
SpreadMapEntry;
import '../problems.dart' show getFileUri, unhandled;
import '../source/source_loader.dart';
import 'redirecting_factory_body.dart' show RedirectingFactoryBody;
class CollectionTransformer extends Transformer {
final CoreTypes coreTypes;
final TypeEnvironment typeEnvironment;
final Procedure listAdd;
final Procedure setFactory;
final Procedure setAdd;
final Procedure objectEquals;
final Procedure mapEntries;
final Procedure mapPut;
final Class mapEntryClass;
final Field mapEntryKey;
final Field mapEntryValue;
final SourceLoaderDataForTesting dataForTesting;
/// Library that contains the transformed nodes.
///
/// The transformation of the nodes is affected by the NNBD opt-in status of
/// the library.
Library _currentLibrary;
static Procedure _findSetFactory(CoreTypes coreTypes) {
Procedure factory = coreTypes.index.getMember('dart:core', 'Set', '');
RedirectingFactoryBody body = factory?.function?.body;
return body?.target;
}
CollectionTransformer(SourceLoader loader)
: coreTypes = loader.coreTypes,
typeEnvironment = loader.typeInferenceEngine.typeSchemaEnvironment,
listAdd = loader.coreTypes.index.getMember('dart:core', 'List', 'add'),
setFactory = _findSetFactory(loader.coreTypes),
setAdd = loader.coreTypes.index.getMember('dart:core', 'Set', 'add'),
objectEquals =
loader.coreTypes.index.getMember('dart:core', 'Object', '=='),
mapEntries =
loader.coreTypes.index.getMember('dart:core', 'Map', 'get:entries'),
mapPut = loader.coreTypes.index.getMember('dart:core', 'Map', '[]='),
mapEntryClass =
loader.coreTypes.index.getClass('dart:core', 'MapEntry'),
mapEntryKey =
loader.coreTypes.index.getMember('dart:core', 'MapEntry', 'key'),
mapEntryValue =
loader.coreTypes.index.getMember('dart:core', 'MapEntry', 'value'),
dataForTesting = loader.dataForTesting;
TreeNode _translateListOrSet(
Expression node, DartType elementType, List<Expression> elements,
{bool isSet: false}) {
// Translate elements in place up to the first non-expression, if any.
int i = 0;
for (; i < elements.length; ++i) {
if (elements[i] is ControlFlowElement) break;
elements[i] = elements[i].accept<TreeNode>(this)..parent = node;
}
// If there were only expressions, we are done.
if (i == elements.length) return node;
// Build a block expression and create an empty list or set.
VariableDeclaration result;
if (isSet) {
result = _createVariable(
_createSetLiteral(node.fileOffset, elementType, []),
typeEnvironment.setType(elementType, _currentLibrary.nonNullable));
} else {
result = _createVariable(
_createListLiteral(node.fileOffset, elementType, []),
typeEnvironment.listType(elementType, _currentLibrary.nonNullable));
}
List<Statement> body = [result];
// Add the elements up to the first non-expression.
for (int j = 0; j < i; ++j) {
_addExpressionElement(elements[j], isSet, result, body);
}
// Translate the elements starting with the first non-expression.
for (; i < elements.length; ++i) {
_translateElement(elements[i], elementType, isSet, result, body);
}
return _createBlockExpression(
node.fileOffset, _createBlock(body), _createVariableGet(result));
}
void _translateElement(Expression element, DartType elementType, bool isSet,
VariableDeclaration result, List<Statement> body) {
if (element is SpreadElement) {
_translateSpreadElement(element, elementType, isSet, result, body);
} else if (element is IfElement) {
_translateIfElement(element, elementType, isSet, result, body);
} else if (element is ForElement) {
_translateForElement(element, elementType, isSet, result, body);
} else if (element is ForInElement) {
_translateForInElement(element, elementType, isSet, result, body);
} else {
_addExpressionElement(
element.accept<TreeNode>(this), isSet, result, body);
}
}
void _addExpressionElement(Expression element, bool isSet,
VariableDeclaration result, List<Statement> body) {
body.add(_createExpressionStatement(
_createAdd(_createVariableGet(result), element, isSet)));
}
void _translateIfElement(IfElement element, DartType elementType, bool isSet,
VariableDeclaration result, List<Statement> body) {
List<Statement> thenStatements = [];
_translateElement(element.then, elementType, isSet, result, thenStatements);
List<Statement> elseStatements;
if (element.otherwise != null) {
_translateElement(element.otherwise, elementType, isSet, result,
elseStatements = <Statement>[]);
}
Statement thenBody = thenStatements.length == 1
? thenStatements.first
: _createBlock(thenStatements);
Statement elseBody;
if (elseStatements != null && elseStatements.isNotEmpty) {
elseBody = elseStatements.length == 1
? elseStatements.first
: _createBlock(elseStatements);
}
body.add(_createIf(element.fileOffset,
element.condition.accept<TreeNode>(this), thenBody, elseBody));
}
void _translateForElement(ForElement element, DartType elementType,
bool isSet, VariableDeclaration result, List<Statement> body) {
List<Statement> statements = <Statement>[];
_translateElement(element.body, elementType, isSet, result, statements);
Statement loopBody =
statements.length == 1 ? statements.first : _createBlock(statements);
ForStatement loop = _createForStatement(
element.fileOffset,
element.variables,
element.condition?.accept<TreeNode>(this),
element.updates,
loopBody);
transformList(loop.variables, this, loop);
transformList(loop.updates, this, loop);
dataForTesting?.registerAlias(element, loop);
body.add(loop);
}
void _translateForInElement(ForInElement element, DartType elementType,
bool isSet, VariableDeclaration result, List<Statement> body) {
List<Statement> statements;
Statement prologue = element.prologue;
if (prologue == null) {
statements = <Statement>[];
} else {
prologue = prologue.accept<TreeNode>(this);
statements =
prologue is Block ? prologue.statements : <Statement>[prologue];
}
_translateElement(element.body, elementType, isSet, result, statements);
Statement loopBody =
statements.length == 1 ? statements.first : _createBlock(statements);
if (element.problem != null) {
body.add(
_createExpressionStatement(element.problem.accept<TreeNode>(this)));
}
ForInStatement loop = _createForInStatement(element.fileOffset,
element.variable, element.iterable.accept<TreeNode>(this), loopBody,
isAsync: element.isAsync);
dataForTesting?.registerAlias(element, loop);
body.add(loop);
}
void _translateSpreadElement(SpreadElement element, DartType elementType,
bool isSet, VariableDeclaration result, List<Statement> body) {
Expression value = element.expression.accept<TreeNode>(this);
// Null-aware spreads require testing the subexpression's value.
VariableDeclaration temp;
if (element.isNullAware) {
temp = _createVariable(value,
typeEnvironment.iterableType(elementType, _currentLibrary.nullable));
body.add(temp);
value = _createNullCheckedVariableGet(temp);
}
VariableDeclaration variable;
Statement loopBody;
if (element.elementType == null ||
!typeEnvironment.isSubtypeOf(element.elementType, elementType,
SubtypeCheckMode.withNullabilities)) {
variable = _createForInVariable(element.fileOffset, const DynamicType());
VariableDeclaration castedVar = _createVariable(
_createImplicitAs(element.expression.fileOffset,
_createVariableGet(variable), elementType),
elementType);
loopBody = _createBlock(<Statement>[
castedVar,
_createExpressionStatement(_createAdd(
_createVariableGet(result), _createVariableGet(castedVar), isSet))
]);
} else {
variable = _createForInVariable(element.fileOffset, elementType);
loopBody = _createExpressionStatement(_createAdd(
_createVariableGet(result), _createVariableGet(variable), isSet));
}
Statement statement =
_createForInStatement(element.fileOffset, variable, value, loopBody);
if (element.isNullAware) {
statement = _createIf(
temp.fileOffset,
_createEqualsNull(_createVariableGet(temp), notEquals: true),
statement);
}
body.add(statement);
}
@override
TreeNode visitListLiteral(ListLiteral node) {
if (node.isConst) {
return _translateConstListOrSet(node, node.typeArgument, node.expressions,
isSet: false);
}
return _translateListOrSet(node, node.typeArgument, node.expressions,
isSet: false);
}
@override
TreeNode visitSetLiteral(SetLiteral node) {
if (node.isConst) {
return _translateConstListOrSet(node, node.typeArgument, node.expressions,
isSet: true);
}
return _translateListOrSet(node, node.typeArgument, node.expressions,
isSet: true);
}
@override
TreeNode visitMapLiteral(MapLiteral node) {
if (node.isConst) {
return _translateConstMap(node);
}
// Translate entries in place up to the first control-flow entry, if any.
int i = 0;
for (; i < node.entries.length; ++i) {
if (node.entries[i] is ControlFlowMapEntry) break;
node.entries[i] = node.entries[i].accept<TreeNode>(this)..parent = node;
}
// If there were no control-flow entries we are done.
if (i == node.entries.length) return node;
// Build a block expression and create an empty map.
VariableDeclaration result = _createVariable(
_createMapLiteral(node.fileOffset, node.keyType, node.valueType, []),
typeEnvironment.mapType(
node.keyType, node.valueType, _currentLibrary.nonNullable));
List<Statement> body = [result];
// Add all the entries up to the first control-flow entry.
for (int j = 0; j < i; ++j) {
_addNormalEntry(node.entries[j], result, body);
}
for (; i < node.entries.length; ++i) {
_translateEntry(
node.entries[i], node.keyType, node.valueType, result, body);
}
return _createBlockExpression(
node.fileOffset, _createBlock(body), _createVariableGet(result));
}
void _translateEntry(MapEntry entry, DartType keyType, DartType valueType,
VariableDeclaration result, List<Statement> body) {
if (entry is SpreadMapEntry) {
_translateSpreadEntry(entry, keyType, valueType, result, body);
} else if (entry is IfMapEntry) {
_translateIfEntry(entry, keyType, valueType, result, body);
} else if (entry is ForMapEntry) {
_translateForEntry(entry, keyType, valueType, result, body);
} else if (entry is ForInMapEntry) {
_translateForInEntry(entry, keyType, valueType, result, body);
} else {
_addNormalEntry(entry.accept<TreeNode>(this), result, body);
}
}
void _addNormalEntry(
MapEntry entry, VariableDeclaration result, List<Statement> body) {
body.add(_createExpressionStatement(_createIndexSet(
entry.fileOffset, _createVariableGet(result), entry.key, entry.value)));
}
void _translateIfEntry(IfMapEntry entry, DartType keyType, DartType valueType,
VariableDeclaration result, List<Statement> body) {
List<Statement> thenBody = [];
_translateEntry(entry.then, keyType, valueType, result, thenBody);
List<Statement> elseBody;
if (entry.otherwise != null) {
_translateEntry(entry.otherwise, keyType, valueType, result,
elseBody = <Statement>[]);
}
Statement thenStatement =
thenBody.length == 1 ? thenBody.first : _createBlock(thenBody);
Statement elseStatement;
if (elseBody != null && elseBody.isNotEmpty) {
elseStatement =
elseBody.length == 1 ? elseBody.first : _createBlock(elseBody);
}
body.add(_createIf(entry.fileOffset, entry.condition.accept<TreeNode>(this),
thenStatement, elseStatement));
}
void _translateForEntry(ForMapEntry entry, DartType keyType,
DartType valueType, VariableDeclaration result, List<Statement> body) {
List<Statement> statements = <Statement>[];
_translateEntry(entry.body, keyType, valueType, result, statements);
Statement loopBody =
statements.length == 1 ? statements.first : _createBlock(statements);
ForStatement loop = _createForStatement(entry.fileOffset, entry.variables,
entry.condition?.accept<TreeNode>(this), entry.updates, loopBody);
dataForTesting?.registerAlias(entry, loop);
transformList(loop.variables, this, loop);
transformList(loop.updates, this, loop);
body.add(loop);
}
void _translateForInEntry(ForInMapEntry entry, DartType keyType,
DartType valueType, VariableDeclaration result, List<Statement> body) {
List<Statement> statements;
Statement prologue = entry.prologue;
if (prologue == null) {
statements = <Statement>[];
} else {
prologue = prologue.accept<TreeNode>(this);
statements =
prologue is Block ? prologue.statements : <Statement>[prologue];
}
_translateEntry(entry.body, keyType, valueType, result, statements);
Statement loopBody =
statements.length == 1 ? statements.first : _createBlock(statements);
if (entry.problem != null) {
body.add(
_createExpressionStatement(entry.problem.accept<TreeNode>(this)));
}
ForInStatement loop = _createForInStatement(entry.fileOffset,
entry.variable, entry.iterable.accept<TreeNode>(this), loopBody,
isAsync: entry.isAsync);
dataForTesting?.registerAlias(entry, loop);
body.add(loop);
}
void _translateSpreadEntry(SpreadMapEntry entry, DartType keyType,
DartType valueType, VariableDeclaration result, List<Statement> body) {
Expression value = entry.expression.accept<TreeNode>(this);
// Null-aware spreads require testing the subexpression's value.
VariableDeclaration temp;
if (entry.isNullAware) {
temp = _createVariable(
value,
typeEnvironment.mapType(
keyType, valueType, _currentLibrary.nullable));
body.add(temp);
value = _createNullCheckedVariableGet(temp);
}
DartType entryType = new InterfaceType(mapEntryClass,
_currentLibrary.nonNullable, <DartType>[keyType, valueType]);
VariableDeclaration variable;
Statement loopBody;
if (entry.entryType == null ||
!typeEnvironment.isSubtypeOf(
entry.entryType, entryType, SubtypeCheckMode.withNullabilities)) {
variable = _createForInVariable(
entry.fileOffset,
new InterfaceType(mapEntryClass, _currentLibrary.nonNullable,
<DartType>[const DynamicType(), const DynamicType()]));
VariableDeclaration keyVar = _createVariable(
_createImplicitAs(
entry.expression.fileOffset,
_createGetKey(
entry.expression.fileOffset, _createVariableGet(variable)),
keyType),
keyType);
VariableDeclaration valueVar = _createVariable(
_createImplicitAs(
entry.expression.fileOffset,
_createGetValue(
entry.expression.fileOffset, _createVariableGet(variable)),
valueType),
valueType);
loopBody = _createBlock(<Statement>[
keyVar,
valueVar,
_createExpressionStatement(_createIndexSet(
entry.expression.fileOffset,
_createVariableGet(result),
_createVariableGet(keyVar),
_createVariableGet(valueVar)))
]);
} else {
variable = _createForInVariable(entry.fileOffset, entryType);
loopBody = _createExpressionStatement(_createIndexSet(
entry.expression.fileOffset,
_createVariableGet(result),
_createGetKey(
entry.expression.fileOffset, _createVariableGet(variable)),
_createGetValue(
entry.expression.fileOffset, _createVariableGet(variable))));
}
Statement statement = _createForInStatement(entry.fileOffset, variable,
_createGetEntries(entry.fileOffset, value), loopBody);
if (entry.isNullAware) {
statement = _createIf(
temp.fileOffset,
_createEqualsNull(_createVariableGet(temp), notEquals: true),
statement);
}
body.add(statement);
}
TreeNode _translateConstListOrSet(
Expression node, DartType elementType, List<Expression> elements,
{bool isSet: false}) {
// Translate elements in place up to the first non-expression, if any.
int i = 0;
for (; i < elements.length; ++i) {
if (elements[i] is ControlFlowElement) break;
elements[i] = elements[i].accept<TreeNode>(this)..parent = node;
}
// If there were only expressions, we are done.
if (i == elements.length) return node;
Expression makeLiteral(int fileOffset, List<Expression> expressions) {
if (isSet) {
return _createSetLiteral(fileOffset, elementType, expressions,
isConst: true);
} else {
return _createListLiteral(fileOffset, elementType, expressions,
isConst: true);
}
}
// Build a concatenation node.
List<Expression> parts = [];
List<Expression> currentPart = i > 0 ? elements.sublist(0, i) : null;
DartType iterableType =
typeEnvironment.iterableType(elementType, _currentLibrary.nonNullable);
for (; i < elements.length; ++i) {
Expression element = elements[i];
if (element is SpreadElement) {
if (currentPart != null) {
parts.add(makeLiteral(node.fileOffset, currentPart));
currentPart = null;
}
Expression spreadExpression = element.expression.accept<TreeNode>(this);
if (element.isNullAware) {
VariableDeclaration temp = _createVariable(
spreadExpression,
typeEnvironment.iterableType(
elementType, _currentLibrary.nullable));
parts.add(_createNullAwareGuard(element.fileOffset, temp,
makeLiteral(element.fileOffset, []), iterableType));
} else {
parts.add(spreadExpression);
}
} else if (element is IfElement) {
if (currentPart != null) {
parts.add(makeLiteral(node.fileOffset, currentPart));
currentPart = null;
}
Expression condition = element.condition.accept<TreeNode>(this);
Expression then = makeLiteral(element.then.fileOffset, [element.then])
.accept<TreeNode>(this);
Expression otherwise = element.otherwise != null
? makeLiteral(element.otherwise.fileOffset, [element.otherwise])
.accept<TreeNode>(this)
: makeLiteral(element.fileOffset, []);
parts.add(_createConditionalExpression(
element.fileOffset, condition, then, otherwise, iterableType));
} else if (element is ForElement || element is ForInElement) {
// Rejected earlier.
unhandled("${element.runtimeType}", "_translateConstListOrSet",
element.fileOffset, getFileUri(element));
} else {
currentPart ??= <Expression>[];
currentPart.add(element.accept<TreeNode>(this));
}
}
if (currentPart != null) {
parts.add(makeLiteral(node.fileOffset, currentPart));
}
if (isSet) {
return new SetConcatenation(parts, typeArgument: elementType)
..fileOffset = node.fileOffset;
} else {
return new ListConcatenation(parts, typeArgument: elementType)
..fileOffset = node.fileOffset;
}
}
TreeNode _translateConstMap(MapLiteral node) {
// Translate entries in place up to the first control-flow entry, if any.
int i = 0;
for (; i < node.entries.length; ++i) {
if (node.entries[i] is ControlFlowMapEntry) break;
node.entries[i] = node.entries[i].accept<TreeNode>(this)..parent = node;
}
// If there were no control-flow entries we are done.
if (i == node.entries.length) return node;
MapLiteral makeLiteral(int fileOffset, List<MapEntry> entries) {
return _createMapLiteral(
fileOffset, node.keyType, node.valueType, entries,
isConst: true);
}
// Build a concatenation node.
List<Expression> parts = [];
List<MapEntry> currentPart = i > 0 ? node.entries.sublist(0, i) : null;
DartType collectionType = typeEnvironment.mapType(
node.keyType, node.valueType, _currentLibrary.nonNullable);
for (; i < node.entries.length; ++i) {
MapEntry entry = node.entries[i];
if (entry is SpreadMapEntry) {
if (currentPart != null) {
parts.add(makeLiteral(node.fileOffset, currentPart));
currentPart = null;
}
Expression spreadExpression = entry.expression.accept<TreeNode>(this);
if (entry.isNullAware) {
VariableDeclaration temp = _createVariable(spreadExpression,
collectionType.withNullability(_currentLibrary.nullable));
parts.add(_createNullAwareGuard(entry.fileOffset, temp,
makeLiteral(entry.fileOffset, []), collectionType));
} else {
parts.add(spreadExpression);
}
} else if (entry is IfMapEntry) {
if (currentPart != null) {
parts.add(makeLiteral(node.fileOffset, currentPart));
currentPart = null;
}
Expression condition = entry.condition.accept<TreeNode>(this);
Expression then = makeLiteral(entry.then.fileOffset, [entry.then])
.accept<TreeNode>(this);
Expression otherwise = entry.otherwise != null
? makeLiteral(entry.otherwise.fileOffset, [entry.otherwise])
.accept<TreeNode>(this)
: makeLiteral(node.fileOffset, []);
parts.add(_createConditionalExpression(
entry.fileOffset, condition, then, otherwise, collectionType));
} else if (entry is ForMapEntry || entry is ForInMapEntry) {
// Rejected earlier.
unhandled("${entry.runtimeType}", "_translateConstMap",
entry.fileOffset, getFileUri(entry));
} else {
currentPart ??= <MapEntry>[];
currentPart.add(entry.accept<TreeNode>(this));
}
}
if (currentPart != null) {
parts.add(makeLiteral(node.fileOffset, currentPart));
}
return new MapConcatenation(parts,
keyType: node.keyType, valueType: node.valueType);
}
void enterLibrary(Library library) {
assert(
_currentLibrary == null,
"Attempting to enter library '${library.fileUri}' "
"without having exited library '${_currentLibrary.fileUri}'.");
_currentLibrary = library;
}
void exitLibrary() {
assert(_currentLibrary != null,
"Attempting to exit a library without having entered one.");
_currentLibrary = null;
}
VariableDeclaration _createVariable(Expression expression, DartType type) {
assert(expression != null);
assert(expression.fileOffset != TreeNode.noOffset);
return new VariableDeclaration.forValue(expression, type: type)
..fileOffset = expression.fileOffset;
}
VariableDeclaration _createForInVariable(int fileOffset, DartType type) {
assert(fileOffset != TreeNode.noOffset);
return new VariableDeclaration.forValue(null, type: type)
..fileOffset = fileOffset;
}
VariableGet _createVariableGet(VariableDeclaration variable) {
assert(variable != null);
assert(variable.fileOffset != TreeNode.noOffset);
return new VariableGet(variable)..fileOffset = variable.fileOffset;
}
VariableGet _createNullCheckedVariableGet(VariableDeclaration variable) {
assert(variable != null);
assert(variable.fileOffset != TreeNode.noOffset);
DartType promotedType =
variable.type.withNullability(_currentLibrary.nonNullable);
if (promotedType != variable.type) {
return new VariableGet(variable, promotedType)
..fileOffset = variable.fileOffset;
}
return _createVariableGet(variable);
}
MapLiteral _createMapLiteral(int fileOffset, DartType keyType,
DartType valueType, List<MapEntry> entries,
{bool isConst: false}) {
assert(fileOffset != null);
assert(fileOffset != TreeNode.noOffset);
return new MapLiteral(entries,
keyType: keyType, valueType: valueType, isConst: isConst)
..fileOffset = fileOffset;
}
ListLiteral _createListLiteral(
int fileOffset, DartType elementType, List<Expression> elements,
{bool isConst: false}) {
assert(fileOffset != null);
assert(fileOffset != TreeNode.noOffset);
return new ListLiteral(elements,
typeArgument: elementType, isConst: isConst)
..fileOffset = fileOffset;
}
Expression _createSetLiteral(
int fileOffset, DartType elementType, List<Expression> elements,
{bool isConst: false}) {
if (isConst) {
return new SetLiteral(elements,
typeArgument: elementType, isConst: isConst)
..fileOffset = fileOffset;
} else {
// TODO(kmillikin): When all the back ends handle set literals we can use
// one here.
return new StaticInvocation(
setFactory,
new Arguments(elements, types: [elementType])
..fileOffset = fileOffset)
..fileOffset = fileOffset;
}
}
ExpressionStatement _createExpressionStatement(Expression expression) {
assert(expression != null);
assert(expression.fileOffset != TreeNode.noOffset);
return new ExpressionStatement(expression)
..fileOffset = expression.fileOffset;
}
MethodInvocation _createAdd(
Expression receiver, Expression argument, bool isSet) {
assert(receiver != null);
assert(argument != null);
assert(argument.fileOffset != TreeNode.noOffset,
"No fileOffset on ${argument}.");
return new MethodInvocation(receiver, new Name('add'),
new Arguments([argument]), isSet ? setAdd : listAdd)
..fileOffset = argument.fileOffset;
}
Expression _createEqualsNull(Expression expression, {bool notEquals: false}) {
assert(expression != null);
assert(expression.fileOffset != TreeNode.noOffset);
Expression check = new MethodInvocation(
expression,
new Name('=='),
new Arguments([new NullLiteral()..fileOffset = expression.fileOffset]),
objectEquals)
..fileOffset = expression.fileOffset;
if (notEquals) {
check = new Not(check)..fileOffset = expression.fileOffset;
}
return check;
}
MethodInvocation _createIndexSet(
int fileOffset, Expression receiver, Expression key, Expression value) {
assert(fileOffset != null);
assert(fileOffset != TreeNode.noOffset);
return new MethodInvocation(
receiver, new Name('[]='), new Arguments([key, value]), mapPut)
..fileOffset = fileOffset;
}
AsExpression _createImplicitAs(
int fileOffset, Expression expression, DartType type) {
assert(fileOffset != null);
assert(fileOffset != TreeNode.noOffset);
return new AsExpression(expression, type)
..isTypeError = true
..isForNonNullableByDefault = _currentLibrary.isNonNullableByDefault
..fileOffset = fileOffset;
}
IfStatement _createIf(int fileOffset, Expression condition, Statement then,
[Statement otherwise]) {
assert(fileOffset != null);
assert(fileOffset != TreeNode.noOffset);
return new IfStatement(condition, then, otherwise)..fileOffset = fileOffset;
}
PropertyGet _createGetKey(int fileOffset, Expression receiver) {
assert(fileOffset != null);
assert(fileOffset != TreeNode.noOffset);
return new PropertyGet(receiver, new Name('key'), mapEntryKey)
..fileOffset = fileOffset;
}
PropertyGet _createGetValue(int fileOffset, Expression receiver) {
assert(fileOffset != null);
assert(fileOffset != TreeNode.noOffset);
return new PropertyGet(receiver, new Name('value'), mapEntryValue)
..fileOffset = fileOffset;
}
PropertyGet _createGetEntries(int fileOffset, Expression receiver) {
assert(fileOffset != null);
assert(fileOffset != TreeNode.noOffset);
return new PropertyGet(receiver, new Name('entries'), mapEntries)
..fileOffset = fileOffset;
}
ForStatement _createForStatement(
int fileOffset,
List<VariableDeclaration> variables,
Expression condition,
List<Expression> updates,
Statement body) {
assert(fileOffset != null);
assert(fileOffset != TreeNode.noOffset);
return new ForStatement(variables, condition, updates, body)
..fileOffset = fileOffset;
}
ForInStatement _createForInStatement(int fileOffset,
VariableDeclaration variable, Expression iterable, Statement body,
{bool isAsync: false}) {
assert(fileOffset != null);
assert(fileOffset != TreeNode.noOffset);
return new ForInStatement(variable, iterable, body, isAsync: isAsync)
..fileOffset = fileOffset;
}
Let _createNullAwareGuard(int fileOffset, VariableDeclaration variable,
Expression defaultValue, DartType type) {
return new Let(
variable,
_createConditionalExpression(
fileOffset,
_createEqualsNull(_createVariableGet(variable)),
defaultValue,
_createNullCheckedVariableGet(variable),
type))
..fileOffset = fileOffset;
}
Block _createBlock(List<Statement> statements) {
return new Block(statements);
}
BlockExpression _createBlockExpression(
int fileOffset, Block body, Expression value) {
assert(fileOffset != null);
assert(fileOffset != TreeNode.noOffset);
return new BlockExpression(body, value)..fileOffset = fileOffset;
}
ConditionalExpression _createConditionalExpression(
int fileOffset,
Expression condition,
Expression then,
Expression otherwise,
DartType type) {
assert(fileOffset != null);
assert(fileOffset != TreeNode.noOffset);
return new ConditionalExpression(condition, then, otherwise, type)
..fileOffset = fileOffset;
}
}