Version 2.17.0-13.0.dev
Merge commit 'a32e171a6fb1da8bdf3212433b389976dc079461' into 'dev'
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/api/introspection.dart b/pkg/_fe_analyzer_shared/lib/src/macros/api/introspection.dart
index 318219b..09c2de9 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/api/introspection.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/api/introspection.dart
@@ -14,10 +14,6 @@
/// A [Code] object representation of this type annotation.
Code get code;
-
- /// Allows you to check the kind of a [TypeAnnotation] in a switch statement,
- /// and without `is` checks.
- TypeAnnotationKind get kind;
}
/// The base class for function type declarations.
@@ -33,9 +29,6 @@
/// The type parameters for this function.
Iterable<TypeParameterDeclaration> get typeParameters;
-
- @override
- TypeAnnotationKind get kind => TypeAnnotationKind.functionType;
}
/// An unresolved reference to a type.
@@ -48,9 +41,6 @@
/// The type arguments, if applicable.
Iterable<TypeAnnotation> get typeArguments;
-
- @override
- TypeAnnotationKind get kind => TypeAnnotationKind.namedType;
}
/// The interface representing a resolved type.
@@ -75,10 +65,6 @@
abstract class Declaration {
/// The name of this declaration.
String get name;
-
- /// Allows you to check the kind of a [Declaration] in a switch statement,
- /// and without `is` checks.
- DeclarationKind get kind;
}
/// A declaration that defines a new type in the program.
@@ -105,9 +91,6 @@
/// Information about fields, methods, and constructors must be retrieved from
/// the `builder` objects.
abstract class ClassDeclaration implements TypeDeclaration {
- @override
- DeclarationKind get kind => DeclarationKind.clazz;
-
/// Whether this class has an `abstract` modifier.
bool get isAbstract;
@@ -129,18 +112,12 @@
/// Type alias introspection information.
abstract class TypeAliasDeclaration extends TypeDeclaration {
- @override
- DeclarationKind get kind => DeclarationKind.typeAlias;
-
/// The type annotation this is an alias for.
TypeAnnotation get type;
}
/// Function introspection information.
abstract class FunctionDeclaration implements Declaration {
- @override
- DeclarationKind get kind => DeclarationKind.function;
-
/// Whether this function has an `abstract` modifier.
bool get isAbstract;
@@ -168,9 +145,6 @@
/// Method introspection information.
abstract class MethodDeclaration implements FunctionDeclaration {
- @override
- DeclarationKind get kind => DeclarationKind.method;
-
/// The class that defines this method.
TypeAnnotation get definingClass;
}
@@ -183,9 +157,6 @@
/// Variable introspection information.
abstract class VariableDeclaration implements Declaration {
- @override
- DeclarationKind get kind => DeclarationKind.variable;
-
/// Whether this function has an `abstract` modifier.
bool get isAbstract;
@@ -201,18 +172,12 @@
/// Field introspection information ..
abstract class FieldDeclaration implements VariableDeclaration {
- @override
- DeclarationKind get kind => DeclarationKind.field;
-
/// The class that defines this method.
TypeAnnotation get definingClass;
}
/// Parameter introspection information.
abstract class ParameterDeclaration implements Declaration {
- @override
- DeclarationKind get kind => DeclarationKind.parameter;
-
/// The type of this parameter.
TypeAnnotation get type;
@@ -230,28 +195,6 @@
/// Type parameter introspection information.
abstract class TypeParameterDeclaration implements Declaration {
- @override
- DeclarationKind get kind => DeclarationKind.typeParameter;
-
/// The bounds for this type parameter, if it has any.
TypeAnnotation? get bounds;
}
-
-// The kinds of type declarations.
-enum DeclarationKind {
- clazz,
- constructor,
- field,
- function,
- method,
- parameter,
- typeAlias,
- typeParameter,
- variable,
-}
-
-// The kinds of type annotations.
-enum TypeAnnotationKind {
- namedType,
- functionType,
-}
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart b/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
index 55ea8bd..332144f 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
@@ -26,6 +26,7 @@
import 'dart:async';
import 'dart:isolate';
+import 'package:_fe_analyzer_shared/src/macros/executor_shared/introspection_impls.dart';
import 'package:_fe_analyzer_shared/src/macros/executor_shared/response_impls.dart';
import 'package:_fe_analyzer_shared/src/macros/executor_shared/serialization.dart';
import 'package:_fe_analyzer_shared/src/macros/executor_shared/protocol.dart';
@@ -99,12 +100,14 @@
return new SerializableResponse(
responseType: MessageType.macroInstanceIdentifier,
response: identifier,
- requestId: request.id);
+ requestId: request.id,
+ serializationZoneId: request.serializationZoneId);
} catch (e) {
return new SerializableResponse(
responseType: MessageType.error,
error: e.toString(),
- requestId: request.id);
+ requestId: request.id,
+ serializationZoneId: request.serializationZoneId);
}
}
@@ -118,7 +121,7 @@
}
Declaration declaration = request.declaration;
if (instance is FunctionDefinitionMacro &&
- declaration is FunctionDeclaration) {
+ declaration is FunctionDeclarationImpl) {
FunctionDefinitionBuilderImpl builder = new FunctionDefinitionBuilderImpl(
declaration,
request.typeResolver,
@@ -128,7 +131,8 @@
return new SerializableResponse(
responseType: MessageType.macroExecutionResult,
response: builder.result,
- requestId: request.id);
+ requestId: request.id,
+ serializationZoneId: request.serializationZoneId);
} else {
throw new UnsupportedError(
('Only FunctionDefinitionMacros are supported currently'));
@@ -137,7 +141,8 @@
return new SerializableResponse(
responseType: MessageType.error,
error: e.toString(),
- requestId: request.id);
+ requestId: request.id,
+ serializationZoneId: request.serializationZoneId);
}
}
''';
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor.dart
index 38a9f4b..9778950 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor.dart
@@ -62,14 +62,14 @@
///
/// Throws an exception if there is an error executing the macro.
Future<MacroExecutionResult> executeTypesPhase(
- MacroInstanceIdentifier macro, Declaration declaration);
+ MacroInstanceIdentifier macro, covariant Declaration declaration);
/// Runs the declarations phase for [macro] on a given [declaration].
///
/// Throws an exception if there is an error executing the macro.
Future<MacroExecutionResult> executeDeclarationsPhase(
MacroInstanceIdentifier macro,
- Declaration declaration,
+ covariant Declaration declaration,
TypeResolver typeResolver,
ClassIntrospector classIntrospector);
@@ -78,7 +78,7 @@
/// Throws an exception if there is an error executing the macro.
Future<MacroExecutionResult> executeDefinitionsPhase(
MacroInstanceIdentifier macro,
- Declaration declaration,
+ covariant Declaration declaration,
TypeResolver typeResolver,
ClassIntrospector classIntrospector,
TypeDeclarationResolver typeDeclarationResolver);
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/introspection_impls.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/introspection_impls.dart
index e237127..7706af7 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/introspection_impls.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/introspection_impls.dart
@@ -2,12 +2,27 @@
// 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 'remote_instance.dart';
+import 'serialization.dart';
+import 'serialization_extensions.dart';
import '../api.dart';
-abstract class TypeAnnotationImpl implements TypeAnnotation {
+abstract class TypeAnnotationImpl extends RemoteInstance
+ implements TypeAnnotation {
final bool isNullable;
- TypeAnnotationImpl({required this.isNullable});
+ TypeAnnotationImpl({required int id, required this.isNullable}) : super(id);
+
+ @override
+ void serialize(Serializer serializer) {
+ super.serialize(serializer);
+ // Client side we don't encode anything but the ID.
+ if (serializationMode == SerializationMode.client) {
+ return;
+ }
+
+ serializer.addBool(isNullable);
+ }
}
class NamedTypeAnnotationImpl extends TypeAnnotationImpl
@@ -27,16 +42,33 @@
final String name;
@override
- final List<TypeAnnotation> typeArguments;
+ final List<TypeAnnotationImpl> typeArguments;
@override
- TypeAnnotationKind get kind => TypeAnnotationKind.namedType;
+ RemoteInstanceKind get kind => RemoteInstanceKind.namedTypeAnnotation;
NamedTypeAnnotationImpl({
+ required int id,
required bool isNullable,
required this.name,
required this.typeArguments,
- }) : super(isNullable: isNullable);
+ }) : super(id: id, isNullable: isNullable);
+
+ @override
+ void serialize(Serializer serializer) {
+ super.serialize(serializer);
+ // Client side we don't encode anything but the ID.
+ if (serializationMode == SerializationMode.client) {
+ return;
+ }
+
+ serializer.addString(name);
+ serializer.startList();
+ for (TypeAnnotationImpl typeArg in typeArguments) {
+ typeArg.serialize(serializer);
+ }
+ serializer.endList();
+ }
}
class FunctionTypeAnnotationImpl extends TypeAnnotationImpl
@@ -72,30 +104,75 @@
]);
@override
- final List<ParameterDeclaration> namedParameters;
+ final List<ParameterDeclarationImpl> namedParameters;
@override
- final List<ParameterDeclaration> positionalParameters;
+ final List<ParameterDeclarationImpl> positionalParameters;
@override
- final TypeAnnotation returnType;
+ final TypeAnnotationImpl returnType;
@override
- final List<TypeParameterDeclaration> typeParameters;
+ final List<TypeParameterDeclarationImpl> typeParameters;
@override
- TypeAnnotationKind get kind => TypeAnnotationKind.functionType;
+ RemoteInstanceKind get kind => RemoteInstanceKind.functionTypeAnnotation;
FunctionTypeAnnotationImpl({
+ required int id,
required bool isNullable,
required this.namedParameters,
required this.positionalParameters,
required this.returnType,
required this.typeParameters,
- }) : super(isNullable: isNullable);
+ }) : super(id: id, isNullable: isNullable);
+
+ @override
+ void serialize(Serializer serializer) {
+ super.serialize(serializer);
+ // Client side we don't encode anything but the ID.
+ if (serializationMode == SerializationMode.client) {
+ return;
+ }
+
+ returnType.serialize(serializer);
+
+ serializer.startList();
+ for (ParameterDeclarationImpl param in positionalParameters) {
+ param.serialize(serializer);
+ }
+ serializer.endList();
+
+ serializer.startList();
+ for (ParameterDeclarationImpl param in namedParameters) {
+ param.serialize(serializer);
+ }
+ serializer.endList();
+
+ serializer.startList();
+ for (TypeParameterDeclarationImpl typeParam in typeParameters) {
+ typeParam.serialize(serializer);
+ }
+ serializer.endList();
+ }
}
-class ParameterDeclarationImpl implements ParameterDeclaration {
+abstract class DeclarationImpl extends RemoteInstance implements Declaration {
+ DeclarationImpl(int id) : super(id);
+ @override
+ void serialize(Serializer serializer) {
+ super.serialize(serializer);
+ // Client side we don't encode anything but the ID.
+ if (serializationMode == SerializationMode.client) {
+ return;
+ }
+
+ serializer.addString(name);
+ }
+}
+
+class ParameterDeclarationImpl extends DeclarationImpl
+ implements ParameterDeclaration {
@override
final String name;
@@ -109,34 +186,73 @@
final bool isRequired;
@override
- final TypeAnnotation type;
+ final TypeAnnotationImpl type;
@override
- DeclarationKind get kind => DeclarationKind.parameter;
+ RemoteInstanceKind get kind => RemoteInstanceKind.parameterDeclaration;
ParameterDeclarationImpl({
+ required int id,
required this.name,
required this.defaultValue,
required this.isNamed,
required this.isRequired,
required this.type,
- });
+ }) : super(id);
+
+ @override
+ void serialize(Serializer serializer) {
+ super.serialize(serializer);
+ // Client side we don't encode anything but the ID.
+ if (serializationMode == SerializationMode.client) {
+ return;
+ }
+
+ if (defaultValue == null) {
+ serializer.addNull();
+ } else {
+ defaultValue!.serialize(serializer);
+ }
+ serializer.addBool(isNamed);
+ serializer.addBool(isRequired);
+ type.serialize(serializer);
+ }
}
-class TypeParameterDeclarationImpl implements TypeParameterDeclaration {
+class TypeParameterDeclarationImpl extends DeclarationImpl
+ implements TypeParameterDeclaration {
@override
final String name;
@override
- final TypeAnnotation? bounds;
+ final TypeAnnotationImpl? bounds;
@override
- DeclarationKind get kind => DeclarationKind.typeParameter;
+ RemoteInstanceKind get kind => RemoteInstanceKind.typeParameterDeclaration;
- TypeParameterDeclarationImpl({required this.name, required this.bounds});
+ TypeParameterDeclarationImpl(
+ {required int id, required this.name, required this.bounds})
+ : super(id);
+
+ @override
+ void serialize(Serializer serializer) {
+ super.serialize(serializer);
+ // Client side we don't encode anything but the ID.
+ if (serializationMode == SerializationMode.client) {
+ return;
+ }
+
+ TypeAnnotationImpl? bounds = this.bounds;
+ if (bounds == null) {
+ serializer.addNull();
+ } else {
+ bounds.serialize(serializer);
+ }
+ }
}
-class FunctionDeclarationImpl implements FunctionDeclaration {
+class FunctionDeclarationImpl extends DeclarationImpl
+ implements FunctionDeclaration {
@override
final String name;
@@ -153,21 +269,22 @@
final bool isSetter;
@override
- final List<ParameterDeclaration> namedParameters;
+ final List<ParameterDeclarationImpl> namedParameters;
@override
- final List<ParameterDeclaration> positionalParameters;
+ final List<ParameterDeclarationImpl> positionalParameters;
@override
- final TypeAnnotation returnType;
+ final TypeAnnotationImpl returnType;
@override
- final List<TypeParameterDeclaration> typeParameters;
+ final List<TypeParameterDeclarationImpl> typeParameters;
@override
- DeclarationKind get kind => DeclarationKind.function;
+ RemoteInstanceKind get kind => RemoteInstanceKind.functionDeclaration;
FunctionDeclarationImpl({
+ required int id,
required this.name,
required this.isAbstract,
required this.isExternal,
@@ -177,5 +294,37 @@
required this.positionalParameters,
required this.returnType,
required this.typeParameters,
- });
+ }) : super(id);
+
+ @override
+ void serialize(Serializer serializer) {
+ super.serialize(serializer);
+ // Client side we don't encode anything but the ID.
+ if (serializationMode == SerializationMode.client) {
+ return;
+ }
+
+ serializer
+ ..addBool(isAbstract)
+ ..addBool(isExternal)
+ ..addBool(isGetter)
+ ..addBool(isSetter)
+ ..startList();
+ for (ParameterDeclarationImpl named in namedParameters) {
+ named.serialize(serializer);
+ }
+ serializer
+ ..endList()
+ ..startList();
+ for (ParameterDeclarationImpl positional in positionalParameters) {
+ positional.serialize(serializer);
+ }
+ serializer.endList();
+ returnType.serialize(serializer);
+ serializer.startList();
+ for (TypeParameterDeclarationImpl param in typeParameters) {
+ param.serialize(serializer);
+ }
+ serializer.endList();
+ }
}
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/protocol.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/protocol.dart
index 3dcc455..68d0f94 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/protocol.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/protocol.dart
@@ -9,19 +9,26 @@
import '../executor.dart';
import '../api.dart';
import '../executor_shared/response_impls.dart';
+import 'introspection_impls.dart';
+import 'remote_instance.dart';
import 'serialization.dart';
-import 'serialization_extensions.dart';
/// Base class all requests extend, provides a unique id for each request.
abstract class Request implements Serializable {
final int id;
- Request({int? id}) : this.id = id ?? _next++;
+ final int serializationZoneId;
+
+ Request({int? id, required this.serializationZoneId})
+ : this.id = id ?? _next++;
Request.deserialize(Deserializer deserializer)
- : id = (deserializer..moveNext()).expectNum();
+ : serializationZoneId = (deserializer..moveNext()).expectNum(),
+ id = (deserializer..moveNext()).expectNum();
- void serialize(Serializer serializer) => serializer.addNum(id);
+ void serialize(Serializer serializer) => serializer
+ ..addNum(serializationZoneId)
+ ..addNum(id);
static int _next = 0;
}
@@ -44,16 +51,21 @@
final MessageType responseType;
final String? error;
final int requestId;
+ final int serializationZoneId;
SerializableResponse({
this.error,
required this.requestId,
this.response,
required this.responseType,
+ required this.serializationZoneId,
}) : assert(response != null || error != null),
assert(response == null || error == null);
- factory SerializableResponse.deserialize(Deserializer deserializer) {
+ /// You must first parse the [serializationZoneId] yourself, and then
+ /// call this function in that zone, and pass the ID.
+ factory SerializableResponse.deserialize(
+ Deserializer deserializer, int serializationZoneId) {
deserializer.moveNext();
MessageType responseType = MessageType.values[deserializer.expectNum()];
Serializable? response;
@@ -80,11 +92,14 @@
responseType: responseType,
response: response,
error: error,
- requestId: (deserializer..moveNext()).expectNum());
+ requestId: (deserializer..moveNext()).expectNum(),
+ serializationZoneId: serializationZoneId);
}
void serialize(Serializer serializer) {
- serializer.addNum(responseType.index);
+ serializer
+ ..addNum(serializationZoneId)
+ ..addNum(responseType.index);
if (response != null) {
response!.serialize(serializer);
} else if (error != null) {
@@ -99,7 +114,8 @@
final Uri library;
final String name;
- LoadMacroRequest(this.library, this.name);
+ LoadMacroRequest(this.library, this.name, {required int serializationZoneId})
+ : super(serializationZoneId: serializationZoneId);
LoadMacroRequest.deserialize(Deserializer deserializer)
: library = Uri.parse((deserializer..moveNext()).expectString()),
@@ -122,8 +138,9 @@
final String constructorName;
final Arguments arguments;
- InstantiateMacroRequest(
- this.macroClass, this.constructorName, this.arguments);
+ InstantiateMacroRequest(this.macroClass, this.constructorName, this.arguments,
+ {required int serializationZoneId})
+ : super(serializationZoneId: serializationZoneId);
InstantiateMacroRequest.deserialize(Deserializer deserializer)
: macroClass = new MacroClassIdentifierImpl.deserialize(deserializer),
@@ -145,7 +162,7 @@
/// phase.
class ExecuteDefinitionsPhaseRequest extends Request {
final MacroInstanceIdentifier macro;
- final Declaration declaration;
+ final DeclarationImpl declaration;
/// Client/Server specific implementation, not serialized.
final TypeResolver typeResolver;
@@ -157,14 +174,16 @@
final TypeDeclarationResolver typeDeclarationResolver;
ExecuteDefinitionsPhaseRequest(this.macro, this.declaration,
- this.typeResolver, this.classIntrospector, this.typeDeclarationResolver);
+ this.typeResolver, this.classIntrospector, this.typeDeclarationResolver,
+ {required int serializationZoneId})
+ : super(serializationZoneId: serializationZoneId);
/// When deserializing we have already consumed the message type, so we don't
/// consume it again.
ExecuteDefinitionsPhaseRequest.deserialize(Deserializer deserializer,
this.typeResolver, this.classIntrospector, this.typeDeclarationResolver)
: macro = new MacroInstanceIdentifierImpl.deserialize(deserializer),
- declaration = (deserializer..moveNext()).expectDeclaration(),
+ declaration = RemoteInstance.deserialize(deserializer),
super.deserialize(deserializer);
void serialize(Serializer serializer) {
@@ -177,14 +196,15 @@
/// A request to reflect on a type annotation
class ReflectTypeRequest extends Request {
- final TypeAnnotation typeAnnotation;
+ final TypeAnnotationImpl typeAnnotation;
- ReflectTypeRequest(this.typeAnnotation);
+ ReflectTypeRequest(this.typeAnnotation, {required int serializationZoneId})
+ : super(serializationZoneId: serializationZoneId);
/// When deserializing we have already consumed the message type, so we don't
/// consume it again.
ReflectTypeRequest.deserialize(Deserializer deserializer)
- : typeAnnotation = (deserializer..moveNext()).expectTypeAnnotation(),
+ : typeAnnotation = RemoteInstance.deserialize(deserializer),
super.deserialize(deserializer);
void serialize(Serializer serializer) {
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/remote_instance.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/remote_instance.dart
new file mode 100644
index 0000000..5dcd1f8
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/remote_instance.dart
@@ -0,0 +1,86 @@
+// Copyright (c) 2022, 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 'dart:async';
+
+import 'package:meta/meta.dart';
+
+import 'serialization.dart';
+import 'serialization_extensions.dart';
+
+/// The key used to store the remote instance cache in the current serialization
+/// zone (in server mode only).
+const Symbol remoteInstanceZoneKey = #remoteInstanceCache;
+
+/// On the server side we keep track of remote instances by their ID.
+///
+/// These are a part of the current serialization zone, which all serialization
+/// and deserialization must be done in.
+///
+/// This means the cache lifetime is that of the serialization zone it is run
+/// in.
+Map<int, RemoteInstance> get _remoteInstanceCache =>
+ Zone.current[remoteInstanceZoneKey];
+
+/// Base class for types that need to be able to be traced back to a specific
+/// instance on the server side.
+abstract class RemoteInstance implements Serializable {
+ /// The unique ID for this instance.
+ final int id;
+
+ /// The type of instance being encoded.
+ RemoteInstanceKind get kind;
+
+ /// Static, incrementing ids.
+ static int _nextId = 0;
+
+ /// Gets the next unique identifier.
+ static int get uniqueId => _nextId++;
+
+ /// On the client side [id]s are given and you should reconstruct objects with
+ /// the given ID. On the server side ids should be created using
+ /// [RemoteInstance.uniqueId].
+ RemoteInstance(this.id);
+
+ /// Retrieves a cached instance by ID.
+ static T cached<T>(int id) => _remoteInstanceCache[id] as T;
+
+ /// Deserializes an instance based on the current [serializationMode].
+ static T deserialize<T>(Deserializer deserializer) =>
+ (deserializer..moveNext()).expectRemoteInstance();
+
+ /// This method should be overridden by all subclasses, which should on their
+ /// first line call this super function.
+ ///
+ /// They should then return immediately if [serializationMode] is
+ /// [SerializationMode.client], so that only an ID is sent.
+ @mustCallSuper
+ void serialize(Serializer serializer) {
+ serializer.addNum(id);
+ switch (serializationMode) {
+ case SerializationMode.client:
+ // We only send the ID from the client side.
+ return;
+ case SerializationMode.server:
+ serializer.addNum(kind.index);
+ _remoteInstanceCache[id] = this;
+ return;
+ }
+ }
+}
+
+// The kinds of instances.
+enum RemoteInstanceKind {
+ classDeclaration,
+ constructorDeclaration,
+ fieldDeclaration,
+ functionDeclaration,
+ functionTypeAnnotation,
+ methodDeclaration,
+ namedTypeAnnotation,
+ parameterDeclaration,
+ typeAliasDeclaration,
+ typeParameterDeclaration,
+ variableDeclaration,
+}
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/response_impls.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/response_impls.dart
index 5155f01..77556ad 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/response_impls.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/response_impls.dart
@@ -4,8 +4,11 @@
import 'dart:async';
+import 'package:_fe_analyzer_shared/src/macros/executor_shared/remote_instance.dart';
+
import '../executor.dart';
import '../api.dart';
+import 'introspection_impls.dart';
import 'serialization.dart';
import 'serialization_extensions.dart';
@@ -102,7 +105,7 @@
final ClassIntrospector classIntrospector;
/// The declaration this is a builder for.
- final FunctionDeclaration declaration;
+ final FunctionDeclarationImpl declaration;
/// The final result, will be built up over `augment` calls.
final MacroExecutionResultImpl result;
@@ -113,8 +116,7 @@
FunctionDefinitionBuilderImpl.deserialize(Deserializer deserializer,
this.typeResolver, this.typeDeclarationResolver, this.classIntrospector)
- : declaration =
- (deserializer..moveNext()).expectDeclaration<FunctionDeclaration>(),
+ : declaration = RemoteInstance.deserialize(deserializer),
result = new MacroExecutionResultImpl.deserialize(deserializer);
void serialize(Serializer serializer) {
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/serialization.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/serialization.dart
index f5f9343..89bbf59 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/serialization.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/serialization.dart
@@ -4,10 +4,19 @@
import 'dart:async';
+import 'remote_instance.dart';
+
/// All serialization must be done in a serialization Zone, which tells it
/// whether we are the client or server.
+///
+/// In [SerializationMode.server], sets up a remote instance cache to use when
+/// deserializing remote instances back to their original instance.
T withSerializationMode<T>(SerializationMode mode, T Function() fn) =>
- runZoned(fn, zoneValues: {#serializationMode: mode});
+ runZoned(fn, zoneValues: {
+ #serializationMode: mode,
+ if (mode == SerializationMode.server)
+ remoteInstanceZoneKey: <int, RemoteInstance>{}
+ });
/// Serializable interface
abstract class Serializable {
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/serialization_extensions.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/serialization_extensions.dart
index 01cb768..1fafb40 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/serialization_extensions.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/serialization_extensions.dart
@@ -1,97 +1,90 @@
import 'package:_fe_analyzer_shared/src/macros/executor_shared/introspection_impls.dart';
+import 'remote_instance.dart';
import 'serialization.dart';
import '../api.dart';
extension DeserializerExtensions on Deserializer {
- TypeAnnotation expectTypeAnnotation() {
+ T expectRemoteInstance<T>() {
int id = expectNum();
switch (serializationMode) {
- case SerializationMode.server:
- return _typeAnnotationsById[id]!;
case SerializationMode.client:
- TypeAnnotation typeAnnotation;
moveNext();
- TypeAnnotationKind type = TypeAnnotationKind.values[expectNum()];
+ RemoteInstanceKind kind = RemoteInstanceKind.values[expectNum()];
moveNext();
- switch (type) {
- case TypeAnnotationKind.namedType:
- typeAnnotation = _expectNamedTypeAnnotation();
- break;
- case TypeAnnotationKind.functionType:
- typeAnnotation = _expectFunctionTypeAnnotation();
- break;
+ switch (kind) {
+ case RemoteInstanceKind.namedTypeAnnotation:
+ return _expectNamedTypeAnnotation(id) as T;
+ case RemoteInstanceKind.functionTypeAnnotation:
+ return _expectFunctionTypeAnnotation(id) as T;
+ case RemoteInstanceKind.functionDeclaration:
+ return _expectFunctionDeclaration(id) as T;
+ case RemoteInstanceKind.parameterDeclaration:
+ return _expectParameterDeclaration(id) as T;
+ case RemoteInstanceKind.typeParameterDeclaration:
+ return _expectTypeParameterDeclaration(id) as T;
+ default:
+ throw new UnsupportedError('Unsupported remote object kind: $kind');
}
- _typeAnnotationIds[typeAnnotation] = id;
- return typeAnnotation;
+ case SerializationMode.server:
+ return RemoteInstance.cached(id);
}
}
- NamedTypeAnnotation _expectNamedTypeAnnotation() {
+ NamedTypeAnnotation _expectNamedTypeAnnotation(int id) {
bool isNullable = expectBool();
moveNext();
String name = expectString();
moveNext();
expectList();
- List<TypeAnnotation> typeArguments = [
+ List<TypeAnnotationImpl> typeArguments = [
for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
- expectTypeAnnotation(),
+ expectRemoteInstance(),
];
return new NamedTypeAnnotationImpl(
- isNullable: isNullable, name: name, typeArguments: typeArguments);
+ id: id,
+ isNullable: isNullable,
+ name: name,
+ typeArguments: typeArguments);
}
- FunctionTypeAnnotation _expectFunctionTypeAnnotation() {
+ FunctionTypeAnnotation _expectFunctionTypeAnnotation(int id) {
bool isNullable = expectBool();
- moveNext();
- TypeAnnotation returnType = expectTypeAnnotation();
+ TypeAnnotationImpl returnType = RemoteInstance.deserialize(this);
moveNext();
expectList();
- List<ParameterDeclaration> positionalParameters = [
+ List<ParameterDeclarationImpl> positionalParameters = [
for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
- expectDeclaration(),
+ expectRemoteInstance(),
];
moveNext();
expectList();
- List<ParameterDeclaration> namedParameters = [
+ List<ParameterDeclarationImpl> namedParameters = [
for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
- expectDeclaration(),
+ expectRemoteInstance(),
];
moveNext();
expectList();
- List<TypeParameterDeclaration> typeParameters = [
+ List<TypeParameterDeclarationImpl> typeParameters = [
for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
- expectDeclaration(),
+ expectRemoteInstance(),
];
return new FunctionTypeAnnotationImpl(
- isNullable: isNullable,
- returnType: returnType,
- positionalParameters: positionalParameters,
- namedParameters: namedParameters,
- typeParameters: typeParameters);
+ id: id,
+ isNullable: isNullable,
+ returnType: returnType,
+ positionalParameters: positionalParameters,
+ namedParameters: namedParameters,
+ typeParameters: typeParameters,
+ );
}
- T expectDeclaration<T extends Declaration>() {
- DeclarationKind kind = DeclarationKind.values[expectNum()];
- moveNext();
- switch (kind) {
- case DeclarationKind.parameter:
- return _expectParameterDeclaration() as T;
- case DeclarationKind.typeParameter:
- return _expectTypeParameterDeclaration() as T;
- case DeclarationKind.function:
- return _expectFunctionDeclaration() as T;
- default:
- throw new UnimplementedError('Cant deserialize $kind yet');
- }
- }
-
- ParameterDeclaration _expectParameterDeclaration() {
+ ParameterDeclaration _expectParameterDeclaration(int id) {
String name = expectString();
moveNext();
Code? defaultValue;
@@ -101,10 +94,11 @@
bool isNamed = expectBool();
moveNext();
bool isRequired = expectBool();
- moveNext();
- TypeAnnotation type = expectTypeAnnotation();
+
+ TypeAnnotationImpl type = RemoteInstance.deserialize(this);
return new ParameterDeclarationImpl(
+ id: id,
defaultValue: defaultValue,
isNamed: isNamed,
isRequired: isRequired,
@@ -112,17 +106,17 @@
type: type);
}
- TypeParameterDeclaration _expectTypeParameterDeclaration() {
+ TypeParameterDeclaration _expectTypeParameterDeclaration(int id) {
String name = expectString();
moveNext();
- TypeAnnotation? bounds;
+ TypeAnnotationImpl? bounds;
if (!checkNull()) {
- bounds = expectTypeAnnotation();
+ bounds = expectRemoteInstance();
}
- return new TypeParameterDeclarationImpl(name: name, bounds: bounds);
+ return new TypeParameterDeclarationImpl(id: id, name: name, bounds: bounds);
}
- FunctionDeclaration _expectFunctionDeclaration() {
+ FunctionDeclaration _expectFunctionDeclaration(int id) {
String name = expectString();
moveNext();
bool isAbstract = expectBool();
@@ -135,28 +129,28 @@
moveNext();
expectList();
- List<ParameterDeclaration> namedParameters = [
+ List<ParameterDeclarationImpl> namedParameters = [
for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
- expectDeclaration()
+ expectRemoteInstance(),
];
moveNext();
expectList();
- List<ParameterDeclaration> positionalParameters = [
+ List<ParameterDeclarationImpl> positionalParameters = [
for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
- expectDeclaration()
+ expectRemoteInstance(),
];
- moveNext();
- TypeAnnotation returnType = expectTypeAnnotation();
+ TypeAnnotationImpl returnType = RemoteInstance.deserialize(this);
moveNext();
expectList();
- List<TypeParameterDeclaration> typeParameters = [
+ List<TypeParameterDeclarationImpl> typeParameters = [
for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
- expectDeclaration()
+ expectRemoteInstance(),
];
return new FunctionDeclarationImpl(
+ id: id,
name: name,
isAbstract: isAbstract,
isExternal: isExternal,
@@ -184,7 +178,7 @@
parts.add(expectString());
break;
case CodePartKind.typeAnnotation:
- parts.add(expectTypeAnnotation());
+ parts.add(expectRemoteInstance());
break;
}
}
@@ -212,179 +206,6 @@
}
}
-/// On the server side we keep track of type annotations by their ID.
-final _typeAnnotationsById = <int, TypeAnnotation>{};
-
-/// On the client side we keep an expando of ids on [TypeAnnotation]s.
-final _typeAnnotationIds = new Expando<int>();
-
-/// Incrementing ids for [TypeAnnotationImpl]s
-int _nextTypeAnnotationId = 0;
-
-extension SerializeTypeAnnotation on TypeAnnotation {
- void serialize(Serializer serializer) {
- TypeAnnotation self = this;
- if (self is NamedTypeAnnotationImpl) {
- self.serialize(serializer);
- } else if (self is FunctionTypeAnnotationImpl) {
- self.serialize(serializer);
- } else {
- throw new UnsupportedError(
- 'Type ${this.runtimeType} is not supported for serialization.');
- }
- }
-}
-
-/// Does the parts of serialization for type annotations that is shared between
-/// implementations.
-///
-/// Returns `false` if we should continue serializing the rest of the object, or
-/// `true` if the object is fully serialized (just an ID).
-bool _doSharedTypeAnnotationSerialization(Serializer serializer,
- TypeAnnotation typeAnnotation, TypeAnnotationKind kind) {
- switch (serializationMode) {
- case SerializationMode.client:
- // Only send the ID from the client side, and assume we have one.
- int id = _typeAnnotationIds[typeAnnotation]!;
- serializer.addNum(id);
- return true;
- case SerializationMode.server:
- // Server side we may create new ids for unseen annotations,
- // and continue to serialize the rest of the annotation.
- int id = _typeAnnotationIds[typeAnnotation] ?? _nextTypeAnnotationId++;
- // TODO: We should clean these up at some point.
- _typeAnnotationsById[id] = typeAnnotation;
- serializer.addNum(id);
- break;
- }
- serializer.addNum(kind.index);
- serializer.addBool(typeAnnotation.isNullable);
- return false;
-}
-
-extension SerializeNamedTypeAnnotation on NamedTypeAnnotation {
- void serialize(Serializer serializer) {
- if (_doSharedTypeAnnotationSerialization(
- serializer, this, TypeAnnotationKind.namedType)) {
- return;
- }
- serializer.addString(name);
- serializer.startList();
- for (TypeAnnotation typeArg in typeArguments) {
- typeArg.serialize(serializer);
- }
- serializer.endList();
- }
-}
-
-extension SerializeFunctionTypeAnnotation on FunctionTypeAnnotation {
- void serialize(Serializer serializer) {
- if (_doSharedTypeAnnotationSerialization(
- serializer, this, TypeAnnotationKind.functionType)) {
- return;
- }
-
- returnType.serialize(serializer);
-
- serializer.startList();
- for (ParameterDeclaration param in positionalParameters) {
- param.serialize(serializer);
- }
- serializer.endList();
-
- serializer.startList();
- for (ParameterDeclaration param in namedParameters) {
- param.serialize(serializer);
- }
- serializer.endList();
-
- serializer.startList();
- for (TypeParameterDeclaration typeParam in typeParameters) {
- typeParam.serialize(serializer);
- }
- serializer.endList();
- }
-}
-
-/// Does the shared parts of [Declaration] serialization.
-void _serializeDeclaration(Serializer serializer, Declaration declaration) {
- serializer.addNum(declaration.kind.index);
- serializer.addString(declaration.name);
-}
-
-/// Checks the type and deserializes it appropriately.
-extension SerializeDeclaration on Declaration {
- void serialize(Serializer serializer) {
- switch (kind) {
- case DeclarationKind.parameter:
- (this as ParameterDeclaration).serialize(serializer);
- break;
- case DeclarationKind.typeParameter:
- (this as TypeParameterDeclaration).serialize(serializer);
- break;
- case DeclarationKind.function:
- (this as FunctionDeclaration).serialize(serializer);
- break;
- default:
- throw new UnimplementedError('Cant serialize $kind yet');
- }
- }
-}
-
-extension SerializeParameterDeclaration on ParameterDeclaration {
- void serialize(Serializer serializer) {
- _serializeDeclaration(serializer, this);
- if (defaultValue == null) {
- serializer.addNull();
- } else {
- defaultValue!.serialize(serializer);
- }
- serializer.addBool(isNamed);
- serializer.addBool(isRequired);
- type.serialize(serializer);
- }
-}
-
-extension SerializeTypeParameterDeclaration on TypeParameterDeclaration {
- void serialize(Serializer serializer) {
- _serializeDeclaration(serializer, this);
- TypeAnnotation? bounds = this.bounds;
- if (bounds == null) {
- serializer.addNull();
- } else {
- bounds.serialize(serializer);
- }
- }
-}
-
-extension SerializeFunctionDeclaration on FunctionDeclaration {
- void serialize(Serializer serializer) {
- _serializeDeclaration(serializer, this);
- serializer
- ..addBool(isAbstract)
- ..addBool(isExternal)
- ..addBool(isGetter)
- ..addBool(isSetter)
- ..startList();
- for (ParameterDeclaration named in namedParameters) {
- named.serialize(serializer);
- }
- serializer
- ..endList()
- ..startList();
- for (ParameterDeclaration positional in positionalParameters) {
- positional.serialize(serializer);
- }
- serializer.endList();
- returnType.serialize(serializer);
- serializer.startList();
- for (TypeParameterDeclaration param in typeParameters) {
- param.serialize(serializer);
- }
- serializer.endList();
- }
-}
-
extension SerializeCode on Code {
void serialize(Serializer serializer) {
serializer
@@ -398,7 +219,7 @@
} else if (part is Code) {
serializer.addNum(CodePartKind.code.index);
part.serialize(serializer);
- } else if (part is TypeAnnotation) {
+ } else if (part is TypeAnnotationImpl) {
serializer.addNum(CodePartKind.typeAnnotation.index);
part.serialize(serializer);
} else {
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/isolate_mirrors_executor/isolate_mirrors_executor.dart b/pkg/_fe_analyzer_shared/lib/src/macros/isolate_mirrors_executor/isolate_mirrors_executor.dart
index 136a6d5..24c7d80 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/isolate_mirrors_executor/isolate_mirrors_executor.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/isolate_mirrors_executor/isolate_mirrors_executor.dart
@@ -7,6 +7,7 @@
import 'dart:mirrors';
import 'isolate_mirrors_impl.dart';
+import '../executor_shared/introspection_impls.dart';
import '../executor_shared/protocol.dart';
import '../executor.dart';
import '../api.dart';
@@ -101,12 +102,14 @@
@override
Future<MacroExecutionResult> executeDefinitionsPhase(
MacroInstanceIdentifier macro,
- Declaration declaration,
+ DeclarationImpl declaration,
TypeResolver typeResolver,
ClassIntrospector classIntrospector,
TypeDeclarationResolver typeDeclarationResolver) =>
_sendRequest(new ExecuteDefinitionsPhaseRequest(macro, declaration,
- typeResolver, classIntrospector, typeDeclarationResolver));
+ typeResolver, classIntrospector, typeDeclarationResolver,
+ // Serialization zones are not necessary in this executor.
+ serializationZoneId: -1));
@override
Future<MacroExecutionResult> executeTypesPhase(
@@ -121,7 +124,9 @@
String constructor,
Arguments arguments) =>
_sendRequest(
- new InstantiateMacroRequest(macroClass, constructor, arguments));
+ new InstantiateMacroRequest(macroClass, constructor, arguments,
+ // Serialization zones are not necessary in this executor.
+ serializationZoneId: -1));
@override
Future<MacroClassIdentifier> loadMacro(Uri library, String name,
@@ -131,7 +136,9 @@
throw new UnsupportedError(
'The IsolateMirrorsExecutor does not support precompiled dill files');
}
- return _sendRequest(new LoadMacroRequest(library, name));
+ return _sendRequest(new LoadMacroRequest(library, name,
+ // Serialization zones are not necessary in this executor.
+ serializationZoneId: -1));
}
/// Sends a request and returns the response, casting it to the expected
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/isolate_mirrors_executor/isolate_mirrors_impl.dart b/pkg/_fe_analyzer_shared/lib/src/macros/isolate_mirrors_executor/isolate_mirrors_impl.dart
index e6848f8..fe7e32e 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/isolate_mirrors_executor/isolate_mirrors_impl.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/isolate_mirrors_executor/isolate_mirrors_impl.dart
@@ -6,6 +6,7 @@
import 'dart:isolate';
import 'dart:mirrors';
+import '../executor_shared/introspection_impls.dart';
import '../executor_shared/response_impls.dart';
import '../executor_shared/protocol.dart';
import '../api.dart';
@@ -83,9 +84,9 @@
throw new StateError('Unrecognized macro instance ${request.macro}\n'
'Known instances: $_macroInstances)');
}
- Declaration declaration = request.declaration;
+ DeclarationImpl declaration = request.declaration;
if (instance is FunctionDefinitionMacro &&
- declaration is FunctionDeclaration) {
+ declaration is FunctionDeclarationImpl) {
FunctionDefinitionBuilderImpl builder = new FunctionDefinitionBuilderImpl(
declaration,
request.typeResolver,
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/isolated_executor/isolated_executor.dart b/pkg/_fe_analyzer_shared/lib/src/macros/isolated_executor/isolated_executor.dart
index b33dd94..4b564b6 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/isolated_executor/isolated_executor.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/isolated_executor/isolated_executor.dart
@@ -6,9 +6,10 @@
import 'dart:isolate';
import '../api.dart';
+import '../executor_shared/introspection_impls.dart';
import '../executor_shared/protocol.dart';
-import '../executor_shared/serialization.dart';
import '../executor_shared/response_impls.dart';
+import '../executor_shared/serialization.dart';
import '../executor.dart';
/// Returns an instance of [_IsolatedMacroExecutor].
@@ -47,7 +48,7 @@
@override
Future<MacroExecutionResult> executeDeclarationsPhase(
MacroInstanceIdentifier macro,
- Declaration declaration,
+ DeclarationImpl declaration,
TypeResolver typeResolver,
ClassIntrospector classIntrospector) =>
_executors[macro]!.executeDeclarationsPhase(
@@ -56,7 +57,7 @@
@override
Future<MacroExecutionResult> executeDefinitionsPhase(
MacroInstanceIdentifier macro,
- Declaration declaration,
+ DeclarationImpl declaration,
TypeResolver typeResolver,
ClassIntrospector classIntrospector,
TypeDeclarationResolver typeDeclarationResolver) =>
@@ -65,7 +66,7 @@
@override
Future<MacroExecutionResult> executeTypesPhase(
- MacroInstanceIdentifier macro, Declaration declaration) =>
+ MacroInstanceIdentifier macro, DeclarationImpl declaration) =>
_executors[macro]!.executeTypesPhase(macro, declaration);
@override
@@ -114,6 +115,15 @@
/// A map of response completers by request id.
final responseCompleters = <int, Completer<Response>>{};
+ /// We need to know which serialization zone to deserialize objects in, so
+ /// that we read them from the correct cache. Each request creates its own
+ /// zone which it stores here by ID and then responses are deserialized in
+ /// the same zone.
+ static final serializationZones = <int, Zone>{};
+
+ /// Incrementing identifier for the serialization zone ids.
+ static int _nextSerializationZoneId = 0;
+
_SingleIsolatedMacroExecutor(
{required this.onClose,
required this.responseStream,
@@ -141,13 +151,16 @@
if (!sendPortCompleter.isCompleted) {
sendPortCompleter.complete(message as SendPort);
} else {
- withSerializationMode(SerializationMode.server, () {
- JsonDeserializer deserializer =
- new JsonDeserializer(message as List<Object?>);
- SerializableResponse response =
- new SerializableResponse.deserialize(deserializer);
- responseStreamController.add(response);
- });
+ JsonDeserializer deserializer =
+ new JsonDeserializer(message as List<Object?>);
+ // Ever object starts with a zone ID which dictates the zone in which we
+ // should deserialize the message.
+ deserializer.moveNext();
+ int zoneId = deserializer.expectNum();
+ Zone zone = serializationZones[zoneId]!;
+ SerializableResponse response = zone.run(
+ () => new SerializableResponse.deserialize(deserializer, zoneId));
+ responseStreamController.add(response);
}
}).onDone(responseStreamController.close);
@@ -172,7 +185,7 @@
@override
Future<MacroExecutionResult> executeDeclarationsPhase(
MacroInstanceIdentifier macro,
- Declaration declaration,
+ DeclarationImpl declaration,
TypeResolver typeResolver,
ClassIntrospector classIntrospector) {
// TODO: implement executeDeclarationsPhase
@@ -182,12 +195,13 @@
@override
Future<MacroExecutionResult> executeDefinitionsPhase(
MacroInstanceIdentifier macro,
- Declaration declaration,
+ DeclarationImpl declaration,
TypeResolver typeResolver,
ClassIntrospector classIntrospector,
TypeDeclarationResolver typeDeclarationResolver) =>
- _sendRequest(new ExecuteDefinitionsPhaseRequest(macro, declaration,
- typeResolver, classIntrospector, typeDeclarationResolver));
+ _sendRequest((zoneId) => new ExecuteDefinitionsPhaseRequest(macro,
+ declaration, typeResolver, classIntrospector, typeDeclarationResolver,
+ serializationZoneId: zoneId));
@override
Future<MacroExecutionResult> executeTypesPhase(
@@ -201,8 +215,9 @@
MacroClassIdentifier macroClass,
String constructor,
Arguments arguments) =>
- _sendRequest(
- new InstantiateMacroRequest(macroClass, constructor, arguments));
+ _sendRequest((zoneId) => new InstantiateMacroRequest(
+ macroClass, constructor, arguments,
+ serializationZoneId: zoneId));
/// These calls are handled by the higher level executor.
@override
@@ -212,16 +227,24 @@
/// Sends a [request] and handles the response, casting it to the expected
/// type or throwing the error provided.
- Future<T> _sendRequest<T>(Request request) =>
+ Future<T> _sendRequest<T>(Request Function(int) requestFactory) =>
withSerializationMode(SerializationMode.server, () async {
+ int zoneId = _nextSerializationZoneId++;
+ serializationZones[zoneId] = Zone.current;
+ Request request = requestFactory(zoneId);
JsonSerializer serializer = new JsonSerializer();
request.serialize(serializer);
sendPort.send(serializer.result);
Completer<Response> completer = new Completer<Response>();
responseCompleters[request.id] = completer;
- Response response = await completer.future;
- T? result = response.response as T?;
- if (result != null) return result;
- throw response.error!;
+ try {
+ Response response = await completer.future;
+ T? result = response.response as T?;
+ if (result != null) return result;
+ throw response.error!;
+ } finally {
+ // Clean up the zone after the request is done.
+ serializationZones.remove(zoneId);
+ }
});
}
diff --git a/pkg/_fe_analyzer_shared/test/macros/executor_shared/serialization_test.dart b/pkg/_fe_analyzer_shared/test/macros/executor_shared/serialization_test.dart
index c069423..9faf079 100644
--- a/pkg/_fe_analyzer_shared/test/macros/executor_shared/serialization_test.dart
+++ b/pkg/_fe_analyzer_shared/test/macros/executor_shared/serialization_test.dart
@@ -2,12 +2,14 @@
// 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:_fe_analyzer_shared/src/macros/executor_shared/introspection_impls.dart';
+import 'package:_fe_analyzer_shared/src/macros/executor_shared/remote_instance.dart';
import 'package:_fe_analyzer_shared/src/macros/executor_shared/serialization.dart';
import 'package:test/test.dart';
void main() {
group('json serializer', () {
- test('can serialize and deserialize data', () {
+ test('can serialize and deserialize basic data', () {
var serializer = JsonSerializer();
serializer
..addNum(1)
@@ -74,5 +76,41 @@
expect(deserializer.moveNext(), false);
});
+
+ test('remote instances', () async {
+ var string = NamedTypeAnnotationImpl(
+ id: RemoteInstance.uniqueId,
+ isNullable: false,
+ name: 'String',
+ typeArguments: const []);
+ var foo = NamedTypeAnnotationImpl(
+ id: RemoteInstance.uniqueId,
+ isNullable: false,
+ name: 'Foo',
+ typeArguments: [string]);
+ Object? serializedFoo;
+ var serializer = JsonSerializer();
+
+ withSerializationMode(SerializationMode.server, () {
+ foo.serialize(serializer);
+ serializedFoo = serializer.result;
+ var response = roundTrip(serializedFoo);
+ var deserializer = JsonDeserializer(response as List<Object?>);
+ var instance = RemoteInstance.deserialize(deserializer);
+ expect(instance, foo);
+ });
+ });
+ });
+}
+
+/// Deserializes [serialized] in client mode and sends it back.
+Object? roundTrip(Object? serialized) {
+ return withSerializationMode(SerializationMode.client, () {
+ var deserializer = JsonDeserializer(serialized as List<Object?>);
+ var instance =
+ RemoteInstance.deserialize<NamedTypeAnnotationImpl>(deserializer);
+ var serializer = JsonSerializer();
+ instance.serialize(serializer);
+ return serializer.result;
});
}
diff --git a/pkg/_fe_analyzer_shared/test/macros/isolate_mirror_executor/isolate_mirror_executor_test.dart b/pkg/_fe_analyzer_shared/test/macros/isolate_mirror_executor/isolate_mirror_executor_test.dart
index 7056208..aae2ad0 100644
--- a/pkg/_fe_analyzer_shared/test/macros/isolate_mirror_executor/isolate_mirror_executor_test.dart
+++ b/pkg/_fe_analyzer_shared/test/macros/isolate_mirror_executor/isolate_mirror_executor_test.dart
@@ -7,6 +7,7 @@
import 'package:_fe_analyzer_shared/src/macros/api.dart';
import 'package:_fe_analyzer_shared/src/macros/executor.dart';
import 'package:_fe_analyzer_shared/src/macros/executor_shared/introspection_impls.dart';
+import 'package:_fe_analyzer_shared/src/macros/executor_shared/remote_instance.dart';
import 'package:_fe_analyzer_shared/src/macros/isolate_mirrors_executor/isolate_mirrors_executor.dart'
as mirrorExecutor;
@@ -15,6 +16,16 @@
void main() {
late MacroExecutor executor;
+ late File simpleMacroFile;
+
+ setUpAll(() {
+ // We support running from either the root of the SDK or the package root.
+ simpleMacroFile =
+ File('pkg/_fe_analyzer_shared/test/macros/simple_macro.dart');
+ if (!simpleMacroFile.existsSync()) {
+ simpleMacroFile = File('test/macros/simple_macro.dart');
+ }
+ });
setUp(() async {
executor = await mirrorExecutor.start();
@@ -25,12 +36,8 @@
});
test('can load macros and create instances', () async {
- var clazzId = await executor.loadMacro(
- // Tests run from the root of the repo.
- File('pkg/_fe_analyzer_shared/test/macros/simple_macro.dart')
- .absolute
- .uri,
- 'SimpleMacro');
+ var clazzId =
+ await executor.loadMacro(simpleMacroFile.absolute.uri, 'SimpleMacro');
expect(clazzId, isNotNull, reason: 'Can load a macro.');
var instanceId =
@@ -51,6 +58,7 @@
var definitionResult = await executor.executeDefinitionsPhase(
instanceId,
FunctionDeclarationImpl(
+ id: RemoteInstance.uniqueId,
isAbstract: false,
isExternal: false,
isGetter: false,
@@ -59,7 +67,10 @@
namedParameters: [],
positionalParameters: [],
returnType: NamedTypeAnnotationImpl(
- name: 'String', isNullable: false, typeArguments: const []),
+ id: RemoteInstance.uniqueId,
+ name: 'String',
+ isNullable: false,
+ typeArguments: const []),
typeParameters: [],
),
_FakeTypeResolver(),
diff --git a/pkg/_fe_analyzer_shared/test/macros/isolated_executor/isolated_executor_test.dart b/pkg/_fe_analyzer_shared/test/macros/isolated_executor/isolated_executor_test.dart
index 4659971..c4cac8b 100644
--- a/pkg/_fe_analyzer_shared/test/macros/isolated_executor/isolated_executor_test.dart
+++ b/pkg/_fe_analyzer_shared/test/macros/isolated_executor/isolated_executor_test.dart
@@ -9,6 +9,7 @@
import 'package:_fe_analyzer_shared/src/macros/bootstrap.dart';
import 'package:_fe_analyzer_shared/src/macros/executor.dart';
import 'package:_fe_analyzer_shared/src/macros/executor_shared/introspection_impls.dart';
+import 'package:_fe_analyzer_shared/src/macros/executor_shared/remote_instance.dart';
import 'package:_fe_analyzer_shared/src/macros/isolated_executor/isolated_executor.dart'
as isolatedExecutor;
@@ -18,6 +19,16 @@
void main() {
late MacroExecutor executor;
late Directory tmpDir;
+ late File simpleMacroFile;
+
+ setUpAll(() {
+ // We support running from either the root of the SDK or the package root.
+ simpleMacroFile =
+ File('pkg/_fe_analyzer_shared/test/macros/simple_macro.dart');
+ if (!simpleMacroFile.existsSync()) {
+ simpleMacroFile = File('test/macros/simple_macro.dart');
+ }
+ });
setUp(() async {
executor = await isolatedExecutor.start();
@@ -30,10 +41,7 @@
});
test('can load macros and create instances', () async {
- // Tests run from the root of the repo.
- var macroUri = File('pkg/_fe_analyzer_shared/test/macros/simple_macro.dart')
- .absolute
- .uri;
+ var macroUri = simpleMacroFile.absolute.uri;
var macroName = 'SimpleMacro';
var bootstrapContent =
@@ -73,6 +81,7 @@
var definitionResult = await executor.executeDefinitionsPhase(
instanceId,
FunctionDeclarationImpl(
+ id: RemoteInstance.uniqueId,
isAbstract: false,
isExternal: false,
isGetter: false,
@@ -81,7 +90,10 @@
namedParameters: [],
positionalParameters: [],
returnType: NamedTypeAnnotationImpl(
- name: 'String', isNullable: false, typeArguments: const []),
+ id: RemoteInstance.uniqueId,
+ name: 'String',
+ isNullable: false,
+ typeArguments: const []),
typeParameters: [],
),
_FakeTypeResolver(),
diff --git a/pkg/front_end/lib/src/fasta/source/diet_listener.dart b/pkg/front_end/lib/src/fasta/source/diet_listener.dart
index 77c43b7..99fa6f1 100644
--- a/pkg/front_end/lib/src/fasta/source/diet_listener.dart
+++ b/pkg/front_end/lib/src/fasta/source/diet_listener.dart
@@ -50,6 +50,8 @@
show InferenceDataForTesting, TypeInferenceEngine;
import '../type_inference/type_inferrer.dart' show TypeInferrer;
import 'diet_parser.dart';
+import 'source_constructor_builder.dart';
+import 'source_enum_builder.dart';
import 'source_field_builder.dart';
import 'source_function_builder.dart';
import 'source_library_builder.dart' show SourceLibraryBuilder;
@@ -962,6 +964,19 @@
void endEnum(Token enumKeyword, Token leftBrace, int memberCount) {
debugEvent("Enum");
checkEmpty(enumKeyword.charOffset);
+
+ SourceEnumBuilder? enumBuilder = currentClass as SourceEnumBuilder?;
+ if (enumBuilder != null) {
+ DeclaredSourceConstructorBuilder? defaultConstructorBuilder =
+ enumBuilder.synthesizedDefaultConstructorBuilder;
+ if (defaultConstructorBuilder != null) {
+ BodyBuilder bodyBuilder =
+ createFunctionListener(defaultConstructorBuilder);
+ bodyBuilder.finishConstructor(
+ defaultConstructorBuilder, AsyncMarker.Sync, new EmptyStatement());
+ }
+ }
+
currentDeclaration = null;
memberScope = libraryBuilder.scope;
}
diff --git a/pkg/front_end/lib/src/fasta/source/outline_builder.dart b/pkg/front_end/lib/src/fasta/source/outline_builder.dart
index 820c2d5..6fe3c15 100644
--- a/pkg/front_end/lib/src/fasta/source/outline_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/outline_builder.dart
@@ -2329,7 +2329,7 @@
// We pop more values than needed to reach typeVariables, offset and name.
List<TypeBuilder>? interfaces = pop() as List<TypeBuilder>?;
- List<TypeBuilder>? mixins = pop() as List<TypeBuilder>?;
+ Object? mixins = pop();
List<TypeVariableBuilder>? typeVariables =
pop() as List<TypeVariableBuilder>?;
int charOffset = popCharOffset(); // identifier char offset.
@@ -2341,7 +2341,7 @@
push(name ?? NullValue.Name);
push(charOffset);
push(typeVariables ?? NullValue.TypeVariables);
- push(mixins ?? NullValue.TypeBuilderList);
+ push(mixins ?? NullValue.TypeBuilder);
push(interfaces ?? NullValue.TypeBuilderList);
push(enumKeyword.charOffset); // start char offset.
@@ -2364,7 +2364,7 @@
int endCharOffset = popCharOffset();
int startCharOffset = popCharOffset();
pop() as List<TypeBuilder>?; // interfaces.
- pop() as List<TypeBuilder>?; // mixins.
+ MixinApplicationBuilder? mixinBuilder = pop() as MixinApplicationBuilder?;
List<TypeVariableBuilder>? typeVariables =
pop() as List<TypeVariableBuilder>?;
int charOffset = popCharOffset(); // identifier char offset.
@@ -2373,8 +2373,15 @@
checkEmpty(startCharOffset);
if (name is! ParserRecovery) {
- libraryBuilder.addEnum(metadata, name as String, typeVariables,
- enumConstantInfos, startCharOffset, charOffset, endCharOffset);
+ libraryBuilder.addEnum(
+ metadata,
+ name as String,
+ typeVariables,
+ mixinBuilder,
+ enumConstantInfos,
+ startCharOffset,
+ charOffset,
+ endCharOffset);
} else {
libraryBuilder
.endNestedDeclaration(
@@ -3199,15 +3206,23 @@
if (mixins is ParserRecovery) {
push(new ParserRecovery(withKeyword.charOffset));
} else {
- // TODO(cstefantsova): Handle enum mixins here.
- push(mixins);
+ NamedTypeBuilder enumType = new NamedTypeBuilder(
+ "_Enum",
+ const NullabilityBuilder.omitted(),
+ /* arguments = */ null,
+ /* fileUri = */ null,
+ /* charOffset = */ null,
+ instanceTypeVariableAccess:
+ InstanceTypeVariableAccessState.Unexpected);
+ push(libraryBuilder.addMixinApplication(
+ enumType, mixins as List<TypeBuilder>, withKeyword.charOffset));
}
}
@override
void handleEnumNoWithClause() {
debugEvent("EnumNoWithClause");
- push(NullValue.TypeBuilderList);
+ push(NullValue.TypeBuilder);
}
@override
diff --git a/pkg/front_end/lib/src/fasta/source/source_class_builder.dart b/pkg/front_end/lib/src/fasta/source/source_class_builder.dart
index 3faf679..c95900c 100644
--- a/pkg/front_end/lib/src/fasta/source/source_class_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_class_builder.dart
@@ -105,6 +105,8 @@
SourceClassBuilder? _patchBuilder;
+ final bool isEnumMixin;
+
SourceClassBuilder(
List<MetadataBuilder>? metadata,
int modifiers,
@@ -124,7 +126,8 @@
{Class? cls,
this.mixedInTypeBuilder,
this.isMixinDeclaration = false,
- this.isMacro: false})
+ this.isMacro: false,
+ this.isEnumMixin: false})
: actualCls = initializeClass(cls, typeVariables, name, parent,
startCharOffset, nameOffset, charEndOffset, referencesFromIndexed),
super(metadata, modifiers, name, typeVariables, supertype, interfaces,
@@ -195,6 +198,11 @@
if (supertypeBuilder != null) {
supertypeBuilder = _checkSupertype(supertypeBuilder!);
}
+ if (isEnumMixin) {
+ assert(supertypeBuilder?.name == "_Enum");
+ supertypeBuilder?.resolveIn(coreLibrary.scope,
+ supertypeBuilder?.charOffset ?? charOffset, fileUri, library);
+ }
Supertype? supertype =
supertypeBuilder?.buildSupertype(library, charOffset, fileUri);
if (supertype != null) {
diff --git a/pkg/front_end/lib/src/fasta/source/source_enum_builder.dart b/pkg/front_end/lib/src/fasta/source/source_enum_builder.dart
index 2f84424..9a0fe61 100644
--- a/pkg/front_end/lib/src/fasta/source/source_enum_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_enum_builder.dart
@@ -46,6 +46,7 @@
import '../builder/nullability_builder.dart';
import '../builder/procedure_builder.dart';
import '../builder/type_builder.dart';
+import '../builder/type_declaration_builder.dart';
import '../builder/type_variable_builder.dart';
import '../fasta_codes.dart'
show
@@ -86,7 +87,7 @@
final NamedTypeBuilder listType;
- DeclaredSourceConstructorBuilder? _synthesizedDefaultConstructorBuilder;
+ DeclaredSourceConstructorBuilder? synthesizedDefaultConstructorBuilder;
SourceEnumBuilder.internal(
List<MetadataBuilder>? metadata,
@@ -99,7 +100,7 @@
this.intType,
this.listType,
this.objectType,
- TypeBuilder enumType,
+ TypeBuilder supertypeBuilder,
this.stringType,
SourceLibraryBuilder parent,
int startCharOffset,
@@ -111,7 +112,7 @@
0,
name,
typeVariables,
- enumType,
+ supertypeBuilder,
/* interfaces = */ null,
/* onTypes = */ null,
scope,
@@ -128,6 +129,7 @@
List<MetadataBuilder>? metadata,
String name,
List<TypeVariableBuilder>? typeVariables,
+ TypeBuilder? supertypeBuilder,
List<EnumConstantInfo?>? enumConstantInfos,
SourceLibraryBuilder parent,
int startCharOffset,
@@ -173,7 +175,7 @@
/* fileUri = */ null,
/* charOffset = */ null,
instanceTypeVariableAccess: InstanceTypeVariableAccessState.Unexpected);
- NamedTypeBuilder enumType = new NamedTypeBuilder(
+ supertypeBuilder ??= new NamedTypeBuilder(
"_Enum",
const NullabilityBuilder.omitted(),
/* arguments = */ null,
@@ -418,14 +420,14 @@
intType,
listType,
objectType,
- enumType,
+ supertypeBuilder,
stringType,
parent,
startCharOffsetComputed,
charOffset,
charEndOffset,
referencesFromIndexed)
- .._synthesizedDefaultConstructorBuilder =
+ ..synthesizedDefaultConstructorBuilder =
synthesizedDefaultConstructorBuilder;
void setParent(String name, MemberBuilder? builder) {
@@ -458,9 +460,21 @@
coreLibrary.scope, charOffset, fileUri, libraryBuilder);
objectType.resolveIn(
coreLibrary.scope, charOffset, fileUri, libraryBuilder);
- TypeBuilder supertypeBuilder = this.supertypeBuilder!;
- supertypeBuilder.resolveIn(
- coreLibrary.scope, charOffset, fileUri, libraryBuilder);
+ TypeBuilder? supertypeBuilder = this.supertypeBuilder;
+ NamedTypeBuilder? enumType;
+
+ while (enumType == null && supertypeBuilder is NamedTypeBuilder) {
+ TypeDeclarationBuilder? superclassBuilder = supertypeBuilder.declaration;
+ if (superclassBuilder is ClassBuilder &&
+ superclassBuilder.isMixinApplication) {
+ supertypeBuilder = superclassBuilder.supertypeBuilder;
+ } else {
+ enumType = supertypeBuilder;
+ }
+ }
+ assert(enumType is NamedTypeBuilder && enumType.name == "_Enum");
+ enumType!.resolveIn(coreLibrary.scope, charOffset, fileUri, libraryBuilder);
+
listType.resolveIn(coreLibrary.scope, charOffset, fileUri, libraryBuilder);
List<Expression> values = <Expression>[];
@@ -481,34 +495,39 @@
valuesBuilder.build(libraryBuilder);
// The super initializer for the synthesized default constructor is
- // inserted here. Other constructors are handled in
- // [BodyBuilder.finishConstructor], as they are processed via the pipeline
- // for constructor parsing and building.
- if (_synthesizedDefaultConstructorBuilder != null) {
- Constructor constructor =
- _synthesizedDefaultConstructorBuilder!.build(libraryBuilder);
- ClassBuilder objectClass = objectType.declaration as ClassBuilder;
- ClassBuilder enumClass = supertypeBuilder.declaration as ClassBuilder;
- MemberBuilder? superConstructor = enumClass.findConstructorOrFactory(
- "", charOffset, fileUri, libraryBuilder);
- if (superConstructor == null || !superConstructor.isConstructor) {
- // TODO(ahe): Ideally, we would also want to check that [Object]'s
- // unnamed constructor requires no arguments. But that information
- // isn't always available at this point, and it's not really a
- // situation that can happen unless you start modifying the SDK
- // sources. (We should add a correct message. We no longer depend on
- // Object here.)
- library.addProblem(
- messageNoUnnamedConstructorInObject,
- objectClass.charOffset,
- objectClass.name.length,
- objectClass.fileUri);
- } else {
- constructor.initializers.add(new SuperInitializer(
- superConstructor.member as Constructor,
- new Arguments.forwarded(
- constructor.function, libraryBuilder.library))
- ..parent = constructor);
+ // inserted here if the enum's supertype is _Enum to preserve the legacy
+ // behavior or having the old-style enum constants built in the outlines.
+ // Other constructors are handled in [BodyBuilder.finishConstructor] as
+ // they are processed via the pipeline for constructor parsing and
+ // building.
+ if (identical(this.supertypeBuilder, enumType)) {
+ if (synthesizedDefaultConstructorBuilder != null) {
+ Constructor constructor =
+ synthesizedDefaultConstructorBuilder!.build(libraryBuilder);
+ ClassBuilder objectClass = objectType.declaration as ClassBuilder;
+ ClassBuilder enumClass = enumType.declaration as ClassBuilder;
+ MemberBuilder? superConstructor = enumClass.findConstructorOrFactory(
+ "", charOffset, fileUri, libraryBuilder);
+ if (superConstructor == null || !superConstructor.isConstructor) {
+ // TODO(ahe): Ideally, we would also want to check that [Object]'s
+ // unnamed constructor requires no arguments. But that information
+ // isn't always available at this point, and it's not really a
+ // situation that can happen unless you start modifying the SDK
+ // sources. (We should add a correct message. We no longer depend on
+ // Object here.)
+ library.addProblem(
+ messageNoUnnamedConstructorInObject,
+ objectClass.charOffset,
+ objectClass.name.length,
+ objectClass.fileUri);
+ } else {
+ constructor.initializers.add(new SuperInitializer(
+ superConstructor.member as Constructor,
+ new Arguments.forwarded(
+ constructor.function, libraryBuilder.library))
+ ..parent = constructor);
+ }
+ synthesizedDefaultConstructorBuilder = null;
}
}
diff --git a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
index 28267ee..0ef86e7 100644
--- a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
@@ -2103,7 +2103,8 @@
List<TypeVariableBuilder>? typeVariables,
int modifiers: 0,
List<TypeBuilder>? interfaces,
- required bool isMacro}) {
+ required bool isMacro,
+ bool isEnumMixin: false}) {
if (name == null) {
// The following parameters should only be used when building a named
// mixin application.
@@ -2322,7 +2323,8 @@
charEndOffset,
referencesFromIndexedClass,
mixedInTypeBuilder: isMixinDeclaration ? null : mixin,
- isMacro: isNamedMixinApplication && isMacro);
+ isMacro: isNamedMixinApplication && isMacro,
+ isEnumMixin: isEnumMixin && i == 0);
// TODO(ahe, kmillikin): Should always be true?
// pkg/analyzer/test/src/summary/resynthesize_kernel_test.dart can't
// handle that :(
@@ -2791,6 +2793,7 @@
List<MetadataBuilder>? metadata,
String name,
List<TypeVariableBuilder>? typeVariables,
+ TypeBuilder? supertypeBuilder,
List<EnumConstantInfo?>? enumConstantInfos,
int startCharOffset,
int charOffset,
@@ -2812,6 +2815,9 @@
metadata,
name,
typeVariables,
+ applyMixins(supertypeBuilder, startCharOffset, charOffset,
+ charEndOffset, name, /* isMixinDeclaration = */ false,
+ typeVariables: typeVariables, isMacro: false, isEnumMixin: true),
enumConstantInfos,
this,
startCharOffset,
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index 7cfd23a..e105550 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -353,6 +353,7 @@
deterministic
dev
device
+dictates
diff
differs
diffs
@@ -735,6 +736,7 @@
lhs
lib
libs
+lifetime
lifted
lifter
limiting
@@ -1074,6 +1076,7 @@
recompiling
recompute
recomputed
+reconstruct
recorder
recoveries
recreate
@@ -1375,6 +1378,7 @@
topological
tops
tput
+traced
tracker
traditional
transformers
@@ -1602,6 +1606,7 @@
yields
yn
ynon
+yourself
ypotentially
ys
yss
@@ -1613,3 +1618,4 @@
zn
zone
zoned
+zones
diff --git a/pkg/front_end/test/spell_checking_list_common.txt b/pkg/front_end/test/spell_checking_list_common.txt
index 3f775f2..09b0c93 100644
--- a/pkg/front_end/test/spell_checking_list_common.txt
+++ b/pkg/front_end/test/spell_checking_list_common.txt
@@ -1029,6 +1029,7 @@
entries
entry
enum
+enum's
enumerated
enumeration
enumerations
diff --git a/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart b/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart
new file mode 100644
index 0000000..81e592e
--- /dev/null
+++ b/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart
@@ -0,0 +1,51 @@
+// Copyright (c) 2022, 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.
+
+class A {
+ String get foo => "foo";
+}
+
+class B {
+ int bar() => 42;
+}
+
+mixin M {
+ void set callOnAssignment(void Function() f) {
+ f();
+ }
+}
+
+enum E1 with A { one, two }
+
+enum E2 with A, B { one, two }
+
+enum E3 with M { one, two }
+
+expectEquals(x, y) {
+ if (x != y) {
+ throw "Expected '$x' and '$y' to be equal.";
+ }
+}
+
+expectThrows(void Function() f) {
+ try {
+ f();
+ throw "Expected function to throw.";
+ } catch (e) {}
+}
+
+void throwOnCall() {
+ throw 42;
+}
+
+main() {
+ expectEquals(E1.one.foo, "foo");
+ expectEquals(E1.two.foo, "foo");
+ expectEquals(E2.one.foo, "foo");
+ expectEquals(E2.two.foo, "foo");
+ expectEquals(E2.one.bar(), "bar");
+ expectEquals(E2.two.bar(), "bar");
+ expectThrows(E3.one.callOnAssignment = throwOnCall);
+ expectThrows(E3.two.callOnAssignment = throwOnCall);
+}
diff --git a/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.strong.expect b/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.strong.expect
new file mode 100644
index 0000000..6e10e5c
--- /dev/null
+++ b/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.strong.expect
@@ -0,0 +1,137 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+ synthetic constructor •() → self::A
+ : super core::Object::•()
+ ;
+ get foo() → core::String
+ return "foo";
+}
+class B extends core::Object {
+ synthetic constructor •() → self::B
+ : super core::Object::•()
+ ;
+ method bar() → core::int
+ return 42;
+}
+abstract class M extends core::Object /*isMixinDeclaration*/ {
+ set callOnAssignment(() → void f) → void {
+ f(){() → void};
+ }
+}
+abstract class _E1&_Enum&A = core::_Enum with self::A /*isAnonymousMixin,hasConstConstructor*/ {
+ const synthetic constructor •(core::int index, core::String _name) → self::_E1&_Enum&A
+ : super core::_Enum::•(index, _name)
+ ;
+ mixin-super-stub get foo() → core::String
+ return super.{self::A::foo};
+}
+class E1 extends self::_E1&_Enum&A /*isEnum*/ {
+ static const field core::List<self::E1> values = #C7;
+ static const field self::E1 one = #C3;
+ static const field self::E1 two = #C6;
+ const constructor •(core::int index, core::String name) → self::E1
+ : super self::_E1&_Enum&A::•(index, name)
+ ;
+ method toString() → core::String
+ ;
+}
+abstract class _E2&_Enum&A = core::_Enum with self::A /*isAnonymousMixin,hasConstConstructor*/ {
+ const synthetic constructor •(core::int index, core::String _name) → self::_E2&_Enum&A
+ : super core::_Enum::•(index, _name)
+ ;
+ mixin-super-stub get foo() → core::String
+ return super.{self::A::foo};
+}
+abstract class _E2&_Enum&A&B = self::_E2&_Enum&A with self::B /*isAnonymousMixin,hasConstConstructor*/ {
+ const synthetic constructor •(core::int index, core::String _name) → self::_E2&_Enum&A&B
+ : super self::_E2&_Enum&A::•(index, _name)
+ ;
+ mixin-super-stub method bar() → core::int
+ return super.{self::B::bar}();
+}
+class E2 extends self::_E2&_Enum&A&B /*isEnum*/ {
+ static const field core::List<self::E2> values = #C10;
+ static const field self::E2 one = #C8;
+ static const field self::E2 two = #C9;
+ const constructor •(core::int index, core::String name) → self::E2
+ : super self::_E2&_Enum&A&B::•(index, name)
+ ;
+ method toString() → core::String
+ ;
+}
+abstract class _E3&_Enum&M = core::_Enum with self::M /*isAnonymousMixin,hasConstConstructor*/ {
+ const synthetic constructor •(core::int index, core::String _name) → self::_E3&_Enum&M
+ : super core::_Enum::•(index, _name)
+ ;
+ mixin-super-stub set callOnAssignment(() → void f) → void
+ return super.{self::M::callOnAssignment} = f;
+}
+class E3 extends self::_E3&_Enum&M /*isEnum*/ {
+ static const field core::List<self::E3> values = #C13;
+ static const field self::E3 one = #C11;
+ static const field self::E3 two = #C12;
+ const constructor •(core::int index, core::String name) → self::E3
+ : super self::_E3&_Enum&M::•(index, name)
+ ;
+ method toString() → core::String
+ ;
+}
+static method expectEquals(dynamic x, dynamic y) → dynamic {
+ if(!(x =={core::Object::==}{(core::Object) → core::bool} y)) {
+ throw "Expected '${x}' and '${y}' to be equal.";
+ }
+}
+static method expectThrows(() → void f) → dynamic {
+ try {
+ f(){() → void};
+ throw "Expected function to throw.";
+ }
+ on core::Object catch(final core::Object e) {
+ }
+}
+static method throwOnCall() → void {
+ throw 42;
+}
+static method main() → dynamic {
+ self::expectEquals(#C3.{self::_E1&_Enum&A::foo}{core::String}, "foo");
+ self::expectEquals(#C6.{self::_E1&_Enum&A::foo}{core::String}, "foo");
+ self::expectEquals(#C8.{self::_E2&_Enum&A::foo}{core::String}, "foo");
+ self::expectEquals(#C9.{self::_E2&_Enum&A::foo}{core::String}, "foo");
+ self::expectEquals(#C8.{self::_E2&_Enum&A&B::bar}(){() → core::int}, "bar");
+ self::expectEquals(#C9.{self::_E2&_Enum&A&B::bar}(){() → core::int}, "bar");
+ self::expectThrows(#C11.{self::_E3&_Enum&M::callOnAssignment} = #C14);
+ self::expectThrows(#C12.{self::_E3&_Enum&M::callOnAssignment} = #C14);
+}
+
+constants {
+ #C1 = 0
+ #C2 = "one"
+ #C3 = self::E1 {index:#C1, _name:#C2}
+ #C4 = 1
+ #C5 = "two"
+ #C6 = self::E1 {index:#C4, _name:#C5}
+ #C7 = <self::E1>[#C3, #C6]
+ #C8 = self::E2 {index:#C1, _name:#C2}
+ #C9 = self::E2 {index:#C4, _name:#C5}
+ #C10 = <self::E2>[#C8, #C9]
+ #C11 = self::E3 {index:#C1, _name:#C2}
+ #C12 = self::E3 {index:#C4, _name:#C5}
+ #C13 = <self::E3>[#C11, #C12]
+ #C14 = static-tearoff self::throwOnCall
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///simple_mixins.dart:
+- E1. (from org-dartlang-testcase:///simple_mixins.dart:19:6)
+- _E1&_Enum&A. (from org-dartlang-testcase:///simple_mixins.dart:19:6)
+- _Enum. (from org-dartlang-sdk:///sdk/lib/core/enum.dart:76:9)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
+- E2. (from org-dartlang-testcase:///simple_mixins.dart:21:6)
+- _E2&_Enum&A&B. (from org-dartlang-testcase:///simple_mixins.dart:21:6)
+- _E2&_Enum&A. (from org-dartlang-testcase:///simple_mixins.dart:21:6)
+- E3. (from org-dartlang-testcase:///simple_mixins.dart:23:6)
+- _E3&_Enum&M. (from org-dartlang-testcase:///simple_mixins.dart:23:6)
diff --git a/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.strong.transformed.expect b/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.strong.transformed.expect
new file mode 100644
index 0000000..4a5994b
--- /dev/null
+++ b/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.strong.transformed.expect
@@ -0,0 +1,138 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+ synthetic constructor •() → self::A
+ : super core::Object::•()
+ ;
+ get foo() → core::String
+ return "foo";
+}
+class B extends core::Object {
+ synthetic constructor •() → self::B
+ : super core::Object::•()
+ ;
+ method bar() → core::int
+ return 42;
+}
+abstract class M extends core::Object /*isMixinDeclaration*/ {
+ set callOnAssignment(() → void f) → void {
+ f(){() → void};
+ }
+}
+abstract class _E1&_Enum&A extends core::_Enum implements self::A /*isAnonymousMixin,isEliminatedMixin,hasConstConstructor*/ {
+ const synthetic constructor •(core::int index, core::String _name) → self::_E1&_Enum&A
+ : super core::_Enum::•(index, _name)
+ ;
+ get foo() → core::String
+ return "foo";
+}
+class E1 extends self::_E1&_Enum&A /*isEnum*/ {
+ static const field core::List<self::E1> values = #C7;
+ static const field self::E1 one = #C3;
+ static const field self::E1 two = #C6;
+ const constructor •(core::int index, core::String name) → self::E1
+ : super self::_E1&_Enum&A::•(index, name)
+ ;
+ method toString() → core::String
+ ;
+}
+abstract class _E2&_Enum&A extends core::_Enum implements self::A /*isAnonymousMixin,isEliminatedMixin,hasConstConstructor*/ {
+ const synthetic constructor •(core::int index, core::String _name) → self::_E2&_Enum&A
+ : super core::_Enum::•(index, _name)
+ ;
+ get foo() → core::String
+ return "foo";
+}
+abstract class _E2&_Enum&A&B extends self::_E2&_Enum&A implements self::B /*isAnonymousMixin,isEliminatedMixin,hasConstConstructor*/ {
+ const synthetic constructor •(core::int index, core::String _name) → self::_E2&_Enum&A&B
+ : super self::_E2&_Enum&A::•(index, _name)
+ ;
+ method bar() → core::int
+ return 42;
+}
+class E2 extends self::_E2&_Enum&A&B /*isEnum*/ {
+ static const field core::List<self::E2> values = #C10;
+ static const field self::E2 one = #C8;
+ static const field self::E2 two = #C9;
+ const constructor •(core::int index, core::String name) → self::E2
+ : super self::_E2&_Enum&A&B::•(index, name)
+ ;
+ method toString() → core::String
+ ;
+}
+abstract class _E3&_Enum&M extends core::_Enum implements self::M /*isAnonymousMixin,isEliminatedMixin,hasConstConstructor*/ {
+ const synthetic constructor •(core::int index, core::String _name) → self::_E3&_Enum&M
+ : super core::_Enum::•(index, _name)
+ ;
+ set callOnAssignment(() → void f) → void {
+ f(){() → void};
+ }
+}
+class E3 extends self::_E3&_Enum&M /*isEnum*/ {
+ static const field core::List<self::E3> values = #C13;
+ static const field self::E3 one = #C11;
+ static const field self::E3 two = #C12;
+ const constructor •(core::int index, core::String name) → self::E3
+ : super self::_E3&_Enum&M::•(index, name)
+ ;
+ method toString() → core::String
+ ;
+}
+static method expectEquals(dynamic x, dynamic y) → dynamic {
+ if(!(x =={core::Object::==}{(core::Object) → core::bool} y)) {
+ throw "Expected '${x}' and '${y}' to be equal.";
+ }
+}
+static method expectThrows(() → void f) → dynamic {
+ try {
+ f(){() → void};
+ throw "Expected function to throw.";
+ }
+ on core::Object catch(final core::Object e) {
+ }
+}
+static method throwOnCall() → void {
+ throw 42;
+}
+static method main() → dynamic {
+ self::expectEquals(#C3.{self::_E1&_Enum&A::foo}{core::String}, "foo");
+ self::expectEquals(#C6.{self::_E1&_Enum&A::foo}{core::String}, "foo");
+ self::expectEquals(#C8.{self::_E2&_Enum&A::foo}{core::String}, "foo");
+ self::expectEquals(#C9.{self::_E2&_Enum&A::foo}{core::String}, "foo");
+ self::expectEquals(#C8.{self::_E2&_Enum&A&B::bar}(){() → core::int}, "bar");
+ self::expectEquals(#C9.{self::_E2&_Enum&A&B::bar}(){() → core::int}, "bar");
+ self::expectThrows(#C11.{self::_E3&_Enum&M::callOnAssignment} = #C14);
+ self::expectThrows(#C12.{self::_E3&_Enum&M::callOnAssignment} = #C14);
+}
+
+constants {
+ #C1 = 0
+ #C2 = "one"
+ #C3 = self::E1 {index:#C1, _name:#C2}
+ #C4 = 1
+ #C5 = "two"
+ #C6 = self::E1 {index:#C4, _name:#C5}
+ #C7 = <self::E1>[#C3, #C6]
+ #C8 = self::E2 {index:#C1, _name:#C2}
+ #C9 = self::E2 {index:#C4, _name:#C5}
+ #C10 = <self::E2>[#C8, #C9]
+ #C11 = self::E3 {index:#C1, _name:#C2}
+ #C12 = self::E3 {index:#C4, _name:#C5}
+ #C13 = <self::E3>[#C11, #C12]
+ #C14 = static-tearoff self::throwOnCall
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///simple_mixins.dart:
+- E1. (from org-dartlang-testcase:///simple_mixins.dart:19:6)
+- _E1&_Enum&A. (from org-dartlang-testcase:///simple_mixins.dart:19:6)
+- _Enum. (from org-dartlang-sdk:///sdk/lib/core/enum.dart:76:9)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
+- E2. (from org-dartlang-testcase:///simple_mixins.dart:21:6)
+- _E2&_Enum&A&B. (from org-dartlang-testcase:///simple_mixins.dart:21:6)
+- _E2&_Enum&A. (from org-dartlang-testcase:///simple_mixins.dart:21:6)
+- E3. (from org-dartlang-testcase:///simple_mixins.dart:23:6)
+- _E3&_Enum&M. (from org-dartlang-testcase:///simple_mixins.dart:23:6)
diff --git a/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.textual_outline.expect b/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.textual_outline.expect
new file mode 100644
index 0000000..8e3abd8
--- /dev/null
+++ b/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.textual_outline.expect
@@ -0,0 +1,16 @@
+class A {
+ String get foo => "foo";
+}
+class B {
+ int bar() => 42;
+}
+mixin M {
+ void set callOnAssignment(void Function() f) {}
+}
+enum E1 with A { one, two }
+enum E2 with A, B { one, two }
+enum E3 with M { one, two }
+expectEquals(x, y) {}
+expectThrows(void Function() f) {}
+void throwOnCall() {}
+main() {}
diff --git a/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.weak.expect b/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.weak.expect
new file mode 100644
index 0000000..82c66e8
--- /dev/null
+++ b/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.weak.expect
@@ -0,0 +1,137 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+ synthetic constructor •() → self::A
+ : super core::Object::•()
+ ;
+ get foo() → core::String
+ return "foo";
+}
+class B extends core::Object {
+ synthetic constructor •() → self::B
+ : super core::Object::•()
+ ;
+ method bar() → core::int
+ return 42;
+}
+abstract class M extends core::Object /*isMixinDeclaration*/ {
+ set callOnAssignment(() → void f) → void {
+ f(){() → void};
+ }
+}
+abstract class _E1&_Enum&A = core::_Enum with self::A /*isAnonymousMixin,hasConstConstructor*/ {
+ const synthetic constructor •(core::int index, core::String _name) → self::_E1&_Enum&A
+ : super core::_Enum::•(index, _name)
+ ;
+ mixin-super-stub get foo() → core::String
+ return super.{self::A::foo};
+}
+class E1 extends self::_E1&_Enum&A /*isEnum*/ {
+ static const field core::List<self::E1> values = #C7;
+ static const field self::E1 one = #C3;
+ static const field self::E1 two = #C6;
+ const constructor •(core::int index, core::String name) → self::E1
+ : super self::_E1&_Enum&A::•(index, name)
+ ;
+ method toString() → core::String
+ ;
+}
+abstract class _E2&_Enum&A = core::_Enum with self::A /*isAnonymousMixin,hasConstConstructor*/ {
+ const synthetic constructor •(core::int index, core::String _name) → self::_E2&_Enum&A
+ : super core::_Enum::•(index, _name)
+ ;
+ mixin-super-stub get foo() → core::String
+ return super.{self::A::foo};
+}
+abstract class _E2&_Enum&A&B = self::_E2&_Enum&A with self::B /*isAnonymousMixin,hasConstConstructor*/ {
+ const synthetic constructor •(core::int index, core::String _name) → self::_E2&_Enum&A&B
+ : super self::_E2&_Enum&A::•(index, _name)
+ ;
+ mixin-super-stub method bar() → core::int
+ return super.{self::B::bar}();
+}
+class E2 extends self::_E2&_Enum&A&B /*isEnum*/ {
+ static const field core::List<self::E2> values = #C10;
+ static const field self::E2 one = #C8;
+ static const field self::E2 two = #C9;
+ const constructor •(core::int index, core::String name) → self::E2
+ : super self::_E2&_Enum&A&B::•(index, name)
+ ;
+ method toString() → core::String
+ ;
+}
+abstract class _E3&_Enum&M = core::_Enum with self::M /*isAnonymousMixin,hasConstConstructor*/ {
+ const synthetic constructor •(core::int index, core::String _name) → self::_E3&_Enum&M
+ : super core::_Enum::•(index, _name)
+ ;
+ mixin-super-stub set callOnAssignment(() → void f) → void
+ return super.{self::M::callOnAssignment} = f;
+}
+class E3 extends self::_E3&_Enum&M /*isEnum*/ {
+ static const field core::List<self::E3> values = #C13;
+ static const field self::E3 one = #C11;
+ static const field self::E3 two = #C12;
+ const constructor •(core::int index, core::String name) → self::E3
+ : super self::_E3&_Enum&M::•(index, name)
+ ;
+ method toString() → core::String
+ ;
+}
+static method expectEquals(dynamic x, dynamic y) → dynamic {
+ if(!(x =={core::Object::==}{(core::Object) → core::bool} y)) {
+ throw "Expected '${x}' and '${y}' to be equal.";
+ }
+}
+static method expectThrows(() → void f) → dynamic {
+ try {
+ f(){() → void};
+ throw "Expected function to throw.";
+ }
+ on core::Object catch(final core::Object e) {
+ }
+}
+static method throwOnCall() → void {
+ throw 42;
+}
+static method main() → dynamic {
+ self::expectEquals(#C3.{self::_E1&_Enum&A::foo}{core::String}, "foo");
+ self::expectEquals(#C6.{self::_E1&_Enum&A::foo}{core::String}, "foo");
+ self::expectEquals(#C8.{self::_E2&_Enum&A::foo}{core::String}, "foo");
+ self::expectEquals(#C9.{self::_E2&_Enum&A::foo}{core::String}, "foo");
+ self::expectEquals(#C8.{self::_E2&_Enum&A&B::bar}(){() → core::int}, "bar");
+ self::expectEquals(#C9.{self::_E2&_Enum&A&B::bar}(){() → core::int}, "bar");
+ self::expectThrows(#C11.{self::_E3&_Enum&M::callOnAssignment} = #C14);
+ self::expectThrows(#C12.{self::_E3&_Enum&M::callOnAssignment} = #C14);
+}
+
+constants {
+ #C1 = 0
+ #C2 = "one"
+ #C3 = self::E1 {index:#C1, _name:#C2}
+ #C4 = 1
+ #C5 = "two"
+ #C6 = self::E1 {index:#C4, _name:#C5}
+ #C7 = <self::E1*>[#C3, #C6]
+ #C8 = self::E2 {index:#C1, _name:#C2}
+ #C9 = self::E2 {index:#C4, _name:#C5}
+ #C10 = <self::E2*>[#C8, #C9]
+ #C11 = self::E3 {index:#C1, _name:#C2}
+ #C12 = self::E3 {index:#C4, _name:#C5}
+ #C13 = <self::E3*>[#C11, #C12]
+ #C14 = static-tearoff self::throwOnCall
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///simple_mixins.dart:
+- E1. (from org-dartlang-testcase:///simple_mixins.dart:19:6)
+- _E1&_Enum&A. (from org-dartlang-testcase:///simple_mixins.dart:19:6)
+- _Enum. (from org-dartlang-sdk:///sdk/lib/core/enum.dart:76:9)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
+- E2. (from org-dartlang-testcase:///simple_mixins.dart:21:6)
+- _E2&_Enum&A&B. (from org-dartlang-testcase:///simple_mixins.dart:21:6)
+- _E2&_Enum&A. (from org-dartlang-testcase:///simple_mixins.dart:21:6)
+- E3. (from org-dartlang-testcase:///simple_mixins.dart:23:6)
+- _E3&_Enum&M. (from org-dartlang-testcase:///simple_mixins.dart:23:6)
diff --git a/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.weak.modular.expect b/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.weak.modular.expect
new file mode 100644
index 0000000..82c66e8
--- /dev/null
+++ b/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.weak.modular.expect
@@ -0,0 +1,137 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+ synthetic constructor •() → self::A
+ : super core::Object::•()
+ ;
+ get foo() → core::String
+ return "foo";
+}
+class B extends core::Object {
+ synthetic constructor •() → self::B
+ : super core::Object::•()
+ ;
+ method bar() → core::int
+ return 42;
+}
+abstract class M extends core::Object /*isMixinDeclaration*/ {
+ set callOnAssignment(() → void f) → void {
+ f(){() → void};
+ }
+}
+abstract class _E1&_Enum&A = core::_Enum with self::A /*isAnonymousMixin,hasConstConstructor*/ {
+ const synthetic constructor •(core::int index, core::String _name) → self::_E1&_Enum&A
+ : super core::_Enum::•(index, _name)
+ ;
+ mixin-super-stub get foo() → core::String
+ return super.{self::A::foo};
+}
+class E1 extends self::_E1&_Enum&A /*isEnum*/ {
+ static const field core::List<self::E1> values = #C7;
+ static const field self::E1 one = #C3;
+ static const field self::E1 two = #C6;
+ const constructor •(core::int index, core::String name) → self::E1
+ : super self::_E1&_Enum&A::•(index, name)
+ ;
+ method toString() → core::String
+ ;
+}
+abstract class _E2&_Enum&A = core::_Enum with self::A /*isAnonymousMixin,hasConstConstructor*/ {
+ const synthetic constructor •(core::int index, core::String _name) → self::_E2&_Enum&A
+ : super core::_Enum::•(index, _name)
+ ;
+ mixin-super-stub get foo() → core::String
+ return super.{self::A::foo};
+}
+abstract class _E2&_Enum&A&B = self::_E2&_Enum&A with self::B /*isAnonymousMixin,hasConstConstructor*/ {
+ const synthetic constructor •(core::int index, core::String _name) → self::_E2&_Enum&A&B
+ : super self::_E2&_Enum&A::•(index, _name)
+ ;
+ mixin-super-stub method bar() → core::int
+ return super.{self::B::bar}();
+}
+class E2 extends self::_E2&_Enum&A&B /*isEnum*/ {
+ static const field core::List<self::E2> values = #C10;
+ static const field self::E2 one = #C8;
+ static const field self::E2 two = #C9;
+ const constructor •(core::int index, core::String name) → self::E2
+ : super self::_E2&_Enum&A&B::•(index, name)
+ ;
+ method toString() → core::String
+ ;
+}
+abstract class _E3&_Enum&M = core::_Enum with self::M /*isAnonymousMixin,hasConstConstructor*/ {
+ const synthetic constructor •(core::int index, core::String _name) → self::_E3&_Enum&M
+ : super core::_Enum::•(index, _name)
+ ;
+ mixin-super-stub set callOnAssignment(() → void f) → void
+ return super.{self::M::callOnAssignment} = f;
+}
+class E3 extends self::_E3&_Enum&M /*isEnum*/ {
+ static const field core::List<self::E3> values = #C13;
+ static const field self::E3 one = #C11;
+ static const field self::E3 two = #C12;
+ const constructor •(core::int index, core::String name) → self::E3
+ : super self::_E3&_Enum&M::•(index, name)
+ ;
+ method toString() → core::String
+ ;
+}
+static method expectEquals(dynamic x, dynamic y) → dynamic {
+ if(!(x =={core::Object::==}{(core::Object) → core::bool} y)) {
+ throw "Expected '${x}' and '${y}' to be equal.";
+ }
+}
+static method expectThrows(() → void f) → dynamic {
+ try {
+ f(){() → void};
+ throw "Expected function to throw.";
+ }
+ on core::Object catch(final core::Object e) {
+ }
+}
+static method throwOnCall() → void {
+ throw 42;
+}
+static method main() → dynamic {
+ self::expectEquals(#C3.{self::_E1&_Enum&A::foo}{core::String}, "foo");
+ self::expectEquals(#C6.{self::_E1&_Enum&A::foo}{core::String}, "foo");
+ self::expectEquals(#C8.{self::_E2&_Enum&A::foo}{core::String}, "foo");
+ self::expectEquals(#C9.{self::_E2&_Enum&A::foo}{core::String}, "foo");
+ self::expectEquals(#C8.{self::_E2&_Enum&A&B::bar}(){() → core::int}, "bar");
+ self::expectEquals(#C9.{self::_E2&_Enum&A&B::bar}(){() → core::int}, "bar");
+ self::expectThrows(#C11.{self::_E3&_Enum&M::callOnAssignment} = #C14);
+ self::expectThrows(#C12.{self::_E3&_Enum&M::callOnAssignment} = #C14);
+}
+
+constants {
+ #C1 = 0
+ #C2 = "one"
+ #C3 = self::E1 {index:#C1, _name:#C2}
+ #C4 = 1
+ #C5 = "two"
+ #C6 = self::E1 {index:#C4, _name:#C5}
+ #C7 = <self::E1*>[#C3, #C6]
+ #C8 = self::E2 {index:#C1, _name:#C2}
+ #C9 = self::E2 {index:#C4, _name:#C5}
+ #C10 = <self::E2*>[#C8, #C9]
+ #C11 = self::E3 {index:#C1, _name:#C2}
+ #C12 = self::E3 {index:#C4, _name:#C5}
+ #C13 = <self::E3*>[#C11, #C12]
+ #C14 = static-tearoff self::throwOnCall
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///simple_mixins.dart:
+- E1. (from org-dartlang-testcase:///simple_mixins.dart:19:6)
+- _E1&_Enum&A. (from org-dartlang-testcase:///simple_mixins.dart:19:6)
+- _Enum. (from org-dartlang-sdk:///sdk/lib/core/enum.dart:76:9)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
+- E2. (from org-dartlang-testcase:///simple_mixins.dart:21:6)
+- _E2&_Enum&A&B. (from org-dartlang-testcase:///simple_mixins.dart:21:6)
+- _E2&_Enum&A. (from org-dartlang-testcase:///simple_mixins.dart:21:6)
+- E3. (from org-dartlang-testcase:///simple_mixins.dart:23:6)
+- _E3&_Enum&M. (from org-dartlang-testcase:///simple_mixins.dart:23:6)
diff --git a/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.weak.outline.expect b/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.weak.outline.expect
new file mode 100644
index 0000000..abd958f
--- /dev/null
+++ b/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.weak.outline.expect
@@ -0,0 +1,96 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+ synthetic constructor •() → self::A
+ ;
+ get foo() → core::String
+ ;
+}
+class B extends core::Object {
+ synthetic constructor •() → self::B
+ ;
+ method bar() → core::int
+ ;
+}
+abstract class M extends core::Object /*isMixinDeclaration*/ {
+ set callOnAssignment(() → void f) → void
+ ;
+}
+abstract class _E1&_Enum&A = core::_Enum with self::A /*isAnonymousMixin,hasConstConstructor*/ {
+ const synthetic constructor •(core::int index, core::String _name) → self::_E1&_Enum&A
+ : super core::_Enum::•(index, _name)
+ ;
+ mixin-super-stub get foo() → core::String
+ return super.{self::A::foo};
+}
+class E1 extends self::_E1&_Enum&A /*isEnum*/ {
+ static const field core::List<self::E1> values = const <self::E1>[self::E1::one, self::E1::two];
+ static const field self::E1 one = const self::E1::•(0, "one");
+ static const field self::E1 two = const self::E1::•(1, "two");
+ const constructor •(core::int index, core::String name) → self::E1
+ ;
+ method toString() → core::String
+ ;
+}
+abstract class _E2&_Enum&A = core::_Enum with self::A /*isAnonymousMixin,hasConstConstructor*/ {
+ const synthetic constructor •(core::int index, core::String _name) → self::_E2&_Enum&A
+ : super core::_Enum::•(index, _name)
+ ;
+ mixin-super-stub get foo() → core::String
+ return super.{self::A::foo};
+}
+abstract class _E2&_Enum&A&B = self::_E2&_Enum&A with self::B /*isAnonymousMixin,hasConstConstructor*/ {
+ const synthetic constructor •(core::int index, core::String _name) → self::_E2&_Enum&A&B
+ : super self::_E2&_Enum&A::•(index, _name)
+ ;
+ mixin-super-stub method bar() → core::int
+ return super.{self::B::bar}();
+}
+class E2 extends self::_E2&_Enum&A&B /*isEnum*/ {
+ static const field core::List<self::E2> values = const <self::E2>[self::E2::one, self::E2::two];
+ static const field self::E2 one = const self::E2::•(0, "one");
+ static const field self::E2 two = const self::E2::•(1, "two");
+ const constructor •(core::int index, core::String name) → self::E2
+ ;
+ method toString() → core::String
+ ;
+}
+abstract class _E3&_Enum&M = core::_Enum with self::M /*isAnonymousMixin,hasConstConstructor*/ {
+ const synthetic constructor •(core::int index, core::String _name) → self::_E3&_Enum&M
+ : super core::_Enum::•(index, _name)
+ ;
+ mixin-super-stub set callOnAssignment(() → void f) → void
+ return super.{self::M::callOnAssignment} = f;
+}
+class E3 extends self::_E3&_Enum&M /*isEnum*/ {
+ static const field core::List<self::E3> values = const <self::E3>[self::E3::one, self::E3::two];
+ static const field self::E3 one = const self::E3::•(0, "one");
+ static const field self::E3 two = const self::E3::•(1, "two");
+ const constructor •(core::int index, core::String name) → self::E3
+ ;
+ method toString() → core::String
+ ;
+}
+static method expectEquals(dynamic x, dynamic y) → dynamic
+ ;
+static method expectThrows(() → void f) → dynamic
+ ;
+static method throwOnCall() → void
+ ;
+static method main() → dynamic
+ ;
+
+
+Extra constant evaluation status:
+Evaluated: ListLiteral @ org-dartlang-testcase:///simple_mixins.dart:19:6 -> ListConstant(const <E1*>[const E1{}, const E1{}])
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///simple_mixins.dart:19:18 -> InstanceConstant(const E1{})
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///simple_mixins.dart:19:23 -> InstanceConstant(const E1{})
+Evaluated: ListLiteral @ org-dartlang-testcase:///simple_mixins.dart:21:6 -> ListConstant(const <E2*>[const E2{}, const E2{}])
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///simple_mixins.dart:21:21 -> InstanceConstant(const E2{})
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///simple_mixins.dart:21:26 -> InstanceConstant(const E2{})
+Evaluated: ListLiteral @ org-dartlang-testcase:///simple_mixins.dart:23:6 -> ListConstant(const <E3*>[const E3{}, const E3{}])
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///simple_mixins.dart:23:18 -> InstanceConstant(const E3{})
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///simple_mixins.dart:23:23 -> InstanceConstant(const E3{})
+Extra constant evaluation: evaluated: 22, effectively constant: 9
diff --git a/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.weak.transformed.expect b/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.weak.transformed.expect
new file mode 100644
index 0000000..af40a65
--- /dev/null
+++ b/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.weak.transformed.expect
@@ -0,0 +1,138 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+ synthetic constructor •() → self::A
+ : super core::Object::•()
+ ;
+ get foo() → core::String
+ return "foo";
+}
+class B extends core::Object {
+ synthetic constructor •() → self::B
+ : super core::Object::•()
+ ;
+ method bar() → core::int
+ return 42;
+}
+abstract class M extends core::Object /*isMixinDeclaration*/ {
+ set callOnAssignment(() → void f) → void {
+ f(){() → void};
+ }
+}
+abstract class _E1&_Enum&A extends core::_Enum implements self::A /*isAnonymousMixin,isEliminatedMixin,hasConstConstructor*/ {
+ const synthetic constructor •(core::int index, core::String _name) → self::_E1&_Enum&A
+ : super core::_Enum::•(index, _name)
+ ;
+ get foo() → core::String
+ return "foo";
+}
+class E1 extends self::_E1&_Enum&A /*isEnum*/ {
+ static const field core::List<self::E1> values = #C7;
+ static const field self::E1 one = #C3;
+ static const field self::E1 two = #C6;
+ const constructor •(core::int index, core::String name) → self::E1
+ : super self::_E1&_Enum&A::•(index, name)
+ ;
+ method toString() → core::String
+ ;
+}
+abstract class _E2&_Enum&A extends core::_Enum implements self::A /*isAnonymousMixin,isEliminatedMixin,hasConstConstructor*/ {
+ const synthetic constructor •(core::int index, core::String _name) → self::_E2&_Enum&A
+ : super core::_Enum::•(index, _name)
+ ;
+ get foo() → core::String
+ return "foo";
+}
+abstract class _E2&_Enum&A&B extends self::_E2&_Enum&A implements self::B /*isAnonymousMixin,isEliminatedMixin,hasConstConstructor*/ {
+ const synthetic constructor •(core::int index, core::String _name) → self::_E2&_Enum&A&B
+ : super self::_E2&_Enum&A::•(index, _name)
+ ;
+ method bar() → core::int
+ return 42;
+}
+class E2 extends self::_E2&_Enum&A&B /*isEnum*/ {
+ static const field core::List<self::E2> values = #C10;
+ static const field self::E2 one = #C8;
+ static const field self::E2 two = #C9;
+ const constructor •(core::int index, core::String name) → self::E2
+ : super self::_E2&_Enum&A&B::•(index, name)
+ ;
+ method toString() → core::String
+ ;
+}
+abstract class _E3&_Enum&M extends core::_Enum implements self::M /*isAnonymousMixin,isEliminatedMixin,hasConstConstructor*/ {
+ const synthetic constructor •(core::int index, core::String _name) → self::_E3&_Enum&M
+ : super core::_Enum::•(index, _name)
+ ;
+ set callOnAssignment(() → void f) → void {
+ f(){() → void};
+ }
+}
+class E3 extends self::_E3&_Enum&M /*isEnum*/ {
+ static const field core::List<self::E3> values = #C13;
+ static const field self::E3 one = #C11;
+ static const field self::E3 two = #C12;
+ const constructor •(core::int index, core::String name) → self::E3
+ : super self::_E3&_Enum&M::•(index, name)
+ ;
+ method toString() → core::String
+ ;
+}
+static method expectEquals(dynamic x, dynamic y) → dynamic {
+ if(!(x =={core::Object::==}{(core::Object) → core::bool} y)) {
+ throw "Expected '${x}' and '${y}' to be equal.";
+ }
+}
+static method expectThrows(() → void f) → dynamic {
+ try {
+ f(){() → void};
+ throw "Expected function to throw.";
+ }
+ on core::Object catch(final core::Object e) {
+ }
+}
+static method throwOnCall() → void {
+ throw 42;
+}
+static method main() → dynamic {
+ self::expectEquals(#C3.{self::_E1&_Enum&A::foo}{core::String}, "foo");
+ self::expectEquals(#C6.{self::_E1&_Enum&A::foo}{core::String}, "foo");
+ self::expectEquals(#C8.{self::_E2&_Enum&A::foo}{core::String}, "foo");
+ self::expectEquals(#C9.{self::_E2&_Enum&A::foo}{core::String}, "foo");
+ self::expectEquals(#C8.{self::_E2&_Enum&A&B::bar}(){() → core::int}, "bar");
+ self::expectEquals(#C9.{self::_E2&_Enum&A&B::bar}(){() → core::int}, "bar");
+ self::expectThrows(#C11.{self::_E3&_Enum&M::callOnAssignment} = #C14);
+ self::expectThrows(#C12.{self::_E3&_Enum&M::callOnAssignment} = #C14);
+}
+
+constants {
+ #C1 = 0
+ #C2 = "one"
+ #C3 = self::E1 {index:#C1, _name:#C2}
+ #C4 = 1
+ #C5 = "two"
+ #C6 = self::E1 {index:#C4, _name:#C5}
+ #C7 = <self::E1*>[#C3, #C6]
+ #C8 = self::E2 {index:#C1, _name:#C2}
+ #C9 = self::E2 {index:#C4, _name:#C5}
+ #C10 = <self::E2*>[#C8, #C9]
+ #C11 = self::E3 {index:#C1, _name:#C2}
+ #C12 = self::E3 {index:#C4, _name:#C5}
+ #C13 = <self::E3*>[#C11, #C12]
+ #C14 = static-tearoff self::throwOnCall
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///simple_mixins.dart:
+- E1. (from org-dartlang-testcase:///simple_mixins.dart:19:6)
+- _E1&_Enum&A. (from org-dartlang-testcase:///simple_mixins.dart:19:6)
+- _Enum. (from org-dartlang-sdk:///sdk/lib/core/enum.dart:76:9)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
+- E2. (from org-dartlang-testcase:///simple_mixins.dart:21:6)
+- _E2&_Enum&A&B. (from org-dartlang-testcase:///simple_mixins.dart:21:6)
+- _E2&_Enum&A. (from org-dartlang-testcase:///simple_mixins.dart:21:6)
+- E3. (from org-dartlang-testcase:///simple_mixins.dart:23:6)
+- _E3&_Enum&M. (from org-dartlang-testcase:///simple_mixins.dart:23:6)
diff --git a/pkg/front_end/testcases/strong.status b/pkg/front_end/testcases/strong.status
index 2aef19d..f57bbdf 100644
--- a/pkg/front_end/testcases/strong.status
+++ b/pkg/front_end/testcases/strong.status
@@ -15,6 +15,7 @@
constructor_tearoffs/call_instantiation: TypeCheckError
constructor_tearoffs/lowering/invalid_redirect: VerificationError
+enhanced_enums/simple_mixins: RuntimeError
extension_types/access_setter_as_getter: ExpectationFileMismatchSerialized # Expected.
extension_types/call_not_get: ExpectationFileMismatchSerialized # Expected.
extension_types/extension_on_nullable: ExpectationFileMismatchSerialized # Expected.
diff --git a/pkg/front_end/testcases/text_serialization.status b/pkg/front_end/testcases/text_serialization.status
index b66d7f8..5a838fa 100644
--- a/pkg/front_end/testcases/text_serialization.status
+++ b/pkg/front_end/testcases/text_serialization.status
@@ -8,6 +8,7 @@
constructor_tearoffs/call_instantiation: TypeCheckError
constructor_tearoffs/lowering/invalid_redirect: VerificationError
+enhanced_enums/simple_mixins: RuntimeError
extension_types/access_setter_as_getter: ExpectationFileMismatchSerialized # Expected.
extension_types/call_not_get: ExpectationFileMismatchSerialized # Expected.
extension_types/extension_on_nullable: ExpectationFileMismatchSerialized # Expected.
diff --git a/pkg/front_end/testcases/textual_outline.status b/pkg/front_end/testcases/textual_outline.status
index 6736b08..0228fca 100644
--- a/pkg/front_end/testcases/textual_outline.status
+++ b/pkg/front_end/testcases/textual_outline.status
@@ -35,6 +35,7 @@
enhanced_enums/qualified_names_with_no_type_arguments: FormatterCrash
enhanced_enums/redirecting_initializers: FormatterCrash
enhanced_enums/simple_fields: FormatterCrash
+enhanced_enums/simple_mixins: FormatterCrash
extension_types/basic_show: FormatterCrash
extension_types/call_not_get: FormatterCrash
extension_types/keyword_in_show_hide_element: FormatterCrash
diff --git a/pkg/front_end/testcases/weak.status b/pkg/front_end/testcases/weak.status
index 8898c1a..f75594a 100644
--- a/pkg/front_end/testcases/weak.status
+++ b/pkg/front_end/testcases/weak.status
@@ -22,6 +22,7 @@
constructor_tearoffs/call_instantiation: TypeCheckError
constructor_tearoffs/lowering/invalid_redirect: VerificationError
+enhanced_enums/simple_mixins: RuntimeError
extension_types/access_setter_as_getter: ExpectationFileMismatchSerialized # Expected.
extension_types/call_not_get: ExpectationFileMismatchSerialized # Expected.
extension_types/extension_on_nullable: ExpectationFileMismatchSerialized # Expected.
diff --git a/runtime/vm/compiler/asm_intrinsifier_arm64.cc b/runtime/vm/compiler/asm_intrinsifier_arm64.cc
index f9c178a..4056cbf 100644
--- a/runtime/vm/compiler/asm_intrinsifier_arm64.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_arm64.cc
@@ -51,9 +51,8 @@
R1);
// Set the length field in the growable array object to 0.
- __ LoadImmediate(R1, 0);
__ StoreCompressedIntoObjectNoBarrier(
- R0, FieldAddress(R0, target::GrowableObjectArray::length_offset()), R1);
+ R0, FieldAddress(R0, target::GrowableObjectArray::length_offset()), ZR);
__ ret(); // Returns the newly allocated object in R0.
__ Bind(normal_ir_body);
diff --git a/runtime/vm/compiler/backend/il_arm64.cc b/runtime/vm/compiler/backend/il_arm64.cc
index 1c5673c..979c8ba 100644
--- a/runtime/vm/compiler/backend/il_arm64.cc
+++ b/runtime/vm/compiler/backend/il_arm64.cc
@@ -5286,10 +5286,16 @@
__ CompareObjectRegisters(result_mod, ZR);
__ b(&done, GE);
// Result is negative, adjust it.
- __ CompareObjectRegisters(right, ZR);
- __ sub(TMP2, result_mod, compiler::Operand(right), compiler::kObjectBytes);
- __ add(TMP, result_mod, compiler::Operand(right), compiler::kObjectBytes);
- __ csel(result_mod, TMP, TMP2, GE);
+ if (RangeUtils::IsNegative(divisor_range())) {
+ __ sub(result_mod, result_mod, compiler::Operand(right));
+ } else if (RangeUtils::IsPositive(divisor_range())) {
+ __ add(result_mod, result_mod, compiler::Operand(right));
+ } else {
+ __ CompareObjectRegisters(right, ZR);
+ __ sub(TMP2, result_mod, compiler::Operand(right), compiler::kObjectBytes);
+ __ add(TMP, result_mod, compiler::Operand(right), compiler::kObjectBytes);
+ __ csel(result_mod, TMP, TMP2, GE);
+ }
__ Bind(&done);
}
@@ -5634,8 +5640,7 @@
// Handle modulo/division by zero exception on slow path.
if (slow_path->has_divide_by_zero()) {
- __ CompareRegisters(right, ZR);
- __ b(slow_path->entry_label(), EQ);
+ __ cbz(slow_path->entry_label(), right);
}
// Perform actual operation
diff --git a/runtime/vm/compiler/backend/range_analysis.cc b/runtime/vm/compiler/backend/range_analysis.cc
index 7d02f87..20c4ba0 100644
--- a/runtime/vm/compiler/backend/range_analysis.cc
+++ b/runtime/vm/compiler/backend/range_analysis.cc
@@ -2082,6 +2082,10 @@
return OnlyGreaterThanOrEqualTo(0);
}
+bool Range::IsNegative() const {
+ return OnlyLessThanOrEqualTo(-1);
+}
+
bool Range::OnlyLessThanOrEqualTo(int64_t val) const {
const RangeBoundary upper_bound = max().UpperBound();
return !upper_bound.IsPositiveInfinity() &&
diff --git a/runtime/vm/compiler/backend/range_analysis.h b/runtime/vm/compiler/backend/range_analysis.h
index b0037f4..aa247c7 100644
--- a/runtime/vm/compiler/backend/range_analysis.h
+++ b/runtime/vm/compiler/backend/range_analysis.h
@@ -413,6 +413,9 @@
// [0, +inf]
bool IsPositive() const;
+ // [-inf, -1]
+ bool IsNegative() const;
+
// [-inf, val].
bool OnlyLessThanOrEqualTo(int64_t val) const;
@@ -560,6 +563,9 @@
static bool IsPositive(Range* range) {
return !Range::IsUnknown(range) && range->IsPositive();
}
+ static bool IsNegative(Range* range) {
+ return !Range::IsUnknown(range) && range->IsNegative();
+ }
static bool Overlaps(Range* range, intptr_t min, intptr_t max) {
return Range::IsUnknown(range) || range->Overlaps(min, max);
diff --git a/runtime/vm/compiler/stub_code_compiler_arm64.cc b/runtime/vm/compiler/stub_code_compiler_arm64.cc
index bcdc1f4..4d22204 100644
--- a/runtime/vm/compiler/stub_code_compiler_arm64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_arm64.cc
@@ -1961,15 +1961,15 @@
Label not_parameterized_case;
const Register kClsIdReg = R4;
- const Register kTypeOffestReg = R5;
+ const Register kTypeOffsetReg = R5;
__ ExtractClassIdFromTags(kClsIdReg, kTagsReg);
// Load class' type_arguments_field offset in words.
- __ LoadClassById(kTypeOffestReg, kClsIdReg);
+ __ LoadClassById(kTypeOffsetReg, kClsIdReg);
__ ldr(
- kTypeOffestReg,
- FieldAddress(kTypeOffestReg,
+ kTypeOffsetReg,
+ FieldAddress(kTypeOffsetReg,
target::Class::
host_type_arguments_field_offset_in_words_offset()),
kFourBytes);
@@ -1977,7 +1977,7 @@
// Set the type arguments in the new object.
__ StoreCompressedIntoObjectNoBarrier(
AllocateObjectABI::kResultReg,
- Address(AllocateObjectABI::kResultReg, kTypeOffestReg, UXTX,
+ Address(AllocateObjectABI::kResultReg, kTypeOffsetReg, UXTX,
Address::Scaled),
AllocateObjectABI::kTypeArgumentsReg);
@@ -3723,9 +3723,7 @@
/* R0: new object start as a tagged pointer. */
/* R1: new object end address. */
/* R2: iterator which initially points to the start of the variable */
- /* R3: scratch register. */
/* data area to be initialized. */
- __ mov(R3, ZR);
__ AddImmediate(R2, R0, target::TypedData::HeaderSize() - 1);
__ StoreInternalPointer(
R0, FieldAddress(R0, target::TypedDataBase::data_field_offset()), R2);
@@ -3733,7 +3731,7 @@
__ Bind(&init_loop);
__ cmp(R2, Operand(R1));
__ b(&done, CS);
- __ str(R3, Address(R2, 0));
+ __ str(ZR, Address(R2, 0));
__ add(R2, R2, Operand(target::kWordSize));
__ b(&init_loop);
__ Bind(&done);
diff --git a/sdk/lib/_internal/js_dev_runtime/private/string_helper.dart b/sdk/lib/_internal/js_dev_runtime/private/string_helper.dart
index 7a0de57..474042a 100644
--- a/sdk/lib/_internal/js_dev_runtime/private/string_helper.dart
+++ b/sdk/lib/_internal/js_dev_runtime/private/string_helper.dart
@@ -169,8 +169,15 @@
var re = regExpGetGlobalNative(pattern);
return stringReplaceJS(receiver, re, replacement);
} else {
- // TODO(floitsch): implement generic String.replace (with patterns).
- throw "String.replaceAll(Pattern) UNIMPLEMENTED";
+ int startIndex = 0;
+ StringBuffer result = StringBuffer();
+ for (Match match in pattern.allMatches(receiver)) {
+ result.write(substring2Unchecked(receiver, startIndex, match.start));
+ result.write(replacement);
+ startIndex = match.end;
+ }
+ result.write(substring1Unchecked(receiver, startIndex));
+ return result.toString();
}
}
diff --git a/sdk/lib/_internal/js_runtime/lib/string_helper.dart b/sdk/lib/_internal/js_runtime/lib/string_helper.dart
index 5e6c50d..9305f55 100644
--- a/sdk/lib/_internal/js_runtime/lib/string_helper.dart
+++ b/sdk/lib/_internal/js_runtime/lib/string_helper.dart
@@ -173,9 +173,21 @@
return stringReplaceJS(receiver, re, replacement);
}
+ return stringReplaceAllGeneral(receiver, pattern, replacement);
+}
+
+String stringReplaceAllGeneral(
+ String receiver, Pattern pattern, String replacement) {
checkNull(pattern);
- // TODO(floitsch): implement generic String.replace (with patterns).
- throw "String.replaceAll(Pattern) UNIMPLEMENTED";
+ int startIndex = 0;
+ StringBuffer result = StringBuffer();
+ for (Match match in pattern.allMatches(receiver)) {
+ result.write(substring2Unchecked(receiver, startIndex, match.start));
+ result.write(replacement);
+ startIndex = match.end;
+ }
+ result.write(substring1Unchecked(receiver, startIndex));
+ return result.toString();
}
/// Replaces all non-overlapping occurences of [pattern] in [receiver] with
diff --git a/tests/corelib/string_replace_all_common.dart b/tests/corelib/string_replace_all_common.dart
new file mode 100644
index 0000000..4bdbff0
--- /dev/null
+++ b/tests/corelib/string_replace_all_common.dart
@@ -0,0 +1,144 @@
+// Copyright (c) 2012, 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:expect/expect.dart";
+
+testAll(Pattern Function(Pattern) wrap) {
+ testReplaceAll(wrap);
+ testReplaceAllMapped(wrap);
+ testSplitMapJoin(wrap);
+}
+
+testReplaceAll(Pattern Function(Pattern) wrap) {
+ Expect.equals("aXXcaXXdae", "abcabdae".replaceAll(wrap("b"), "XX"));
+
+ // Test with the replaced string at the beginning.
+ Expect.equals("XXbcXXbdXXe", "abcabdae".replaceAll(wrap("a"), "XX"));
+
+ // Test with the replaced string at the end.
+ Expect.equals("abcabdaXX", "abcabdae".replaceAll(wrap("e"), "XX"));
+
+ // Test when there are no occurence of the string to replace.
+ Expect.equals("abcabdae", "abcabdae".replaceAll(wrap("f"), "XX"));
+
+ // Test when the string to change is the empty string.
+ Expect.equals("", "".replaceAll(wrap("from"), "to"));
+
+ // Test when the string to change is a substring of the string to
+ // replace.
+ Expect.equals("fro", "fro".replaceAll(wrap("from"), "to"));
+
+ // Test when the string to change is the replaced string.
+ Expect.equals("to", "from".replaceAll(wrap("from"), "to"));
+
+ // Test when matches are adjacent
+ Expect.equals("toto", "fromfrom".replaceAll(wrap("from"), "to"));
+
+ // Test when the string to change is the replacement string.
+ Expect.equals("to", "to".replaceAll(wrap("from"), "to"));
+
+ // Test replacing by the empty string.
+ Expect.equals("bcbde", "abcabdae".replaceAll(wrap("a"), ""));
+ Expect.equals("AB", "AfromB".replaceAll(wrap("from"), ""));
+
+ // Test changing the empty string.
+ Expect.equals("to", "".replaceAll(wrap(""), "to"));
+
+ // Test replacing the empty string.
+ Expect.equals("toAtoBtoCto", "ABC".replaceAll(wrap(""), "to"));
+
+ // Pattern strings containing RegExp metacharacters - these are not
+ // interpreted as RegExps.
+ Expect.equals(r"$$", "||".replaceAll(wrap("|"), r"$"));
+ Expect.equals(r"$$$$", "||".replaceAll(wrap("|"), r"$$"));
+ Expect.equals(r"x$|x", "x|.|x".replaceAll(wrap("|."), r"$"));
+ Expect.equals(r"$$", "..".replaceAll(wrap("."), r"$"));
+ Expect.equals(r"[$$$$]", "[..]".replaceAll(wrap("."), r"$$"));
+ Expect.equals(r"[$]", "[..]".replaceAll(wrap(".."), r"$"));
+ Expect.equals(r"$$", r"\\".replaceAll(wrap(r"\"), r"$"));
+}
+
+testReplaceAllMapped(Pattern Function(Pattern) wrap) {
+ String mark(Match m) => "[${m[0]}]";
+ Expect.equals("a[b]ca[b]dae", "abcabdae".replaceAllMapped(wrap("b"), mark));
+
+ // Test with the replaced string at the beginning.
+ Expect.equals("[a]bc[a]bd[a]e", "abcabdae".replaceAllMapped(wrap("a"), mark));
+
+ // Test with the replaced string at the end.
+ Expect.equals("abcabda[e]", "abcabdae".replaceAllMapped(wrap("e"), mark));
+
+ // Test when there are no occurence of the string to replace.
+ Expect.equals("abcabdae", "abcabdae".replaceAllMapped(wrap("f"), mark));
+
+ // Test when the string to change is the empty string.
+ Expect.equals("", "".replaceAllMapped(wrap("from"), mark));
+
+ // Test when the string to change is a substring of the string to
+ // replace.
+ Expect.equals("fro", "fro".replaceAllMapped(wrap("from"), mark));
+
+ // Test when matches are adjacent
+ Expect.equals(
+ "[from][from]", "fromfrom".replaceAllMapped(wrap("from"), mark));
+
+ // Test replacing by the empty string.
+ Expect.equals("bcbde", "abcabdae".replaceAllMapped(wrap("a"), (m) => ""));
+ Expect.equals("AB", "AfromB".replaceAllMapped(wrap("from"), (m) => ""));
+
+ // Test changing the empty string.
+ Expect.equals("[]", "".replaceAllMapped(wrap(""), mark));
+
+ // Test replacing the empty string.
+ Expect.equals("[]A[]B[]C[]", "ABC".replaceAllMapped(wrap(""), mark));
+}
+
+testSplitMapJoin(Pattern Function(Pattern) wrap) {
+ String mark(Match m) => "[${m[0]}]";
+ String rest(String s) => "<${s}>";
+
+ Expect.equals("<a>[b]<ca>[b]<dae>",
+ "abcabdae".splitMapJoin(wrap("b"), onMatch: mark, onNonMatch: rest));
+
+ // Test with the replaced string at the beginning.
+ Expect.equals("<>[a]<bc>[a]<bd>[a]<e>",
+ "abcabdae".splitMapJoin(wrap("a"), onMatch: mark, onNonMatch: rest));
+
+ // Test with the replaced string at the end.
+ Expect.equals("<abcabda>[e]<>",
+ "abcabdae".splitMapJoin(wrap("e"), onMatch: mark, onNonMatch: rest));
+
+ // Test when there are no occurence of the string to replace.
+ Expect.equals("<abcabdae>",
+ "abcabdae".splitMapJoin(wrap("f"), onMatch: mark, onNonMatch: rest));
+
+ // Test when the string to change is the empty string.
+ Expect.equals(
+ "<>", "".splitMapJoin(wrap("from"), onMatch: mark, onNonMatch: rest));
+
+ // Test when the string to change is a substring of the string to
+ // replace.
+ Expect.equals("<fro>",
+ "fro".splitMapJoin(wrap("from"), onMatch: mark, onNonMatch: rest));
+
+ // Test when matches are adjacent
+ Expect.equals("<>[from]<>[from]<>",
+ "fromfrom".splitMapJoin(wrap("from"), onMatch: mark, onNonMatch: rest));
+
+ // Test changing the empty string.
+ Expect.equals(
+ "<>[]<>", "".splitMapJoin(wrap(""), onMatch: mark, onNonMatch: rest));
+
+ // Test replacing the empty string.
+ Expect.equals("<>[]<A>[]<B>[]<C>[]<>",
+ "ABC".splitMapJoin(wrap(""), onMatch: mark, onNonMatch: rest));
+
+ // Test with only onMatch.
+ Expect.equals(
+ "[a]bc[a]bd[a]e", "abcabdae".splitMapJoin(wrap("a"), onMatch: mark));
+
+ // Test with only onNonMatch
+ Expect.equals(
+ "<>a<bc>a<bd>a<e>", "abcabdae".splitMapJoin(wrap("a"), onNonMatch: rest));
+}
diff --git a/tests/corelib/string_replace_all_pattern_test.dart b/tests/corelib/string_replace_all_pattern_test.dart
new file mode 100644
index 0000000..0ca86e7
--- /dev/null
+++ b/tests/corelib/string_replace_all_pattern_test.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2021, 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 "string_replace_all_common.dart";
+
+main() {
+ testAll(Wrapper.wrap);
+}
+
+/// A wrapper that is not recognizable as a String or RegExp.
+class Wrapper implements Pattern {
+ final Pattern _pattern;
+ Wrapper(this._pattern);
+
+ static Pattern wrap(Pattern p) => Wrapper(p);
+
+ Iterable<Match> allMatches(String string, [int start = 0]) =>
+ _pattern.allMatches(string, start);
+
+ Match? matchAsPrefix(String string, [int start = 0]) =>
+ _pattern.matchAsPrefix(string, start);
+
+ String toString() => "Wrap($_pattern)";
+}
diff --git a/tests/corelib/string_replace_all_test.dart b/tests/corelib/string_replace_all_test.dart
index 548ad62..b6eb43b 100644
--- a/tests/corelib/string_replace_all_test.dart
+++ b/tests/corelib/string_replace_all_test.dart
@@ -1,140 +1,9 @@
-// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
+// Copyright (c) 2022, 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:expect/expect.dart";
-
-testReplaceAll() {
- Expect.equals("aXXcaXXdae", "abcabdae".replaceAll("b", "XX"));
-
- // Test with the replaced string at the beginning.
- Expect.equals("XXbcXXbdXXe", "abcabdae".replaceAll("a", "XX"));
-
- // Test with the replaced string at the end.
- Expect.equals("abcabdaXX", "abcabdae".replaceAll("e", "XX"));
-
- // Test when there are no occurence of the string to replace.
- Expect.equals("abcabdae", "abcabdae".replaceAll("f", "XX"));
-
- // Test when the string to change is the empty string.
- Expect.equals("", "".replaceAll("from", "to"));
-
- // Test when the string to change is a substring of the string to
- // replace.
- Expect.equals("fro", "fro".replaceAll("from", "to"));
-
- // Test when the string to change is the replaced string.
- Expect.equals("to", "from".replaceAll("from", "to"));
-
- // Test when matches are adjacent
- Expect.equals("toto", "fromfrom".replaceAll("from", "to"));
-
- // Test when the string to change is the replacement string.
- Expect.equals("to", "to".replaceAll("from", "to"));
-
- // Test replacing by the empty string.
- Expect.equals("bcbde", "abcabdae".replaceAll("a", ""));
- Expect.equals("AB", "AfromB".replaceAll("from", ""));
-
- // Test changing the empty string.
- Expect.equals("to", "".replaceAll("", "to"));
-
- // Test replacing the empty string.
- Expect.equals("toAtoBtoCto", "ABC".replaceAll("", "to"));
-
- // Pattern strings containing RegExp metacharacters - these are not
- // interpreted as RegExps.
- Expect.equals(r"$$", "||".replaceAll("|", r"$"));
- Expect.equals(r"$$$$", "||".replaceAll("|", r"$$"));
- Expect.equals(r"x$|x", "x|.|x".replaceAll("|.", r"$"));
- Expect.equals(r"$$", "..".replaceAll(".", r"$"));
- Expect.equals(r"[$$$$]", "[..]".replaceAll(".", r"$$"));
- Expect.equals(r"[$]", "[..]".replaceAll("..", r"$"));
- Expect.equals(r"$$", r"\\".replaceAll(r"\", r"$"));
-}
-
-testReplaceAllMapped() {
- String mark(Match m) => "[${m[0]}]";
- Expect.equals("a[b]ca[b]dae", "abcabdae".replaceAllMapped("b", mark));
-
- // Test with the replaced string at the beginning.
- Expect.equals("[a]bc[a]bd[a]e", "abcabdae".replaceAllMapped("a", mark));
-
- // Test with the replaced string at the end.
- Expect.equals("abcabda[e]", "abcabdae".replaceAllMapped("e", mark));
-
- // Test when there are no occurence of the string to replace.
- Expect.equals("abcabdae", "abcabdae".replaceAllMapped("f", mark));
-
- // Test when the string to change is the empty string.
- Expect.equals("", "".replaceAllMapped("from", mark));
-
- // Test when the string to change is a substring of the string to
- // replace.
- Expect.equals("fro", "fro".replaceAllMapped("from", mark));
-
- // Test when matches are adjacent
- Expect.equals("[from][from]", "fromfrom".replaceAllMapped("from", mark));
-
- // Test replacing by the empty string.
- Expect.equals("bcbde", "abcabdae".replaceAllMapped("a", (m) => ""));
- Expect.equals("AB", "AfromB".replaceAllMapped("from", (m) => ""));
-
- // Test changing the empty string.
- Expect.equals("[]", "".replaceAllMapped("", mark));
-
- // Test replacing the empty string.
- Expect.equals("[]A[]B[]C[]", "ABC".replaceAllMapped("", mark));
-}
-
-testSplitMapJoin() {
- String mark(Match m) => "[${m[0]}]";
- String wrap(String s) => "<${s}>";
-
- Expect.equals("<a>[b]<ca>[b]<dae>",
- "abcabdae".splitMapJoin("b", onMatch: mark, onNonMatch: wrap));
-
- // Test with the replaced string at the beginning.
- Expect.equals("<>[a]<bc>[a]<bd>[a]<e>",
- "abcabdae".splitMapJoin("a", onMatch: mark, onNonMatch: wrap));
-
- // Test with the replaced string at the end.
- Expect.equals("<abcabda>[e]<>",
- "abcabdae".splitMapJoin("e", onMatch: mark, onNonMatch: wrap));
-
- // Test when there are no occurence of the string to replace.
- Expect.equals("<abcabdae>",
- "abcabdae".splitMapJoin("f", onMatch: mark, onNonMatch: wrap));
-
- // Test when the string to change is the empty string.
- Expect.equals("<>", "".splitMapJoin("from", onMatch: mark, onNonMatch: wrap));
-
- // Test when the string to change is a substring of the string to
- // replace.
- Expect.equals(
- "<fro>", "fro".splitMapJoin("from", onMatch: mark, onNonMatch: wrap));
-
- // Test when matches are adjacent
- Expect.equals("<>[from]<>[from]<>",
- "fromfrom".splitMapJoin("from", onMatch: mark, onNonMatch: wrap));
-
- // Test changing the empty string.
- Expect.equals("<>[]<>", "".splitMapJoin("", onMatch: mark, onNonMatch: wrap));
-
- // Test replacing the empty string.
- Expect.equals("<>[]<A>[]<B>[]<C>[]<>",
- "ABC".splitMapJoin("", onMatch: mark, onNonMatch: wrap));
-
- // Test with only onMatch.
- Expect.equals("[a]bc[a]bd[a]e", "abcabdae".splitMapJoin("a", onMatch: mark));
-
- // Test with only onNonMatch
- Expect.equals(
- "<>a<bc>a<bd>a<e>", "abcabdae".splitMapJoin("a", onNonMatch: wrap));
-}
+import "string_replace_all_common.dart";
main() {
- testReplaceAll();
- testReplaceAllMapped();
- testSplitMapJoin();
+ testAll((pattern) => pattern); // unwrapped
}
diff --git a/tests/corelib_2/string_replace_all_common.dart b/tests/corelib_2/string_replace_all_common.dart
new file mode 100644
index 0000000..780761c
--- /dev/null
+++ b/tests/corelib_2/string_replace_all_common.dart
@@ -0,0 +1,146 @@
+// Copyright (c) 2012, 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
+
+import "package:expect/expect.dart";
+
+testAll(Pattern Function(Pattern) wrap) {
+ testReplaceAll(wrap);
+ testReplaceAllMapped(wrap);
+ testSplitMapJoin(wrap);
+}
+
+testReplaceAll(Pattern Function(Pattern) wrap) {
+ Expect.equals("aXXcaXXdae", "abcabdae".replaceAll(wrap("b"), "XX"));
+
+ // Test with the replaced string at the beginning.
+ Expect.equals("XXbcXXbdXXe", "abcabdae".replaceAll(wrap("a"), "XX"));
+
+ // Test with the replaced string at the end.
+ Expect.equals("abcabdaXX", "abcabdae".replaceAll(wrap("e"), "XX"));
+
+ // Test when there are no occurence of the string to replace.
+ Expect.equals("abcabdae", "abcabdae".replaceAll(wrap("f"), "XX"));
+
+ // Test when the string to change is the empty string.
+ Expect.equals("", "".replaceAll(wrap("from"), "to"));
+
+ // Test when the string to change is a substring of the string to
+ // replace.
+ Expect.equals("fro", "fro".replaceAll(wrap("from"), "to"));
+
+ // Test when the string to change is the replaced string.
+ Expect.equals("to", "from".replaceAll(wrap("from"), "to"));
+
+ // Test when matches are adjacent
+ Expect.equals("toto", "fromfrom".replaceAll(wrap("from"), "to"));
+
+ // Test when the string to change is the replacement string.
+ Expect.equals("to", "to".replaceAll(wrap("from"), "to"));
+
+ // Test replacing by the empty string.
+ Expect.equals("bcbde", "abcabdae".replaceAll(wrap("a"), ""));
+ Expect.equals("AB", "AfromB".replaceAll(wrap("from"), ""));
+
+ // Test changing the empty string.
+ Expect.equals("to", "".replaceAll(wrap(""), "to"));
+
+ // Test replacing the empty string.
+ Expect.equals("toAtoBtoCto", "ABC".replaceAll(wrap(""), "to"));
+
+ // Pattern strings containing RegExp metacharacters - these are not
+ // interpreted as RegExps.
+ Expect.equals(r"$$", "||".replaceAll(wrap("|"), r"$"));
+ Expect.equals(r"$$$$", "||".replaceAll(wrap("|"), r"$$"));
+ Expect.equals(r"x$|x", "x|.|x".replaceAll(wrap("|."), r"$"));
+ Expect.equals(r"$$", "..".replaceAll(wrap("."), r"$"));
+ Expect.equals(r"[$$$$]", "[..]".replaceAll(wrap("."), r"$$"));
+ Expect.equals(r"[$]", "[..]".replaceAll(wrap(".."), r"$"));
+ Expect.equals(r"$$", r"\\".replaceAll(wrap(r"\"), r"$"));
+}
+
+testReplaceAllMapped(Pattern Function(Pattern) wrap) {
+ String mark(Match m) => "[${m[0]}]";
+ Expect.equals("a[b]ca[b]dae", "abcabdae".replaceAllMapped(wrap("b"), mark));
+
+ // Test with the replaced string at the beginning.
+ Expect.equals("[a]bc[a]bd[a]e", "abcabdae".replaceAllMapped(wrap("a"), mark));
+
+ // Test with the replaced string at the end.
+ Expect.equals("abcabda[e]", "abcabdae".replaceAllMapped(wrap("e"), mark));
+
+ // Test when there are no occurence of the string to replace.
+ Expect.equals("abcabdae", "abcabdae".replaceAllMapped(wrap("f"), mark));
+
+ // Test when the string to change is the empty string.
+ Expect.equals("", "".replaceAllMapped(wrap("from"), mark));
+
+ // Test when the string to change is a substring of the string to
+ // replace.
+ Expect.equals("fro", "fro".replaceAllMapped(wrap("from"), mark));
+
+ // Test when matches are adjacent
+ Expect.equals(
+ "[from][from]", "fromfrom".replaceAllMapped(wrap("from"), mark));
+
+ // Test replacing by the empty string.
+ Expect.equals("bcbde", "abcabdae".replaceAllMapped(wrap("a"), (m) => ""));
+ Expect.equals("AB", "AfromB".replaceAllMapped(wrap("from"), (m) => ""));
+
+ // Test changing the empty string.
+ Expect.equals("[]", "".replaceAllMapped(wrap(""), mark));
+
+ // Test replacing the empty string.
+ Expect.equals("[]A[]B[]C[]", "ABC".replaceAllMapped(wrap(""), mark));
+}
+
+testSplitMapJoin(Pattern Function(Pattern) wrap) {
+ String mark(Match m) => "[${m[0]}]";
+ String rest(String s) => "<${s}>";
+
+ Expect.equals("<a>[b]<ca>[b]<dae>",
+ "abcabdae".splitMapJoin(wrap("b"), onMatch: mark, onNonMatch: rest));
+
+ // Test with the replaced string at the beginning.
+ Expect.equals("<>[a]<bc>[a]<bd>[a]<e>",
+ "abcabdae".splitMapJoin(wrap("a"), onMatch: mark, onNonMatch: rest));
+
+ // Test with the replaced string at the end.
+ Expect.equals("<abcabda>[e]<>",
+ "abcabdae".splitMapJoin(wrap("e"), onMatch: mark, onNonMatch: rest));
+
+ // Test when there are no occurence of the string to replace.
+ Expect.equals("<abcabdae>",
+ "abcabdae".splitMapJoin(wrap("f"), onMatch: mark, onNonMatch: rest));
+
+ // Test when the string to change is the empty string.
+ Expect.equals(
+ "<>", "".splitMapJoin(wrap("from"), onMatch: mark, onNonMatch: rest));
+
+ // Test when the string to change is a substring of the string to
+ // replace.
+ Expect.equals("<fro>",
+ "fro".splitMapJoin(wrap("from"), onMatch: mark, onNonMatch: rest));
+
+ // Test when matches are adjacent
+ Expect.equals("<>[from]<>[from]<>",
+ "fromfrom".splitMapJoin(wrap("from"), onMatch: mark, onNonMatch: rest));
+
+ // Test changing the empty string.
+ Expect.equals(
+ "<>[]<>", "".splitMapJoin(wrap(""), onMatch: mark, onNonMatch: rest));
+
+ // Test replacing the empty string.
+ Expect.equals("<>[]<A>[]<B>[]<C>[]<>",
+ "ABC".splitMapJoin(wrap(""), onMatch: mark, onNonMatch: rest));
+
+ // Test with only onMatch.
+ Expect.equals(
+ "[a]bc[a]bd[a]e", "abcabdae".splitMapJoin(wrap("a"), onMatch: mark));
+
+ // Test with only onNonMatch
+ Expect.equals(
+ "<>a<bc>a<bd>a<e>", "abcabdae".splitMapJoin(wrap("a"), onNonMatch: rest));
+}
diff --git a/tests/corelib_2/string_replace_all_pattern_test.dart b/tests/corelib_2/string_replace_all_pattern_test.dart
new file mode 100644
index 0000000..10827c9
--- /dev/null
+++ b/tests/corelib_2/string_replace_all_pattern_test.dart
@@ -0,0 +1,27 @@
+// Copyright (c) 2021, 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
+
+import "string_replace_all_common.dart";
+
+main() {
+ testAll(Wrapper.wrap);
+}
+
+/// A wrapper that is not recognizable as a String or RegExp.
+class Wrapper implements Pattern {
+ final Pattern _pattern;
+ Wrapper(this._pattern);
+
+ static Pattern wrap(Pattern p) => Wrapper(p);
+
+ Iterable<Match> allMatches(String string, [int start = 0]) =>
+ _pattern.allMatches(string, start);
+
+ Match matchAsPrefix(String string, [int start = 0]) =>
+ _pattern.matchAsPrefix(string, start);
+
+ String toString() => "Wrap($_pattern)";
+}
diff --git a/tests/corelib_2/string_replace_all_test.dart b/tests/corelib_2/string_replace_all_test.dart
index e4e340b..9d43fb7 100644
--- a/tests/corelib_2/string_replace_all_test.dart
+++ b/tests/corelib_2/string_replace_all_test.dart
@@ -1,142 +1,11 @@
-// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
+// Copyright (c) 2022, 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
-import "package:expect/expect.dart";
-
-testReplaceAll() {
- Expect.equals("aXXcaXXdae", "abcabdae".replaceAll("b", "XX"));
-
- // Test with the replaced string at the beginning.
- Expect.equals("XXbcXXbdXXe", "abcabdae".replaceAll("a", "XX"));
-
- // Test with the replaced string at the end.
- Expect.equals("abcabdaXX", "abcabdae".replaceAll("e", "XX"));
-
- // Test when there are no occurence of the string to replace.
- Expect.equals("abcabdae", "abcabdae".replaceAll("f", "XX"));
-
- // Test when the string to change is the empty string.
- Expect.equals("", "".replaceAll("from", "to"));
-
- // Test when the string to change is a substring of the string to
- // replace.
- Expect.equals("fro", "fro".replaceAll("from", "to"));
-
- // Test when the string to change is the replaced string.
- Expect.equals("to", "from".replaceAll("from", "to"));
-
- // Test when matches are adjacent
- Expect.equals("toto", "fromfrom".replaceAll("from", "to"));
-
- // Test when the string to change is the replacement string.
- Expect.equals("to", "to".replaceAll("from", "to"));
-
- // Test replacing by the empty string.
- Expect.equals("bcbde", "abcabdae".replaceAll("a", ""));
- Expect.equals("AB", "AfromB".replaceAll("from", ""));
-
- // Test changing the empty string.
- Expect.equals("to", "".replaceAll("", "to"));
-
- // Test replacing the empty string.
- Expect.equals("toAtoBtoCto", "ABC".replaceAll("", "to"));
-
- // Pattern strings containing RegExp metacharacters - these are not
- // interpreted as RegExps.
- Expect.equals(r"$$", "||".replaceAll("|", r"$"));
- Expect.equals(r"$$$$", "||".replaceAll("|", r"$$"));
- Expect.equals(r"x$|x", "x|.|x".replaceAll("|.", r"$"));
- Expect.equals(r"$$", "..".replaceAll(".", r"$"));
- Expect.equals(r"[$$$$]", "[..]".replaceAll(".", r"$$"));
- Expect.equals(r"[$]", "[..]".replaceAll("..", r"$"));
- Expect.equals(r"$$", r"\\".replaceAll(r"\", r"$"));
-}
-
-testReplaceAllMapped() {
- String mark(Match m) => "[${m[0]}]";
- Expect.equals("a[b]ca[b]dae", "abcabdae".replaceAllMapped("b", mark));
-
- // Test with the replaced string at the beginning.
- Expect.equals("[a]bc[a]bd[a]e", "abcabdae".replaceAllMapped("a", mark));
-
- // Test with the replaced string at the end.
- Expect.equals("abcabda[e]", "abcabdae".replaceAllMapped("e", mark));
-
- // Test when there are no occurence of the string to replace.
- Expect.equals("abcabdae", "abcabdae".replaceAllMapped("f", mark));
-
- // Test when the string to change is the empty string.
- Expect.equals("", "".replaceAllMapped("from", mark));
-
- // Test when the string to change is a substring of the string to
- // replace.
- Expect.equals("fro", "fro".replaceAllMapped("from", mark));
-
- // Test when matches are adjacent
- Expect.equals("[from][from]", "fromfrom".replaceAllMapped("from", mark));
-
- // Test replacing by the empty string.
- Expect.equals("bcbde", "abcabdae".replaceAllMapped("a", (m) => ""));
- Expect.equals("AB", "AfromB".replaceAllMapped("from", (m) => ""));
-
- // Test changing the empty string.
- Expect.equals("[]", "".replaceAllMapped("", mark));
-
- // Test replacing the empty string.
- Expect.equals("[]A[]B[]C[]", "ABC".replaceAllMapped("", mark));
-}
-
-testSplitMapJoin() {
- String mark(Match m) => "[${m[0]}]";
- String wrap(String s) => "<${s}>";
-
- Expect.equals("<a>[b]<ca>[b]<dae>",
- "abcabdae".splitMapJoin("b", onMatch: mark, onNonMatch: wrap));
-
- // Test with the replaced string at the beginning.
- Expect.equals("<>[a]<bc>[a]<bd>[a]<e>",
- "abcabdae".splitMapJoin("a", onMatch: mark, onNonMatch: wrap));
-
- // Test with the replaced string at the end.
- Expect.equals("<abcabda>[e]<>",
- "abcabdae".splitMapJoin("e", onMatch: mark, onNonMatch: wrap));
-
- // Test when there are no occurence of the string to replace.
- Expect.equals("<abcabdae>",
- "abcabdae".splitMapJoin("f", onMatch: mark, onNonMatch: wrap));
-
- // Test when the string to change is the empty string.
- Expect.equals("<>", "".splitMapJoin("from", onMatch: mark, onNonMatch: wrap));
-
- // Test when the string to change is a substring of the string to
- // replace.
- Expect.equals(
- "<fro>", "fro".splitMapJoin("from", onMatch: mark, onNonMatch: wrap));
-
- // Test when matches are adjacent
- Expect.equals("<>[from]<>[from]<>",
- "fromfrom".splitMapJoin("from", onMatch: mark, onNonMatch: wrap));
-
- // Test changing the empty string.
- Expect.equals("<>[]<>", "".splitMapJoin("", onMatch: mark, onNonMatch: wrap));
-
- // Test replacing the empty string.
- Expect.equals("<>[]<A>[]<B>[]<C>[]<>",
- "ABC".splitMapJoin("", onMatch: mark, onNonMatch: wrap));
-
- // Test with only onMatch.
- Expect.equals("[a]bc[a]bd[a]e", "abcabdae".splitMapJoin("a", onMatch: mark));
-
- // Test with only onNonMatch
- Expect.equals(
- "<>a<bc>a<bd>a<e>", "abcabdae".splitMapJoin("a", onNonMatch: wrap));
-}
+import "string_replace_all_common.dart";
main() {
- testReplaceAll();
- testReplaceAllMapped();
- testSplitMapJoin();
+ testAll((pattern) => pattern); // unwrapped
}
diff --git a/tools/VERSION b/tools/VERSION
index 3bb13e6..dc4983b 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 17
PATCH 0
-PRERELEASE 12
+PRERELEASE 13
PRERELEASE_PATCH 0
\ No newline at end of file