blob: 253d4ce2fa2e93fecdbda32b8fb7156afd21eb58 [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.
// @dart = 2.9
library fasta.transform_set_literals;
import 'package:kernel/ast.dart';
import 'package:kernel/core_types.dart' show CoreTypes;
import 'package:kernel/src/legacy_erasure.dart' show legacyErasure;
import 'package:kernel/type_algebra.dart' show Substitution;
import '../source/source_loader.dart' show SourceLoader;
import 'redirecting_factory_body.dart' show RedirectingFactoryBody;
// TODO(askesc): Delete this class when all backends support set literals.
class SetLiteralTransformer extends Transformer {
final CoreTypes coreTypes;
final Procedure setFactory;
final Procedure addMethod;
FunctionType _addMethodFunctionType;
final bool useNewMethodInvocationEncoding;
/// 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;
}
static Procedure _findAddMethod(CoreTypes coreTypes) {
return coreTypes.index.getMember('dart:core', 'Set', 'add');
}
SetLiteralTransformer(SourceLoader loader)
: coreTypes = loader.coreTypes,
setFactory = _findSetFactory(loader.coreTypes),
addMethod = _findAddMethod(loader.coreTypes),
useNewMethodInvocationEncoding =
loader.target.backendTarget.supportsNewMethodInvocationEncoding {
_addMethodFunctionType = addMethod.getterType;
}
TreeNode visitSetLiteral(SetLiteral node) {
if (node.isConst) return node;
// Create the set: Set<E> setVar = new Set<E>();
DartType receiverType;
VariableDeclaration setVar = new VariableDeclaration.forValue(
new StaticInvocation(
setFactory, new Arguments([], types: [node.typeArgument])),
type: receiverType = new InterfaceType(coreTypes.setClass,
_currentLibrary.nonNullable, [node.typeArgument]));
// Now create a list of all statements needed.
List<Statement> statements = [setVar];
for (int i = 0; i < node.expressions.length; i++) {
Expression entry = node.expressions[i].accept<TreeNode>(this);
Expression methodInvocation;
if (useNewMethodInvocationEncoding) {
FunctionType functionType = Substitution.fromInterfaceType(receiverType)
.substituteType(_addMethodFunctionType);
if (!_currentLibrary.isNonNullableByDefault) {
functionType = legacyErasure(functionType);
}
methodInvocation = new InstanceInvocation(InstanceAccessKind.Instance,
new VariableGet(setVar), new Name("add"), new Arguments([entry]),
functionType: functionType, interfaceTarget: addMethod)
..fileOffset = entry.fileOffset
..isInvariant = true;
} else {
methodInvocation = new MethodInvocation(new VariableGet(setVar),
new Name("add"), new Arguments([entry]), addMethod)
..fileOffset = entry.fileOffset
..isInvariant = true;
}
statements.add(new ExpressionStatement(methodInvocation)
..fileOffset = methodInvocation.fileOffset);
}
// Finally, return a BlockExpression with the statements, having the value
// of the (now created) set.
return new BlockExpression(new Block(statements), new VariableGet(setVar))
..fileOffset = node.fileOffset;
}
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;
}
}