Temporary desugaring of set literals.
When compiling for backends that have not yet implemented support for
the set literal kernel node, transform all such literals into either a
Set() creation with a sequence of add() calls (for non-const literals)
or a const map literal wrapped in a const _UnmodifiableSet object
(for const literals).
The transformation is only performed on bodies or field initializers
that contain at least one actual set literal (i.e. not including
ambiguous set/map literals that were resolved as map literals), so as
to minimize the overhead for code not containing any set literals.
Change-Id: I80318717f0e8481a73f7034e18beea6494fd44bc
Reviewed-on: https://dart-review.googlesource.com/c/87326
Commit-Queue: Aske Simon Christensen <askesc@google.com>
Reviewed-by: Peter von der Ahé <ahe@google.com>
diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
index 31701d0..d023620 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -109,6 +109,8 @@
getRedirectionTarget,
isRedirectingFactory;
+import 'transform_set_literals.dart' show SetLiteralTransformer;
+
import 'type_algorithms.dart' show calculateBounds;
import 'kernel_api.dart';
@@ -202,6 +204,10 @@
int functionNestingLevel = 0;
+ // Set by type inference when a set literal is encountered that needs to be
+ // transformed because the backend target does not support set literals.
+ bool transformSetLiterals = false;
+
Statement problemInLoopOrSwitch;
Scope switchScope;
@@ -547,6 +553,12 @@
field.initializer = initializer;
_typeInferrer?.inferFieldInitializer(
this, field.builtType, initializer);
+
+ if (transformSetLiterals) {
+ library.loader.setLiteralTransformer ??=
+ new SetLiteralTransformer(library.loader);
+ field.target.accept(library.loader.setLiteralTransformer);
+ }
}
}
}
@@ -736,6 +748,12 @@
_typeInferrer?.inferFunctionBody(
this, _computeReturnTypeContext(member), asyncModifier, body);
+ if (transformSetLiterals) {
+ library.loader.setLiteralTransformer ??=
+ new SetLiteralTransformer(library.loader);
+ body.accept(library.loader.setLiteralTransformer);
+ }
+
// For async, async*, and sync* functions with declared return types, we
// need to determine whether those types are valid.
// TODO(hillerstrom): currently, we need to check whether [legacyMode] is
diff --git a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
index da03c43..08834e4 100644
--- a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
@@ -1114,6 +1114,12 @@
node, inferrer.typeSchemaEnvironment,
inferred: true);
}
+
+ KernelLibraryBuilder library = inferrer.library;
+ if (library != null &&
+ !library.loader.target.backendTarget.supportsSetLiterals) {
+ inferrer.helper.transformSetLiterals = true;
+ }
}
void visitStaticAssignmentJudgment(
diff --git a/pkg/front_end/lib/src/fasta/kernel/transform_set_literals.dart b/pkg/front_end/lib/src/fasta/kernel/transform_set_literals.dart
new file mode 100644
index 0000000..e32f179
--- /dev/null
+++ b/pkg/front_end/lib/src/fasta/kernel/transform_set_literals.dart
@@ -0,0 +1,122 @@
+// 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_set_literals;
+
+import 'dart:core' hide MapEntry;
+
+import 'package:kernel/ast.dart'
+ show
+ Arguments,
+ Class,
+ Constructor,
+ ConstructorInvocation,
+ DartType,
+ Expression,
+ InterfaceType,
+ Let,
+ Library,
+ MapEntry,
+ MapLiteral,
+ MethodInvocation,
+ Name,
+ NullLiteral,
+ Procedure,
+ SetLiteral,
+ StaticInvocation,
+ TreeNode,
+ VariableDeclaration,
+ VariableGet;
+
+import 'package:kernel/core_types.dart' show CoreTypes;
+
+import 'package:kernel/visitor.dart' show Transformer;
+
+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 DartType nullType;
+ final Procedure setFactory;
+ final Procedure addMethod;
+ final Constructor unmodifiableSetConstructor;
+
+ 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');
+ }
+
+ static Constructor _findUnmodifiableSetConstructor(
+ SourceLoader<Library> loader) {
+ // We should not generally dig into libraries like this, and we should
+ // avoid dependencies on libraries other than the ones indexed by
+ // CoreTypes. This is a temporary solution until all backends have
+ // implemented support for set literals.
+ Uri collectionUri = Uri.parse("dart:collection");
+ Library collectionLibrary = loader.builders[collectionUri].target;
+ for (int i = 0; i < collectionLibrary.classes.length; i++) {
+ Class classNode = collectionLibrary.classes[i];
+ if (classNode.name == "_UnmodifiableSet") {
+ for (int j = 0; j < collectionLibrary.classes.length; j++) {
+ Constructor constructor = classNode.constructors[j];
+ if (constructor.name.name.isEmpty) {
+ return constructor;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ SetLiteralTransformer(SourceLoader<Library> loader)
+ : coreTypes = loader.coreTypes,
+ nullType = new InterfaceType(loader.coreTypes.nullClass, []),
+ setFactory = _findSetFactory(loader.coreTypes),
+ addMethod = _findAddMethod(loader.coreTypes),
+ unmodifiableSetConstructor = _findUnmodifiableSetConstructor(loader);
+
+ TreeNode visitSetLiteral(SetLiteral node) {
+ if (node.isConst) {
+ List<MapEntry> entries = new List<MapEntry>(node.expressions.length);
+ for (int i = 0; i < node.expressions.length; i++) {
+ // expression_i: null
+ Expression entry = node.expressions[i].accept(this);
+ entries[i] = new MapEntry(entry, new NullLiteral());
+ }
+ Expression mapExp = new MapLiteral(entries,
+ keyType: node.typeArgument, valueType: nullType, isConst: true);
+ return new ConstructorInvocation(unmodifiableSetConstructor,
+ new Arguments([mapExp], types: [node.typeArgument]),
+ isConst: true);
+ } else {
+ // Outermost declaration of let chain: Set<E> setVar = new Set<E>();
+ VariableDeclaration setVar = new VariableDeclaration.forValue(
+ new StaticInvocation(
+ setFactory, new Arguments([], types: [node.typeArgument])),
+ type: new InterfaceType(coreTypes.setClass, [node.typeArgument]));
+ // Innermost body of let chain: setVar
+ Expression setExp = new VariableGet(setVar);
+ for (int i = node.expressions.length - 1; i >= 0; i--) {
+ // let _ = setVar.add(expression) in rest
+ Expression entry = node.expressions[i].accept(this);
+ setExp = new Let(
+ new VariableDeclaration.forValue(new MethodInvocation(
+ new VariableGet(setVar),
+ new Name("add"),
+ new Arguments([entry]),
+ addMethod)),
+ setExp);
+ }
+ return new Let(setVar, setExp);
+ }
+ }
+}
diff --git a/pkg/front_end/lib/src/fasta/source/source_loader.dart b/pkg/front_end/lib/src/fasta/source/source_loader.dart
index e2a0bc8..9ddb35a 100644
--- a/pkg/front_end/lib/src/fasta/source/source_loader.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_loader.dart
@@ -89,6 +89,8 @@
import '../kernel/body_builder.dart' show BodyBuilder;
+import '../kernel/transform_set_literals.dart' show SetLiteralTransformer;
+
import '../loader.dart' show Loader, untranslatableUriScheme;
import '../parser/class_member_parser.dart' show ClassMemberParser;
@@ -139,6 +141,8 @@
Instrumentation instrumentation;
+ SetLiteralTransformer setLiteralTransformer;
+
SourceLoader(this.fileSystem, this.includeComments, KernelTarget target)
: super(target);
diff --git a/pkg/front_end/lib/src/fasta/type_inference/inference_helper.dart b/pkg/front_end/lib/src/fasta/type_inference/inference_helper.dart
index ff3b0d4..c690103 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/inference_helper.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/inference_helper.dart
@@ -13,6 +13,8 @@
Uri get uri;
+ set transformSetLiterals(bool value);
+
Expression buildProblem(Message message, int charOffset, int length,
{List<LocatedMessage> context, bool suppressMessage});