[macros] Remove pkg/_macros and pkg/macros

Change-Id: I14bde57b4de1a9dafe9aa291baf0fffca89c3b19
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/420680
Reviewed-by: Devon Carew <devoncarew@google.com>
Reviewed-by: Jake Macdonald <jakemac@google.com>
This commit is contained in:
Johnni Winther
2025-04-08 00:18:40 -07:00
committed by Commit Queue
parent 04f3c05a4b
commit f75cb3dfb9
75 changed files with 2 additions and 16603 deletions
-57
View File
@@ -1,57 +0,0 @@
## 0.3.3
- Add `isConst` to constructors.
- Bug fix: Add `const` and `factory` modifiers to constructor augmentations.
- Add `isField` and `isSuper` to parameters.
- Bug fix: Add `this.` and `super.` to parameter augmentations when needed.
## 0.3.2
- Fix bug where augmenting classes with type parameters didn't work.
## 0.3.1
- Make it an error for macros to complete with pending async work scheduled.
## 0.3.0
- Remove type parameter on internal `StaticType` implementation.
## 0.2.0
- Add identifiers to `NamedStaticType`.
- Add `StaticType.asInstanceOf`.
## 0.1.7
- Fix for generating code after extendsType
## 0.1.6
- Add extendsType API for adding an extends clause.
- Refactor builder implementations, fixes some bugs around nested builders.
## 0.1.5
- Handle ParallelWaitError with DiagnosticException errors nicely.
- Fix a bug where we weren't reporting diagnostics for nested builders.
## 0.1.4
- Improve formatting of constructor initializer augmentations.
## 0.1.3
- Validate parts in `Code.fromParts()`.
## 0.1.2
- Add caching for `typeDeclarationOf` results.
## 0.1.1
- Add caching for `TypeDeclaration` related introspection results.
## 0.1.0
Initial release, copied from `_fe_analyzer_shared/lib/src/macros`.
-51
View File
@@ -1,51 +0,0 @@
## Required steps when updating this package
When making any functional change in the `lib` directory of this package, the
following procedure **must** be followed.
### Update pubspec/changelog for each release.
Because this is an SDK vendored package, every change is treated as a release,
and must have a stable version number and CHANGELOG.md entry.
### Update and publish `package:macros`
Additionally, the pub package `macros`, which lives at `pkg/macros`, must have
a corresponding release on pub for each version of this package.
The version of the `_macros` dependency in its pubspec must be updated to match
the new version of this package, and the pubspec version and changelog should be
updated. The changelog should have the same information as the associated
versions of this package.
These changes to the `macros` package should be landed in the same CL as the
changes to this package, and it should be immediately published when the CL is
merged. These should be marked as pre-release versions (with the `-main.x`
suffix), and stable versions will only be published when the beta SDK has been
released (exact process is TBD, possibly could do it as a hotfix, or publish
from a branch).
It is possible that multiple breaking changes can land within the same major
version of this package, during the pre-release period. Version compatibility is
thus **not** guaranteed on the dev or main channels, only the beta and stable
channels.
### Bypassing presubmit checks
When making a non-functional change in the `lib` directory, use the
`--bypass-hooks` flag to bypass presubmit checks, as in
`git cl upload --bypass-hooks`.
## Special considerations for this package
This package should generally be treated like a `dart:` library, since only
exactly one version of it ships with any SDK. That has several implications.
### Must follow breaking change process
Any breaking change to this package should follow the same breaking change
process as any change to the `dart:` libraries.
In general any breaking change made here can result in users not being able to
get a version solve on the newest SDK, if their macro dependencies have not yet
updated to the latest version.
-27
View File
@@ -1,27 +0,0 @@
Copyright 2024, the Dart project authors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Google LLC nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-1
View File
@@ -1 +0,0 @@
file:/tools/OWNERS_FOUNDATION
-59
View File
@@ -1,59 +0,0 @@
#!/usr/bin/env python3
# Copyright (c) 2024, 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.
"""_macros package presubmit python script.
See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
for more details about the presubmit API built into gcl.
"""
PRESUBMIT_VERSION = '2.0.0'
USE_PYTHON3 = True
# Ensures that the pubspec of `package_name` has been altered.
#
# Returns a list of errors if not.
#
# TODO(jakemac): Ensure the version was bumped as well.
def EnsurePubspecAndChangelogAltered(input_api, package_name):
errors = []
package_path = 'pkg/%s' % package_name
pubspec_path = '%s/pubspec.yaml' % package_path
pubspec_changed = any(file.LocalPath() == pubspec_path
for file in input_api.change.AffectedFiles())
if not pubspec_changed:
errors.append(
('The pkg/_macros/lib dir was altered but the version of %s was '
'not bumped. See pkg/_macros/CONTRIBUTING.md' % package_path))
changelog_path = '%s/CHANGELOG.md' % package_path
changelog_changed = any(file.LocalPath() == changelog_path
for file in input_api.change.AffectedFiles())
if not changelog_changed:
errors.append(
('The pkg/_macros/lib dir was altered but the CHANGELOG.md of %s '
'was not edited. See pkg/_macros/CONTRIBUTING.md' % package_path))
return errors
# Invoked on upload and commit.
def CheckChange(input_api, output_api):
errors = []
# If the `lib` dir is altered, we also require a change to the pubspec.yaml
# of both this package and the `macros` package.
lib_changed = any(file.LocalPath().startswith('pkg/_macros/lib')
for file in input_api.AffectedFiles())
if lib_changed:
errors += EnsurePubspecAndChangelogAltered(input_api, '_macros')
errors += EnsurePubspecAndChangelogAltered(input_api, 'macros')
if errors:
return [
output_api.PresubmitError(
'pkg/_macros presubmit/PRESUBMIT.py failure(s):',
long_text='\n\n'.join(errors))
]
return []
-2
View File
@@ -1,2 +0,0 @@
Package `_macros` contains a private API for authoring macros.
It is exposed as a public API by [`package:macros`](./macros/README.md).
-11
View File
@@ -1,11 +0,0 @@
include: package:lints/recommended.yaml
analyzer:
language:
strict-casts: true
errors:
# Remove on next publish or protocol refactoring:
unintended_html_in_doc_comment: ignore
# Remove on next publish or protocol refactoring;
# https://github.com/dart-lang/language/issues/3706.
unnecessary_library_name: ignore
@@ -1,466 +0,0 @@
// 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 'dart:convert';
import 'dart:io';
import 'dart:isolate';
import 'dart:typed_data';
import 'package:_macros/src/executor/message_grouper.dart';
import 'package:_macros/src/executor/serialization.dart';
void main() async {
for (var serializationMode in [
SerializationMode.json,
SerializationMode.byteData
]) {
await withSerializationMode(serializationMode, () async {
await _isolateSpawnBenchmarks();
await _isolateSpawnUriBenchmarks();
await _separateProcessStdioBenchmarks();
await _separateProcessSocketBenchmarks();
});
}
}
Future<void> _isolateSpawnBenchmarks() async {
void Function(SendPort) childIsolateFn(SerializationMode mode) =>
(SendPort sendPort) => withSerializationMode(mode, () {
var isolateReceivePort = ReceivePort();
isolateReceivePort.listen((data) {
deserialize(data);
var result = serialize();
result = result is Uint8List
? TransferableTypedData.fromList([result])
: result;
sendPort.send(result);
});
sendPort.send(isolateReceivePort.sendPort);
});
Completer? responseCompleter;
late SendPort sendPort;
var receivePort = ReceivePort();
var isolate = await Isolate.spawn(
childIsolateFn(serializationMode), receivePort.sendPort);
final sendPortCompleter = Completer<SendPort>();
receivePort.listen((data) {
if (!sendPortCompleter.isCompleted) {
sendPortCompleter.complete(data as SendPort);
} else {
responseCompleter!.complete(data);
}
});
sendPort = await sendPortCompleter.future;
// warmup
for (var i = 0; i < 100; i++) {
responseCompleter = Completer();
var result = serialize();
result =
result is Uint8List ? TransferableTypedData.fromList([result]) : result;
sendPort.send(result);
deserialize(await responseCompleter.future);
}
// measure
var watch = Stopwatch()..start();
for (var i = 0; i < 100; i++) {
responseCompleter = Completer();
var result = serialize();
result =
result is Uint8List ? TransferableTypedData.fromList([result]) : result;
sendPort.send(result);
deserialize(await responseCompleter.future);
}
print('Isolate.spawn + $serializationMode: ${watch.elapsed}');
receivePort.close();
isolate.kill();
}
Future<void> _isolateSpawnUriBenchmarks() async {
Completer? responseCompleter;
late SendPort sendPort;
var receivePort = ReceivePort();
var isolate = await Isolate.spawnUri(
Uri.dataFromString(childProgram(serializationMode)),
[],
receivePort.sendPort);
final sendPortCompleter = Completer<SendPort>();
receivePort.listen((data) {
if (!sendPortCompleter.isCompleted) {
sendPortCompleter.complete(data as SendPort);
} else {
responseCompleter!.complete(data);
}
});
sendPort = await sendPortCompleter.future;
// warmup
for (var i = 0; i < 100; i++) {
responseCompleter = Completer();
var result = serialize();
result =
result is Uint8List ? TransferableTypedData.fromList([result]) : result;
sendPort.send(result);
deserialize(await responseCompleter.future);
}
// measure
var watch = Stopwatch()..start();
for (var i = 0; i < 100; i++) {
responseCompleter = Completer();
var result = serialize();
result =
result is Uint8List ? TransferableTypedData.fromList([result]) : result;
sendPort.send(result);
deserialize(await responseCompleter.future);
}
print('Isolate.spawnUri + $serializationMode: ${watch.elapsed}');
receivePort.close();
isolate.kill();
}
Future<void> _separateProcessStdioBenchmarks() async {
Completer? responseCompleter;
var tmpDir = Directory.systemTemp.createTempSync('serialize_bench');
try {
var file = File(tmpDir.uri.resolve('main.dart').toFilePath());
file.writeAsStringSync(childProgram(serializationMode));
var process = await Process.start(Platform.resolvedExecutable, [
'--packages=${(await Isolate.packageConfig)!.toFilePath()}',
file.path,
]);
var listeners = <StreamSubscription>[
process.stderr.listen((event) {
print('stderr: ${utf8.decode(event)}');
}),
(serializationMode == SerializationMode.json
? process.stdout
: MessageGrouper(process.stdout).messageStream)
.listen((data) {
responseCompleter!.complete(data);
}),
];
// warmup
for (var i = 0; i < 100; i++) {
responseCompleter = Completer();
var result = serialize();
if (result is List<int>) {
final bytesBuilder = BytesBuilder(copy: false);
_writeLength(result, bytesBuilder);
bytesBuilder.add(result);
process.stdin.add(bytesBuilder.takeBytes());
} else {
process.stdin.writeln(jsonEncode(result));
}
deserialize(await responseCompleter.future);
}
// measure
var watch = Stopwatch()..start();
for (var i = 0; i < 100; i++) {
responseCompleter = Completer();
var result = serialize();
if (result is List<int>) {
final bytesBuilder = BytesBuilder(copy: false);
_writeLength(result, bytesBuilder);
bytesBuilder.add(result);
process.stdin.add(bytesBuilder.takeBytes());
} else {
process.stdin.writeln(jsonEncode(result));
}
deserialize(await responseCompleter.future);
}
print('Separate process + Stdio + $serializationMode: ${watch.elapsed}');
for (var listener in listeners) {
listener.cancel();
}
process.kill();
} catch (e, s) {
print('Error running benchmark \n$e\n\n$s');
} finally {
tmpDir.deleteSync(recursive: true);
}
}
Future<void> _separateProcessSocketBenchmarks() async {
Completer? responseCompleter;
var tmpDir = Directory.systemTemp.createTempSync('serialize_bench');
try {
var file = File(tmpDir.uri.resolve('main.dart').toFilePath());
file.writeAsStringSync(childProgram(serializationMode));
ServerSocket serverSocket;
// Try an ipv6 address loopback first, and fall back on ipv4.
try {
serverSocket = await ServerSocket.bind(InternetAddress.loopbackIPv6, 0);
} on SocketException catch (_) {
serverSocket = await ServerSocket.bind(InternetAddress.loopbackIPv4, 0);
}
Completer<Socket> clientCompleter = Completer();
serverSocket.listen((client) {
clientCompleter.complete(client);
});
var process = await Process.start(Platform.resolvedExecutable, [
'--packages=${(await Isolate.packageConfig)!.toFilePath()}',
file.path,
serverSocket.address.address,
serverSocket.port.toString(),
]);
var client = await clientCompleter.future;
// Nagle's algorithm slows us down >100x, disable it.
client.setOption(SocketOption.tcpNoDelay, true);
var listeners = <StreamSubscription>[
(serializationMode == SerializationMode.json
? client
: MessageGrouper(client).messageStream)
.listen((event) {
responseCompleter!.complete(event);
}),
process.stderr.listen((event) {
print('stderr: ${utf8.decode(event)}');
}),
process.stdout.listen((event) {
print('stdout: ${utf8.decode(event)}');
}),
];
// warmup
for (var i = 0; i < 100; i++) {
responseCompleter = Completer();
var result = serialize();
if (result is List<int>) {
final bytesBuilder = BytesBuilder(copy: false);
_writeLength(result, bytesBuilder);
bytesBuilder.add(result);
client.add(bytesBuilder.takeBytes());
} else {
client.write(jsonEncode(result));
}
deserialize(await responseCompleter.future);
}
// measure
var watch = Stopwatch()..start();
for (var i = 0; i < 100; i++) {
responseCompleter = Completer();
var result = serialize();
if (result is List<int>) {
final bytesBuilder = BytesBuilder(copy: false);
_writeLength(result, bytesBuilder);
bytesBuilder.add(result);
client.add(bytesBuilder.takeBytes());
} else {
client.write(jsonEncode(result));
}
deserialize(await responseCompleter.future);
}
print('Separate process + Socket + $serializationMode: ${watch.elapsed}');
for (var listener in listeners) {
listener.cancel();
}
process.kill();
await serverSocket.close();
client.destroy();
} catch (e, s) {
print('Error running benchmark \n$e\n\n$s');
} finally {
tmpDir.deleteSync(recursive: true);
}
}
void _writeLength(List<int> result, BytesBuilder bytesBuilder) {
int length = (result as Uint8List).lengthInBytes;
if (length > 0xffffffff) {
throw StateError('Message was larger than the allowed size!');
}
bytesBuilder.add([
length >> 24 & 0xff,
length >> 16 & 0xff,
length >> 8 & 0xff,
length & 0xff
]);
}
String childProgram(SerializationMode mode) => '''
import 'dart:convert';
import 'dart:io';
import 'dart:isolate';
import 'dart:typed_data';
import 'package:_fe_analyzer_shared/src/macros/executor/message_grouper.dart';
import 'package:_fe_analyzer_shared/src/macros/executor/serialization.dart';
void main(List<String> args, [SendPort? sendPort]) async {
var mode = $mode;
await withSerializationMode(mode, () async {
if (sendPort != null) {
var isolateReceivePort = ReceivePort();
isolateReceivePort.listen((data) {
deserialize(data);
var result = serialize();
result = result is Uint8List
? TransferableTypedData.fromList([result])
: result;
sendPort.send(result);
});
sendPort.send(isolateReceivePort.sendPort);
} else if (args.isNotEmpty) {
var address = args[0];
var port = int.parse(args[1]);
var socket = await Socket.connect(address, port);
if (mode == SerializationMode.json) {
socket.listen((data) {
var json = utf8.decode(data).trimRight();
deserialize(jsonDecode(json));
socket.write(jsonEncode(serialize()));
});
} else {
MessageGrouper(socket).messageStream.listen((data) {
deserialize(data);
var result = serialize() as Uint8List;
final bytesBuilder = BytesBuilder(copy: false);
_writeLength(result, bytesBuilder);
bytesBuilder.add(result);
socket.add(bytesBuilder.takeBytes());
});
}
} else {
// We allow one empty line to work around some weird data.
var allowEmpty = true;
if (mode == SerializationMode.json) {
stdin.listen((data) {
var json = utf8.decode(data).trimRight();
// On exit we tend to get extra empty lines sometimes?
if (json.isEmpty && allowEmpty) {
allowEmpty = false;
return;
}
deserialize(jsonDecode(json));
stdout.write(jsonEncode(serialize()));
});
} else {
MessageGrouper(stdin).messageStream.listen((data) {
deserialize(data);
var result = serialize() as Uint8List;
final bytesBuilder = BytesBuilder(copy: false);
_writeLength(result, bytesBuilder);
bytesBuilder.add(result);
stdout.add(bytesBuilder.takeBytes());
});
}
}
});
}
Object? serialize() {
var serializer = serializerFactory();
for (var i = 0; i < 100; i++) {
serializer.addInt(i * 100);
serializer.addString('foo' * i);
serializer.addBool(i % 2 == 0);
serializer.startList();
for (var j = 0; j < 10; j++) {
serializer.addDouble(i * 5);
}
serializer.endList();
serializer.addNull();
}
return serializer.result;
}
void deserialize(Object? result) {
result = result is TransferableTypedData
? result.materialize().asUint8List()
: result;
var deserializer = deserializerFactory(result);
while (deserializer.moveNext()) {
deserializer
..expectInt()
..moveNext()
..expectString()
..moveNext()
..expectBool()
..moveNext()
..expectList();
while (deserializer.moveNext()) {
deserializer.expectDouble();
}
deserializer
..moveNext()
..checkNull();
}
}
void _writeLength(Uint8List result, BytesBuilder bytesBuilder) {
int length = result.lengthInBytes;
if (length > 0xffffffff) {
throw new StateError('Message was larger than the allowed size!');
}
bytesBuilder.add([
length >> 24 & 0xff,
length >> 16 & 0xff,
length >> 8 & 0xff,
length & 0xff
]);
}''';
Object? serialize() {
var serializer = serializerFactory();
for (var i = -50; i < 50; i++) {
serializer.addInt(i % 2 * 100);
serializer.addString('foo' * i);
serializer.addBool(i < 0);
serializer.startList();
for (var j = 0.0; j < 10; j++) {
serializer.addDouble(i * j);
}
serializer.endList();
serializer.addNull();
}
return serializer.result;
}
void deserialize(Object? result) {
result = result is TransferableTypedData
? result.materialize().asUint8List()
: result;
if (serializationMode == SerializationMode.json) {
if (result is List<int>) {
result = jsonDecode(utf8.decode(result));
}
}
var deserializer = deserializerFactory(result);
while (deserializer.moveNext()) {
deserializer
..expectInt()
..moveNext()
..expectString()
..moveNext()
..expectBool()
..moveNext()
..expectList();
while (deserializer.moveNext()) {
deserializer.expectDouble();
}
deserializer
..moveNext()
..checkNull();
}
}
-13
View File
@@ -1,13 +0,0 @@
// 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 'dart:async';
import 'dart:collection' show UnmodifiableListView;
part 'api/builders.dart';
part 'api/code.dart';
part 'api/diagnostic.dart';
part 'api/exceptions.dart';
part 'api/introspection.dart';
part 'api/macros.dart';
-326
View File
@@ -1,326 +0,0 @@
// 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.
part of '../api.dart';
/// The base interface used to add declarations to the program as well
/// as augment existing ones.
///
/// Can also be used to emit diagnostic messages back to the parent tool.
abstract interface class Builder {
/// Attaches [diagnostic] to the result of this macro application phase.
///
/// Note that this will not immediately send the result, these will all be
/// collected and reported at once when the macro completes this phase.
void report(Diagnostic diagnostic);
}
/// The interface for all introspection that is allowed during the type phase
/// (and later).
abstract interface class TypePhaseIntrospector {
/// Returns an [Identifier] for a top level [name] in [library].
///
/// You should only do this for libraries that are definitely in the
/// transitive import graph of the library you are generating code into. If
/// [library] is not in this transitive import graph, then an unspecified
/// [Exception] should be thrown. The best way to ensure this, is to have the
/// macro library itself import [library] (even if it doesn't directly use
/// it).
///
/// When the name alone is not sufficient to disambiguate between multiple
/// declarations, such as the case of a field (which has a synthetic getter),
/// an [Identifier] pointing to the non-synthetic declaration will be
/// returned. Future calls to `declarationOf(identifier)` will return that
/// non-synthetic declaration.
///
/// If [name] does not exist in [library], then an unspecified [Exception]
/// should be thrown.
@Deprecated(
'This API should eventually be replaced with a different, safer API.')
Future<Identifier> resolveIdentifier(Uri library, String name);
}
/// The API used by [Macro]s to contribute new type declarations to the
/// current library, and get [TypeAnnotation]s from runtime [Type] objects.
abstract interface class TypeBuilder implements Builder, TypePhaseIntrospector {
/// Adds a new type declaration to the surrounding library.
///
/// The [name] must match the name of the new [typeDeclaration] (this does
/// not include any type parameters, just the name).
void declareType(String name, DeclarationCode typeDeclaration);
}
/// The API used by macros in the type phase to add interfaces to an existing
/// type.
abstract interface class InterfaceTypesBuilder implements TypeBuilder {
/// Appends [interfaces] to the list of interfaces for this type.
void appendInterfaces(Iterable<TypeAnnotationCode> interfaces);
}
/// The API used by macros in the type phase to add mixins to an existing
/// type.
abstract interface class MixinTypesBuilder implements TypeBuilder {
/// Appends [mixins] to the list of mixins for this type.
void appendMixins(Iterable<TypeAnnotationCode> mixins);
}
/// The API used by macros in the type phase to add an extends clause to an
/// existing type.
abstract interface class ExtendsTypeBuilder implements TypeBuilder {
/// Sets the `extends` clause to [superclass].
///
/// The type must not already have an `extends` clause.
void extendsType(NamedTypeAnnotationCode superclass);
}
/// The API used by macros in the type phase to augment classes.
abstract interface class ClassTypeBuilder
implements
TypeBuilder,
ExtendsTypeBuilder,
InterfaceTypesBuilder,
MixinTypesBuilder {}
/// The API used by macros in the type phase to augment enums.
abstract interface class EnumTypeBuilder
implements TypeBuilder, InterfaceTypesBuilder, MixinTypesBuilder {}
/// The API used by macros in the type phase to augment mixins.
///
/// Note that mixins don't support mixins, only interfaces.
abstract interface class MixinTypeBuilder
implements TypeBuilder, InterfaceTypesBuilder {}
/// The interface for all introspection that is allowed during the declaration
/// phase (and later).
abstract interface class DeclarationPhaseIntrospector
implements TypePhaseIntrospector {
/// Instantiates a new [StaticType] for a given [type] annotation.
///
/// Throws if [type] is a [RawTypeAnnotationCode], more specific subtypes must
/// be used, as raw [Identifier]s are not allowed.
///
/// Throws an error if the [type] object contains [Identifier]s which cannot
/// be resolved. This should only happen in the case of incomplete or invalid
/// programs, but macros may be asked to run in this state during the
/// development cycle. It may be helpful for users if macros provide a best
/// effort implementation in that case or handle the error in a useful way.
Future<StaticType> resolve(TypeAnnotationCode type);
/// The values available for [enuum].
///
/// This may be incomplete if additional declaration macros are going to run
/// on [enuum].
Future<List<EnumValueDeclaration>> valuesOf(covariant EnumDeclaration enuum);
/// The fields available for [type].
///
/// This may be incomplete if additional declaration macros are going to run
/// on [type].
Future<List<FieldDeclaration>> fieldsOf(covariant TypeDeclaration type);
/// The methods available for [type].
///
/// This may be incomplete if additional declaration macros are going to run
/// on [type].
Future<List<MethodDeclaration>> methodsOf(covariant TypeDeclaration type);
/// The constructors available for [type].
///
/// This may be incomplete if additional declaration macros are going to run
/// on [type].
Future<List<ConstructorDeclaration>> constructorsOf(
covariant TypeDeclaration type);
/// [TypeDeclaration]s for all the types declared in [library].
///
/// Note that this includes [ExtensionDeclaration]s as well, even though they
/// do not actually introduce a new type.
Future<List<TypeDeclaration>> typesOf(covariant Library library);
/// Resolves an [identifier] to its [TypeDeclaration].
///
/// If [identifier] does not resolve to a [TypeDeclaration], then a
/// [MacroImplementationException] is thrown.
Future<TypeDeclaration> typeDeclarationOf(covariant Identifier identifier);
}
/// The API used by [Macro]s to contribute new (non-type)
/// declarations to the current library.
///
/// Can also be used to do subtype checks on types.
abstract interface class DeclarationBuilder
implements Builder, DeclarationPhaseIntrospector {
/// Adds a new regular declaration to the surrounding library.
///
/// Note that type declarations are not supported.
void declareInLibrary(DeclarationCode declaration);
}
/// The API used by [Macro]s to contribute new members to a type.
abstract interface class MemberDeclarationBuilder
implements DeclarationBuilder {
/// Adds a new declaration to the surrounding class.
void declareInType(DeclarationCode declaration);
}
/// The API used by [Macro]s to contribute new members or values to an enum.
abstract interface class EnumDeclarationBuilder
implements MemberDeclarationBuilder {
/// Adds a new enum entry declaration to the surrounding enum.
void declareEnumValue(DeclarationCode declaration);
}
/// The interface for all introspection that is allowed during the definition
/// phase (and later).
abstract interface class DefinitionPhaseIntrospector
implements DeclarationPhaseIntrospector {
/// Resolves any [identifier] to its [Declaration].
Future<Declaration> declarationOf(covariant Identifier identifier);
/// Resolves an [identifier] referring to a type to its [TypeDeclaration].
@override
Future<TypeDeclaration> typeDeclarationOf(covariant Identifier identifier);
/// Infers a real type annotation for [omittedType].
///
/// If no type could be inferred, then a type annotation representing the
/// dynamic type will be given.
Future<TypeAnnotation> inferType(covariant OmittedTypeAnnotation omittedType);
/// Returns a list of all the [Declaration]s in the given [library].
Future<List<Declaration>> topLevelDeclarationsOf(covariant Library library);
}
/// The base class for builders in the definition phase. These can convert
/// any [TypeAnnotation] into its corresponding [TypeDeclaration], and also
/// reflect more deeply on those.
abstract interface class DefinitionBuilder
implements Builder, DefinitionPhaseIntrospector {}
/// The APIs used by [Macro]s that run on library directives, to fill in the
/// definitions of any declarations within that library.
abstract interface class LibraryDefinitionBuilder implements DefinitionBuilder {
/// Retrieve a [TypeDefinitionBuilder] for a type declaration with
/// [identifier].
///
/// Throws a [MacroImplementationException] if [identifier] does not refer to
/// a type declaration in this library.
Future<TypeDefinitionBuilder> buildType(Identifier identifier);
/// Retrieve a [FunctionDefinitionBuilder] for a function declaration with
/// [identifier].
///
/// Throws a [MacroImplementationException] if [identifier] does not refer to
/// a top level function declaration in this library.
Future<FunctionDefinitionBuilder> buildFunction(Identifier identifier);
/// Retrieve a [VariableDefinitionBuilder] for a variable declaration with
/// [identifier].
///
/// Throws a [MacroImplementationException] if [identifier] does not refer to
/// a top level variable declaration in this library.
Future<VariableDefinitionBuilder> buildVariable(Identifier identifier);
}
/// The APIs used by [Macro]s that run on type declarations, to fill in the
/// definitions of any declarations within that class.
abstract interface class TypeDefinitionBuilder implements DefinitionBuilder {
/// Retrieve a [VariableDefinitionBuilder] for a field with [identifier].
///
/// Throws a [MacroImplementationException] if [identifier] does not refer to
/// a field in this class.
Future<VariableDefinitionBuilder> buildField(Identifier identifier);
/// Retrieve a [FunctionDefinitionBuilder] for a method with [identifier].
///
/// Throws a [MacroImplementationException] if [identifier] does not refer to
/// a method in this class.
Future<FunctionDefinitionBuilder> buildMethod(Identifier identifier);
/// Retrieve a [ConstructorDefinitionBuilder] for a constructor with
/// [identifier].
///
/// Throws a [MacroImplementationException] if [identifier] does not refer to
/// a constructor in this class.
Future<ConstructorDefinitionBuilder> buildConstructor(Identifier identifier);
}
/// The APIs used by [Macro]s that run on enums, to fill in the
/// definitions of any declarations within that enum.
abstract interface class EnumDefinitionBuilder
implements TypeDefinitionBuilder {
/// Retrieve an [EnumValueDefinitionBuilder] for an entry with [identifier].
///
/// Throws a [MacroImplementationException] if [identifier] does not refer to
/// an entry on this enum.
Future<EnumValueDefinitionBuilder> buildEnumValue(Identifier identifier);
}
/// The APIs used by [Macro]s to define the body of a constructor
/// or wrap the body of an existing constructor with additional statements.
abstract interface class ConstructorDefinitionBuilder
implements DefinitionBuilder {
/// Augments an existing constructor body with [body] and [initializers].
///
/// The [initializers] should not contain trailing or preceding commas.
///
/// If [docComments] are supplied, they will be added above this augment
/// declaration.
///
/// TODO: Link the library augmentations proposal to describe the semantics.
void augment({
FunctionBodyCode? body,
List<Code>? initializers,
CommentCode? docComments,
});
}
/// The APIs used by [Macro]s to augment functions or methods.
abstract interface class FunctionDefinitionBuilder
implements DefinitionBuilder {
/// Augments the function.
///
/// If [docComments] are supplied, they will be added above this augment
/// declaration.
///
/// TODO: Link the library augmentations proposal to describe the semantics.
void augment(
FunctionBodyCode body, {
CommentCode? docComments,
});
}
/// The API used by [Macro]s to augment a top level variable or instance field.
abstract interface class VariableDefinitionBuilder
implements DefinitionBuilder {
/// Augments the field.
///
/// For [getter] and [setter] the full function declaration should be
/// provided, minus the `augment` keyword (which will be implicitly added).
///
/// If [initializerDocComments] are supplied, they will be added above the
/// augment declaration for [initializer]. It is an error to provide
/// [initializerDocComments] but not [initializer].
///
/// To provide doc comments for [getter] or [setter], just include them in
/// the [DeclarationCode] object for those.
///
/// TODO: Link the library augmentations proposal to describe the semantics.
void augment({
DeclarationCode? getter,
DeclarationCode? setter,
ExpressionCode? initializer,
CommentCode? initializerDocComments,
});
}
/// The API used by [Macro]s to augment an enum entry.
abstract interface class EnumValueDefinitionBuilder
implements DefinitionBuilder {
/// Augments the entry by replacing it with a new one.
///
/// The name of the produced [entry] must match the original name.
void augment(DeclarationCode entry);
}
-504
View File
@@ -1,504 +0,0 @@
// 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.
part of '../api.dart';
/// The base class representing an arbitrary chunk of Dart code, which may or
/// may not be syntactically or semantically valid yet.
sealed class Code {
/// All the chunks of [Code], raw [String]s, [Identifier]s, or
/// [OmittedTypeAnnotation]s that comprise this [Code] object.
///
/// Note that [OmittedTypeAnnotation] objects can only be provided through
/// the [OmittedTypeAnnotationCode] wrapper instance, but will appear in
/// the [parts] of those, so they must be handled whenever iterating [parts].
final List<Object> parts;
/// Can be used to more efficiently detect the kind of code, avoiding is
/// checks and enabling switch statements.
CodeKind get kind;
Code.fromString(String code) : parts = [code];
Code.fromParts(this.parts) {
for (final part in parts) {
switch (part) {
case Code():
case Identifier():
case String():
break; // OK
default:
throw StateError('Unrecognized code part ${part.runtimeType}');
}
}
}
}
/// An arbitrary chunk of code, which does not have to be syntactically valid
/// on its own. Useful to construct other types of code from several parts.
final class RawCode extends Code {
@override
CodeKind get kind => CodeKind.raw;
RawCode.fromString(super.code) : super.fromString();
RawCode.fromParts(super.parts) : super.fromParts();
}
/// A piece of code representing a syntactically valid declaration.
final class DeclarationCode extends Code {
@override
CodeKind get kind => CodeKind.declaration;
DeclarationCode.fromString(super.code) : super.fromString();
DeclarationCode.fromParts(super.parts) : super.fromParts();
}
/// A piece of code representing a code comment. This may contain identifier
/// references inside of `[]` brackets if the comments are doc comments.
final class CommentCode extends Code {
@override
CodeKind get kind => CodeKind.comment;
CommentCode.fromString(super.code) : super.fromString();
CommentCode.fromParts(super.parts) : super.fromParts();
}
/// A piece of code representing a syntactically valid expression.
final class ExpressionCode extends Code {
@override
CodeKind get kind => CodeKind.expression;
ExpressionCode.fromString(super.code) : super.fromString();
ExpressionCode.fromParts(super.parts) : super.fromParts();
}
/// A piece of code representing a syntactically valid function body.
///
/// This includes any and all code after the parameter list of a function,
/// including modifiers like `async`.
///
/// Both arrow and block function bodies are allowed.
final class FunctionBodyCode extends Code {
@override
CodeKind get kind => CodeKind.functionBody;
FunctionBodyCode.fromString(super.code) : super.fromString();
FunctionBodyCode.fromParts(super.parts) : super.fromParts();
}
/// A piece of code identifying a syntactically valid function or function type
/// parameter.
///
/// There is no distinction here made between named and positional parameters.
///
/// There is also no distinction between function type parameters and normal
/// function parameters, so the [name] is nullable (it is not required for
/// positional function type parameters).
///
/// It is the job of the user to construct and combine these together in a way
/// that creates valid parameter lists.
final class ParameterCode implements Code {
/// Optional default value for this parameter (not including the ` = `).
final Code? defaultValue;
/// Any keywords to put on this parameter (before the type).
final List<String> keywords;
/// Optional name of this parameter (can only be omitted for function types).
final String? name;
/// Optional type for this parameter.
final TypeAnnotationCode? type;
/// The type of parameter this is.
final ParameterStyle style;
@override
CodeKind get kind => CodeKind.parameter;
@override
List<Object> get parts => [
if (keywords.isNotEmpty) ...[
...keywords.joinAsCode(' '),
' ',
],
if (type != null) ...[
type!,
' ',
],
if (style == ParameterStyle.fieldFormal)
'this.'
else if (style == ParameterStyle.superFormal)
'super.',
if (name != null) name!,
if (defaultValue != null) ...[
' = ',
defaultValue!,
]
];
ParameterCode({
this.defaultValue,
this.keywords = const [],
this.name,
this.style = ParameterStyle.normal,
this.type,
});
}
/// A piece of code representing a type annotation.
sealed class TypeAnnotationCode implements Code, TypeAnnotation {
@override
TypeAnnotationCode get code => this;
/// Returns a [TypeAnnotationCode] object which is a non-nullable version
/// of this one.
///
/// Returns the current instance if it is already non-nullable.
TypeAnnotationCode get asNonNullable => this;
/// Returns a [TypeAnnotationCode] object which is a non-nullable version
/// of this one.
///
/// Returns the current instance if it is already nullable.
NullableTypeAnnotationCode get asNullable => NullableTypeAnnotationCode(this);
/// Whether or not this type is nullable.
@override
bool get isNullable => false;
}
/// The nullable version of an underlying type annotation.
final class NullableTypeAnnotationCode implements TypeAnnotationCode {
/// The underlying type that is being made nullable.
TypeAnnotationCode underlyingType;
@override
TypeAnnotationCode get code => this;
@override
CodeKind get kind => CodeKind.nullableTypeAnnotation;
@override
List<Object> get parts => [...underlyingType.parts, '?'];
/// Creates a nullable [underlyingType] annotation.
///
/// If [underlyingType] is a NullableTypeAnnotationCode, returns that
/// same type.
NullableTypeAnnotationCode(this.underlyingType);
@override
TypeAnnotationCode get asNonNullable => underlyingType;
@override
NullableTypeAnnotationCode get asNullable => this;
@override
bool get isNullable => true;
}
/// A piece of code representing a reference to a named type.
final class NamedTypeAnnotationCode extends TypeAnnotationCode {
final Identifier name;
final List<TypeAnnotationCode> typeArguments;
@override
CodeKind get kind => CodeKind.namedTypeAnnotation;
@override
List<Object> get parts => [
name,
if (typeArguments.isNotEmpty) ...[
'<',
...typeArguments.joinAsCode(', '),
'>',
],
];
NamedTypeAnnotationCode({required this.name, this.typeArguments = const []});
}
/// A piece of code representing a function type annotation.
final class FunctionTypeAnnotationCode extends TypeAnnotationCode {
final List<ParameterCode> namedParameters;
final List<ParameterCode> optionalPositionalParameters;
final List<ParameterCode> positionalParameters;
final TypeAnnotationCode? returnType;
final List<TypeParameterCode> typeParameters;
@override
CodeKind get kind => CodeKind.functionTypeAnnotation;
@override
List<Object> get parts => [
if (returnType != null) returnType!,
' Function',
if (typeParameters.isNotEmpty) ...[
'<',
...typeParameters.joinAsCode(', '),
'>',
],
'(',
for (ParameterCode positional in positionalParameters) ...[
positional,
', ',
],
if (optionalPositionalParameters.isNotEmpty) ...[
'[',
for (ParameterCode optional in optionalPositionalParameters) ...[
optional,
', ',
],
']',
],
if (namedParameters.isNotEmpty) ...[
'{',
for (ParameterCode named in namedParameters) ...[
named,
', ',
],
'}',
],
')',
];
FunctionTypeAnnotationCode({
this.namedParameters = const [],
this.optionalPositionalParameters = const [],
this.positionalParameters = const [],
this.returnType,
this.typeParameters = const [],
});
}
/// A piece of code identifying a syntactically valid record field declaration.
/// This is only usable in the context of [RecordTypeAnnotationCode] objects.
///
/// There is no distinction here made between named and positional fields.
///
/// The name is not required because it is optional for positional fields.
///
/// It is the job of the user to construct and combine these together in a way
/// that creates valid record type annotations.
final class RecordFieldCode implements Code {
final String? name;
final TypeAnnotationCode type;
@override
CodeKind get kind => CodeKind.recordField;
@override
List<Object> get parts => [
type,
if (name != null) ' ${name!}',
];
RecordFieldCode({
this.name,
required this.type,
});
}
/// A piece of code representing a syntactically valid record type annotation.
final class RecordTypeAnnotationCode extends TypeAnnotationCode {
final List<RecordFieldCode> namedFields;
final List<RecordFieldCode> positionalFields;
@override
CodeKind get kind => CodeKind.recordTypeAnnotation;
@override
List<Object> get parts => [
'(',
if (positionalFields.isNotEmpty)
for (RecordFieldCode positional in positionalFields) ...[
if (positional != positionalFields.first) ', ',
positional,
],
if (namedFields.isNotEmpty) ...[
if (positionalFields.isNotEmpty) ', ',
'{',
for (RecordFieldCode named in namedFields) ...[
if (named != namedFields.first) ', ',
named,
],
'}',
],
')',
];
RecordTypeAnnotationCode({
this.namedFields = const [],
this.positionalFields = const [],
});
}
final class OmittedTypeAnnotationCode extends TypeAnnotationCode {
final OmittedTypeAnnotation typeAnnotation;
OmittedTypeAnnotationCode(this.typeAnnotation);
@override
CodeKind get kind => CodeKind.omittedTypeAnnotation;
@override
List<Object> get parts => [typeAnnotation];
}
/// Raw type annotations are typically used to refer to a local type which you
/// do not have an [Identifier] for (possibly you just created it).
///
/// Whenever possible, use a more specific [TypeAnnotationCode] subtype.
final class RawTypeAnnotationCode extends RawCode
implements TypeAnnotationCode {
@override
CodeKind get kind => CodeKind.rawTypeAnnotation;
/// Returns a [TypeAnnotationCode] object which is a non-nullable version
/// of this one.
///
/// Returns the current instance if it is already non-nullable.
@override
TypeAnnotationCode get asNonNullable => this;
/// Returns a [TypeAnnotationCode] object which is a non-nullable version
/// of this one.
///
/// Returns the current instance if it is already nullable.
@override
NullableTypeAnnotationCode get asNullable => NullableTypeAnnotationCode(this);
RawTypeAnnotationCode._(super.parts) : super.fromParts();
/// Creates a [TypeAnnotationCode] from a raw [String].
///
/// The [code] object must not have trailing whitespace.
static TypeAnnotationCode fromString(String code) => fromParts([code]);
/// Creates a [TypeAnnotationCode] from a raw code [parts].
///
/// Must not end in trailing whitespace.
static TypeAnnotationCode fromParts(List<Object> parts) {
bool wasNullable;
(wasNullable, parts) = _makeNonNullable(parts);
TypeAnnotationCode code = RawTypeAnnotationCode._(parts);
if (wasNullable) code = code.asNullable;
return code;
}
@override
TypeAnnotationCode get code => this;
@override
bool get isNullable => false;
/// Checks if [parts] ends with a ?, and if so then it is removed.
///
/// Returns a record which indicates if [parts] was nullable originally, as
/// well as the potentially new list of parts.
///
/// Throws if [parts] ends with whitespace because we don't allow type
/// annotations to do that.
static (bool wasNullable, List<Object> parts) _makeNonNullable(
List<Object> parts) {
final Iterator<Object> iterator = parts.reversed.iterator;
while (iterator.moveNext()) {
final Object current = iterator.current;
switch (current) {
case String():
if (current.trimRight() != current) {
throw ArgumentError(
'Invalid type annotation, type annotations should not end with '
'whitespace but got `$current`.');
} else if (current.isEmpty) {
continue;
} else if (current.endsWith('?')) {
// It was nullable, trim the `?` and return a copy.
return (
true,
// We are iterating backwards, and need to reverse it after.
[
// Strip the '?'.
current.substring(0, current.length - 1),
for (bool hasNext = iterator.moveNext();
hasNext;
hasNext = iterator.moveNext())
iterator.current,
].reversed.toList(),
);
} else {
return (false, parts);
}
case Identifier():
// Identifiers never contain a `?`.
return (false, parts);
}
}
throw ArgumentError('The empty string is not a valid type annotation.');
}
}
/// A piece of code representing a valid named type parameter.
final class TypeParameterCode implements Code {
final TypeAnnotationCode? bound;
final String name;
@override
CodeKind get kind => CodeKind.typeParameter;
@override
List<Object> get parts => [
name,
if (bound != null) ...[
' extends ',
bound!,
]
];
TypeParameterCode({this.bound, required this.name});
}
extension Join<T extends Object> on List<T> {
/// Joins all the items in this [Join] with [separator], and returns a new
/// list.
///
/// Works on any kind of non-nullable list which accepts String entries, and
/// does not convert the individual items to strings.
List<Object> joinAsCode(String separator) => [
for (int i = 0; i < length - 1; i++) ...[
this[i],
separator,
],
if (isNotEmpty) last,
];
}
enum CodeKind {
comment,
declaration,
expression,
functionBody,
functionTypeAnnotation,
namedTypeAnnotation,
nullableTypeAnnotation,
omittedTypeAnnotation,
parameter,
raw,
rawTypeAnnotation,
recordField,
recordTypeAnnotation,
typeParameter,
}
-108
View File
@@ -1,108 +0,0 @@
// Copyright (c) 2023, 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.
part of '../api.dart';
/// A diagnostic reported from a [Macro].
class Diagnostic {
/// Additional [DiagnosticMessage]s related to this one, to help with the
/// context.
final Iterable<DiagnosticMessage> contextMessages;
/// An optional message describing to the user how they might fix this
/// diagnostic.
final String? correctionMessage;
/// The primary message for this diagnostic.
final DiagnosticMessage message;
/// The severity of this diagnostic.
final Severity severity;
/// General diagnostics for the current macro application.
///
/// These will be attached to the macro application itself.
Diagnostic(this.message, this.severity,
{List<DiagnosticMessage> contextMessages = const [],
this.correctionMessage})
: contextMessages = UnmodifiableListView(contextMessages);
}
/// A message and optional target for a [Diagnostic] reported by a [Macro].
class DiagnosticMessage {
/// The primary message for this diagnostic message.
final String message;
/// The optional target for this diagnostic message.
///
/// If provided, the diagnostic should be linked to this target.
///
/// If not provided, it should be implicitly linked to the macro application
/// that generated this diagnostic.
final DiagnosticTarget? target;
DiagnosticMessage(this.message, {this.target});
}
/// A target for a [DiagnosticMessage]. We use a sealed class to represent a
/// union type of the valid target types.
sealed class DiagnosticTarget {}
/// A [DiagnosticMessage] target which is a [Declaration].
final class DeclarationDiagnosticTarget extends DiagnosticTarget {
final Declaration declaration;
DeclarationDiagnosticTarget(this.declaration);
}
/// A simplified way of creating a [DiagnosticTarget] target for a
/// [Declaration].
extension DeclarationAsTarget on Declaration {
DeclarationDiagnosticTarget get asDiagnosticTarget =>
DeclarationDiagnosticTarget(this);
}
/// A [DiagnosticMessage] target which is a [TypeAnnotation].
final class TypeAnnotationDiagnosticTarget extends DiagnosticTarget {
final TypeAnnotation typeAnnotation;
TypeAnnotationDiagnosticTarget(this.typeAnnotation);
}
/// A simplified way of creating a [DiagnosticTarget] target for a
/// [TypeAnnotation].
extension TypeAnnotationAsTarget on TypeAnnotation {
TypeAnnotationDiagnosticTarget get asDiagnosticTarget =>
TypeAnnotationDiagnosticTarget(this);
}
/// A [DiagnosticMessage] target which is a [MetadataAnnotation].
final class MetadataAnnotationDiagnosticTarget extends DiagnosticTarget {
final MetadataAnnotation metadataAnnotation;
MetadataAnnotationDiagnosticTarget(this.metadataAnnotation);
}
extension MetadataAnnotationAsTarget on MetadataAnnotation {
MetadataAnnotationDiagnosticTarget get asDiagnosticTarget =>
MetadataAnnotationDiagnosticTarget(this);
}
/// The severities supported for [Diagnostic]s.
enum Severity {
/// Informational message only, for example a style guideline is not being
/// followed. These may not always be shown to the user depending on how the
/// app is being compiled and with what flags.
info,
/// Not a critical failure, but something is likely wrong and the code should
/// be changed. Always shown to the user by default, but may be silenceable by
/// some tools.
warning,
/// Critical failure, the macro could not proceed. Cannot be silenced and will
/// always prevent the app from compiling successfully. These are always shown
/// to the user and cannot be silenced.
error,
}
-58
View File
@@ -1,58 +0,0 @@
// Copyright (c) 2024, 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.
part of '../api.dart';
/// Exception for use in macro implementations.
///
/// Throw to stop the current macro execution and report a [Diagnostic].
class DiagnosticException implements Exception {
final Diagnostic diagnostic;
DiagnosticException(this.diagnostic);
}
/// Base class for exceptions thrown by the host implementation during macro
/// execution.
///
/// Macro implementations can catch these exceptions to provide more
/// information to the user. In case an exception results from user error, they
/// can provide a pointer to the likely fix. If the exception results from an
/// implementation error or unknown error, the macro implementation might give
/// the user information on where and how to file an issue.
///
/// If a `MacroException` is not caught by a macro implementation then it will
/// be reported in a user-oriented way, for example for
/// `MacroImplementationException` the displayed message suggests that there
/// is a bug in the macro implementation.
abstract interface class MacroException implements Exception {
String get message;
String? get stackTrace;
}
/// Something unexpected happened during macro execution.
///
/// For example, a bug in the SDK.
abstract interface class UnexpectedMacroException implements MacroException {}
/// An error due to incorrect implementation was thrown during macro execution.
///
/// For example, an incorrect argument was passed to the macro API.
///
/// The type `Error` is usually used for such throwables, and it's common to
/// allow the program to crash when one is thrown.
///
/// In the case of macros, however, type `Exception` is used because the macro
/// implementation can usefully catch it in order to give the user information
/// about how to notify the macro author about the bug.
abstract interface class MacroImplementationException
implements MacroException {}
/// A cycle was detected in macro applications introspecting targets of other
/// macro applications.
///
/// The order the macros should run in is not defined, so allowing
/// introspection in this case would make the macro output non-deterministic.
/// Instead, all the introspection calls in the cycle fail with this exception.
abstract interface class MacroIntrospectionCycleException
implements MacroException {}
-487
View File
@@ -1,487 +0,0 @@
// 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.
part of '../api.dart';
/// The interface for classes that can be targeted by macros.
///
/// Could be a [Declaration] or [Library].
abstract interface class MacroTarget {}
/// The interface for things that can be annotated with [MetadataAnnotation]s.
abstract interface class Annotatable {
Iterable<MetadataAnnotation> get metadata;
}
/// A concrete reference to a named declaration, which may or may not yet be
/// resolved.
///
/// These can be passed directly to [Code] objects, which will automatically do
/// any necessary prefixing when emitting references.
///
/// Identifier equality/identity is not specified. To check for type equality, a
/// [StaticType] should be used.
abstract interface class Identifier {
String get name;
}
/// The interface for an unresolved reference to a type.
///
/// See the subtypes [FunctionTypeAnnotation] and [NamedTypeAnnotation].
abstract interface class TypeAnnotation {
/// Whether or not the type annotation is explicitly nullable (contains a
/// trailing `?`)
bool get isNullable;
/// A convenience method to get a [Code] object equivalent to this type
/// annotation.
TypeAnnotationCode get code;
}
/// The interface for function type declarations.
abstract interface class FunctionTypeAnnotation implements TypeAnnotation {
/// The return type of this function.
TypeAnnotation get returnType;
/// The positional parameters for this function.
Iterable<FormalParameter> get positionalParameters;
/// The named parameters for this function.
Iterable<FormalParameter> get namedParameters;
/// The type parameters for this function.
Iterable<TypeParameter> get typeParameters;
}
/// An unresolved reference to a type.
///
/// These can be resolved to a [TypeDeclaration] using the `builder` classes
/// depending on the phase a macro is running in.
abstract interface class NamedTypeAnnotation implements TypeAnnotation {
/// An identifier pointing to this named type.
Identifier get identifier;
/// The type arguments, if applicable.
Iterable<TypeAnnotation> get typeArguments;
}
/// The interface for record type annotations.
abstract interface class RecordTypeAnnotation implements TypeAnnotation {
/// The positional fields for this record.
Iterable<RecordField> get positionalFields;
/// The named fields for this record.
Iterable<RecordField> get namedFields;
}
/// An omitted type annotation.
///
/// This will be given whenever there is no explicit type annotation for a
/// declaration.
///
/// These type annotations can still produce valid [Code] objects, which will
/// result in the inferred type being emitted into the resulting code (or
/// dynamic).
///
/// In the definition phase, you may also ask explicitly for the inferred type
/// using the `inferType` API.
abstract interface class OmittedTypeAnnotation implements TypeAnnotation {}
/// The interface representing a resolved type.
///
/// Resolved types understand exactly what type they represent, and can be
/// compared to other static types.
abstract interface class StaticType {
/// Returns true if this is a subtype of [other].
Future<bool> isSubtypeOf(covariant StaticType other);
/// Returns true if this is an identical type to [other].
Future<bool> isExactly(covariant StaticType other);
/// Returns a [NamedStaticType] having a [NamedStaticType.declaration] equal
/// to the [declaration] passed here, while also being a supertype of `this`
/// type.
///
/// This is useful to obtain the type arguments required for a known
/// superclass. Consider a class defined as `class MyMap implements
/// Map<Foo, Bar>` and a macro interested in dealing with maps. Once that
/// macro has resolved `MyMap` to a static type, it would call [asInstanceOf]
/// with the type declaration of the `Map` type from `dart:core` to obtain the
/// [NamedStaticType.typeArguments] required on `Map` to be a supertype of
/// `MyMap` (`Foo` and `Bar`, in this case).
///
/// To query whether this type is a subtype of a given type declaration, it
/// is easier to resolve that type and then call [isSubtypeOf].
///
/// Returns null if there is no instantiation of [declaration] that is a
/// supertype of `this`.
Future<NamedStaticType?> asInstanceOf(TypeDeclaration declaration);
}
/// A subtype of [StaticType] representing types that can be resolved by name
/// to a concrete declaration.
abstract interface class NamedStaticType implements StaticType {
/// The [ParameterizedTypeDeclaration] declaring this type.
ParameterizedTypeDeclaration get declaration;
/// The type arguments passed to [declaration] to obtain this type.
List<StaticType> get typeArguments;
}
/// The interface for all declarations.
abstract interface class Declaration implements Annotatable, MacroTarget {
/// The library in which this declaration is defined.
Library get library;
/// An identifier pointing to this named declaration.
Identifier get identifier;
}
/// Interface for all Declarations which are a member of a surrounding type
/// declaration.
abstract interface class MemberDeclaration implements Declaration {
/// The type that defines this member.
Identifier get definingType;
/// Whether or not member has the `static` keyword.
bool get hasStatic;
}
/// Marker interface for a declaration that defines a new type in the program.
///
/// See [ParameterizedTypeDeclaration] and [TypeParameterDeclaration].
abstract interface class TypeDeclaration implements Declaration {}
/// A [TypeDeclaration] which may have type parameters.
///
/// See subtypes [ClassDeclaration], [EnumDeclaration], [MixinDeclaration], and
/// [TypeAliasDeclaration].
abstract interface class ParameterizedTypeDeclaration
implements TypeDeclaration {
/// The type parameters defined for this type declaration.
Iterable<TypeParameterDeclaration> get typeParameters;
}
/// Class introspection information.
///
/// Information about fields, methods, and constructors must be retrieved from
/// the `builder` objects.
abstract interface class ClassDeclaration
implements ParameterizedTypeDeclaration {
/// Whether this class has an `abstract` modifier.
bool get hasAbstract;
/// Whether this class has a `base` modifier.
bool get hasBase;
/// Whether this class has an `external` modifier.
bool get hasExternal;
/// Whether this class has a `final` modifier.
bool get hasFinal;
/// Whether this class has an `interface` modifier.
bool get hasInterface;
/// Whether this class has a `mixin` modifier.
bool get hasMixin;
/// Whether this class has a `sealed` modifier.
bool get hasSealed;
/// The `extends` type annotation, if present.
NamedTypeAnnotation? get superclass;
/// All the `implements` type annotations.
Iterable<NamedTypeAnnotation> get interfaces;
/// All the `with` type annotations.
Iterable<NamedTypeAnnotation> get mixins;
}
/// Enum introspection information.
///
/// Information about values, fields, methods, and constructors must be
/// retrieved from the `builder` objects.
abstract interface class EnumDeclaration
implements ParameterizedTypeDeclaration {
/// All the `implements` type annotations.
Iterable<NamedTypeAnnotation> get interfaces;
/// All the `with` type annotations.
Iterable<NamedTypeAnnotation> get mixins;
}
/// Enum entry introspection information.
///
/// Note that enum values are not introspectable, because they can be augmented.
///
/// You can however do const evaluation of enum values, if they are not in a
/// library cycle with the current library.
abstract interface class EnumValueDeclaration implements Declaration {
/// The enum that surrounds this entry.
Identifier get definingEnum;
}
/// The class for introspecting on an extension.
///
/// Note that extensions do not actually introduce a new type, but we model them
/// as [ParameterizedTypeDeclaration]s anyway, because they generally look
/// exactly like other type declarations, and are treated the same.
abstract interface class ExtensionDeclaration
implements ParameterizedTypeDeclaration, Declaration {
/// The type that appears on the `on` clause of this extension.
TypeAnnotation get onType;
}
/// The class for introspecting on an extension type.
abstract interface class ExtensionTypeDeclaration
implements ParameterizedTypeDeclaration, Declaration {
/// The representation type of this extension type.
TypeAnnotation get representationType;
}
/// Mixin introspection information.
///
/// Information about fields and methods must be retrieved from the `builder`
/// objects.
abstract interface class MixinDeclaration
implements ParameterizedTypeDeclaration {
/// Whether this mixin has a `base` modifier.
bool get hasBase;
/// All the `implements` type annotations.
Iterable<NamedTypeAnnotation> get interfaces;
/// All the `on` clause type annotations.
Iterable<NamedTypeAnnotation> get superclassConstraints;
}
/// Type alias introspection information.
abstract interface class TypeAliasDeclaration
implements ParameterizedTypeDeclaration {
/// The type annotation this is an alias for.
TypeAnnotation get aliasedType;
}
/// Function introspection information.
abstract interface class FunctionDeclaration implements Declaration {
/// Whether or not this function has a body.
///
/// This is useful when augmenting a function, so you know whether an
/// `augment super` call would be valid or not.
///
/// Note that for external functions, this may return `false` even though
/// there is actually a body that is filled in later by another tool.
bool get hasBody;
/// Whether this function has an `external` modifier.
bool get hasExternal;
/// Whether this function is an operator.
bool get isOperator;
/// Whether this function is actually a getter.
bool get isGetter;
/// Whether this function is actually a setter.
bool get isSetter;
/// The return type of this function.
TypeAnnotation get returnType;
/// The positional parameters for this function.
Iterable<FormalParameterDeclaration> get positionalParameters;
/// The named parameters for this function.
Iterable<FormalParameterDeclaration> get namedParameters;
/// The type parameters for this function.
Iterable<TypeParameterDeclaration> get typeParameters;
}
/// Method introspection information.
abstract interface class MethodDeclaration
implements FunctionDeclaration, MemberDeclaration {}
/// Constructor introspection information.
abstract interface class ConstructorDeclaration implements MethodDeclaration {
/// Whether or not this constructor is const.
bool get isConst;
/// Whether or not this is a factory constructor.
bool get isFactory;
}
/// Variable introspection information.
abstract interface class VariableDeclaration implements Declaration {
/// Whether this variable has a `const` modifier.
bool get hasConst;
/// Whether this variable has an `external` modifier.
bool get hasExternal;
/// Whether this variable has a `final` modifier.
bool get hasFinal;
/// Whether this variable has an initializer at its declaration.
bool get hasInitializer;
/// Whether this variable has a `late` modifier.
bool get hasLate;
/// The type of this field.
TypeAnnotation get type;
}
/// Field introspection information.
abstract interface class FieldDeclaration
implements VariableDeclaration, MemberDeclaration {
/// Whether this field has an `abstract` modifier.
bool get hasAbstract;
}
/// General parameter introspection information, for both function type
/// parameters and regular parameters.
///
/// See the subtype [FormalParameterDeclaration] as well, for regular
/// parameters which are not a part of a function type.
abstract interface class FormalParameter implements Annotatable {
/// The type of this parameter.
TypeAnnotation get type;
/// Whether or not this is a named parameter.
bool get isNamed;
/// Whether or not this parameter is either a non-optional positional
/// parameter or an optional parameter with the `required` keyword.
bool get isRequired;
/// The name of this parameter, if present.
///
/// Specifically, function type parameters may not have a name.
String? get name;
/// The style of parameter this is.
ParameterStyle get style;
/// A convenience method to get a `code` object equivalent to this parameter.
///
/// Note that the original default value will not be included, as it is not a
/// part of this API.
ParameterCode get code;
}
/// The different kinds of parameters.
enum ParameterStyle {
/// A standard parameter.
normal,
/// A `this.` style parameter, otherwise known as an initializing formal
/// parameter.
fieldFormal,
/// A `super.` style parameter.
superFormal,
}
/// Parameters of normal functions/methods, which always have an identifier, and
/// declare a new variable in scope.
///
/// These may also be `this.` or `super.` parameters.
abstract interface class FormalParameterDeclaration
implements FormalParameter, Declaration {
@override
String get name;
}
/// Generic type parameter introspection information.
///
/// Not all type parameters introduce new declarations that can be referenced,
/// but those that do will implement the [TypeParameterDeclaration] interface.
abstract interface class TypeParameter implements Annotatable {
/// The bound for this type parameter, if it has any.
TypeAnnotation? get bound;
/// The name of this type parameter.
String get name;
/// A convenience method to get a `code` object equivalent to this type
/// parameter.
TypeParameterCode get code;
}
/// Generic type parameter introspection information for type parameters which
/// introduce a true type declaration that can be referenced.
///
/// Note that type parameters for function types cannot be referenced and only
/// implement [TypeParameter].
abstract interface class TypeParameterDeclaration
implements TypeDeclaration, TypeParameter {}
/// Introspection information for a field on a Record type.
///
/// Note that for positional fields the [identifier] will be the synthesized
/// one (`$1` etc), while for named fields it will be the declared name.
abstract interface class RecordField {
/// A convenience method to get a `code` object equivalent to this field.
RecordFieldCode get code;
/// Record fields don't always have names (if they are positional).
///
/// If you want to reference the getter for a field, you should use
/// [identifier] instead.
String? get name;
/// The type of this field.
TypeAnnotation get type;
}
/// Introspection information for a Library.
abstract interface class Library implements Annotatable, MacroTarget {
/// The language version of this library.
LanguageVersion get languageVersion;
/// The uri identifying this library.
Uri get uri;
}
/// The language version of a library, see
/// https://dart.dev/guides/language/evolution#language-version-numbers.
abstract interface class LanguageVersion {
int get major;
int get minor;
}
/// A metadata annotation on a declaration or library directive.
abstract interface class MetadataAnnotation {}
/// A [MetadataAnnotation] which is a reference to a const value.
abstract interface class IdentifierMetadataAnnotation
implements MetadataAnnotation {
/// The [Identifier] for the const reference.
Identifier get identifier;
}
/// A [MetadataAnnotation] which is a constructor call.
abstract interface class ConstructorMetadataAnnotation
implements MetadataAnnotation {
/// The [NamedTypeAnnotation] of the type that is being constructed.
///
/// If type arguments are provided, this is where they would appear.
NamedTypeAnnotation get type;
/// An [Identifier] referring to the specific constructor being called.
///
/// For unnamed constructors, the name of this identifier will be the empty
/// String.
Identifier get constructor;
/// The positional arguments of this constructor call.
Iterable<ExpressionCode> get positionalArguments;
/// The named arguments of this constructor call.
Map<String, ExpressionCode> get namedArguments;
}
-285
View File
@@ -1,285 +0,0 @@
// 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.
part of '../api.dart';
/// The marker interface for all types of macros.
abstract interface class Macro {}
/// The interface for [Macro]s that can be applied to a library directive, and
/// want to contribute new type declarations to the library.
abstract interface class LibraryTypesMacro implements Macro {
FutureOr<void> buildTypesForLibrary(Library library, TypeBuilder builder);
}
/// The interface for [Macro]s that can be applied to a library directive, and
/// want to contribute new non-type declarations to the library.
abstract interface class LibraryDeclarationsMacro implements Macro {
FutureOr<void> buildDeclarationsForLibrary(
Library library, DeclarationBuilder builder);
}
/// The interface for [Macro]s that can be applied to a library directive, and
/// want to provide definitions for declarations in the library.
abstract interface class LibraryDefinitionMacro implements Macro {
FutureOr<void> buildDefinitionForLibrary(
Library library, LibraryDefinitionBuilder builder);
}
/// The interface for [Macro]s that can be applied to any top level function,
/// instance method, or static method, and want to contribute new type
/// declarations to the program.
abstract interface class FunctionTypesMacro implements Macro {
FutureOr<void> buildTypesForFunction(
FunctionDeclaration function, TypeBuilder builder);
}
/// The interface for [Macro]s that can be applied to any top level function,
/// instance method, or static method, and want to contribute new non-type
/// declarations to the program.
abstract interface class FunctionDeclarationsMacro implements Macro {
FutureOr<void> buildDeclarationsForFunction(
FunctionDeclaration function, DeclarationBuilder builder);
}
/// The interface for [Macro]s that can be applied to any top level function,
/// instance method, or static method, and want to augment the function
/// definition.
abstract interface class FunctionDefinitionMacro implements Macro {
FutureOr<void> buildDefinitionForFunction(
FunctionDeclaration function, FunctionDefinitionBuilder builder);
}
/// The interface for [Macro]s that can be applied to any top level variable or
/// instance field, and want to contribute new type declarations to the
/// program.
abstract interface class VariableTypesMacro implements Macro {
FutureOr<void> buildTypesForVariable(
VariableDeclaration variable, TypeBuilder builder);
}
/// The interface for [Macro]s that can be applied to any top level variable or
/// instance field and want to contribute new non-type declarations to the
/// program.
abstract interface class VariableDeclarationsMacro implements Macro {
FutureOr<void> buildDeclarationsForVariable(
VariableDeclaration variable, DeclarationBuilder builder);
}
/// The interface for [Macro]s that can be applied to any top level variable
/// or instance field, and want to augment the variable definition.
abstract interface class VariableDefinitionMacro implements Macro {
FutureOr<void> buildDefinitionForVariable(
VariableDeclaration variable, VariableDefinitionBuilder builder);
}
/// The interface for [Macro]s that can be applied to any class, and want to
/// contribute new type declarations to the program.
abstract interface class ClassTypesMacro implements Macro {
FutureOr<void> buildTypesForClass(
ClassDeclaration clazz, ClassTypeBuilder builder);
}
/// The interface for [Macro]s that can be applied to any class, and want to
/// contribute new non-type declarations to the program.
abstract interface class ClassDeclarationsMacro implements Macro {
FutureOr<void> buildDeclarationsForClass(
ClassDeclaration clazz, MemberDeclarationBuilder builder);
}
/// The interface for [Macro]s that can be applied to any class, and want to
/// augment the definitions of the members of that class.
abstract interface class ClassDefinitionMacro implements Macro {
FutureOr<void> buildDefinitionForClass(
ClassDeclaration clazz, TypeDefinitionBuilder builder);
}
/// The interface for [Macro]s that can be applied to any enum, and want to
/// contribute new type declarations to the program.
abstract interface class EnumTypesMacro implements Macro {
FutureOr<void> buildTypesForEnum(
EnumDeclaration enuum, EnumTypeBuilder builder);
}
/// The interface for [Macro]s that can be applied to any enum, and want to
/// contribute new non-type declarations to the program.
abstract interface class EnumDeclarationsMacro implements Macro {
FutureOr<void> buildDeclarationsForEnum(
EnumDeclaration enuum, EnumDeclarationBuilder builder);
}
/// The interface for [Macro]s that can be applied to any enum, and want to
/// augment the definitions of members or values of that enum.
abstract interface class EnumDefinitionMacro implements Macro {
FutureOr<void> buildDefinitionForEnum(
EnumDeclaration enuum, EnumDefinitionBuilder builder);
}
/// The interface for [Macro]s that can be applied to any enum, and want to
/// contribute new type declarations to the program.
abstract interface class EnumValueTypesMacro implements Macro {
FutureOr<void> buildTypesForEnumValue(
EnumValueDeclaration entry, TypeBuilder builder);
}
/// The interface for [Macro]s that can be applied to any enum, and want to
/// contribute new non-type declarations to the program.
abstract interface class EnumValueDeclarationsMacro implements Macro {
FutureOr<void> buildDeclarationsForEnumValue(
EnumValueDeclaration entry, EnumDeclarationBuilder builder);
}
/// The interface for [Macro]s that can be applied to any enum, and want to
/// augment the definitions of members or values of that enum.
abstract interface class EnumValueDefinitionMacro implements Macro {
FutureOr<void> buildDefinitionForEnumValue(
EnumValueDeclaration entry, EnumValueDefinitionBuilder builder);
}
/// The interface for [Macro]s that can be applied to any field, and want to
/// contribute new type declarations to the program.
abstract interface class FieldTypesMacro implements Macro {
FutureOr<void> buildTypesForField(
FieldDeclaration field, TypeBuilder builder);
}
/// The interface for [Macro]s that can be applied to any field, and want to
/// contribute new type declarations to the program.
abstract interface class FieldDeclarationsMacro implements Macro {
FutureOr<void> buildDeclarationsForField(
FieldDeclaration field, MemberDeclarationBuilder builder);
}
/// The interface for [Macro]s that can be applied to any field, and want to
/// augment the field definition.
abstract interface class FieldDefinitionMacro implements Macro {
FutureOr<void> buildDefinitionForField(
FieldDeclaration field, VariableDefinitionBuilder builder);
}
/// The interface for [Macro]s that can be applied to any method, and want to
/// contribute new type declarations to the program.
abstract interface class MethodTypesMacro implements Macro {
FutureOr<void> buildTypesForMethod(
MethodDeclaration method, TypeBuilder builder);
}
/// The interface for [Macro]s that can be applied to any method, and want to
/// contribute new non-type declarations to the program.
abstract interface class MethodDeclarationsMacro implements Macro {
FutureOr<void> buildDeclarationsForMethod(
MethodDeclaration method, MemberDeclarationBuilder builder);
}
/// The interface for [Macro]s that can be applied to any method, and want to
/// augment the function definition.
abstract interface class MethodDefinitionMacro implements Macro {
FutureOr<void> buildDefinitionForMethod(
MethodDeclaration method, FunctionDefinitionBuilder builder);
}
/// The interface for [Macro]s that can be applied to any constructor, and want
/// to contribute new type declarations to the program.
abstract interface class ConstructorTypesMacro implements Macro {
FutureOr<void> buildTypesForConstructor(
ConstructorDeclaration constructor, TypeBuilder builder);
}
/// The interface for [Macro]s that can be applied to any constructors, and
/// want to contribute new non-type declarations to the program.
abstract interface class ConstructorDeclarationsMacro implements Macro {
FutureOr<void> buildDeclarationsForConstructor(
ConstructorDeclaration constructor, MemberDeclarationBuilder builder);
}
/// The interface for [Macro]s that can be applied to any constructor, and want
/// to augment the function definition.
abstract interface class ConstructorDefinitionMacro implements Macro {
FutureOr<void> buildDefinitionForConstructor(
ConstructorDeclaration constructor, ConstructorDefinitionBuilder builder);
}
/// The interface for [Macro]s that can be applied to any mixin declaration, and
/// want to contribute new type declarations to the program.
abstract interface class MixinTypesMacro implements Macro {
FutureOr<void> buildTypesForMixin(
MixinDeclaration mixin, MixinTypeBuilder builder);
}
/// The interface for [Macro]s that can be applied to any mixin declaration, and
/// want to contribute new non-type declarations to the program.
abstract interface class MixinDeclarationsMacro implements Macro {
FutureOr<void> buildDeclarationsForMixin(
MixinDeclaration mixin, MemberDeclarationBuilder builder);
}
/// The interface for [Macro]s that can be applied to any mixin declaration, and
/// want to augment the definitions of the members of that mixin.
abstract interface class MixinDefinitionMacro implements Macro {
FutureOr<void> buildDefinitionForMixin(
MixinDeclaration mixin, TypeDefinitionBuilder builder);
}
/// The interface for [Macro]s that can be applied to any extension declaration,
/// and want to contribute new type declarations to the program.
abstract interface class ExtensionTypesMacro implements Macro {
FutureOr<void> buildTypesForExtension(
ExtensionDeclaration extension, TypeBuilder builder);
}
/// The interface for [Macro]s that can be applied to any extension declaration,
/// and want to contribute new non-type declarations to the program.
abstract interface class ExtensionDeclarationsMacro implements Macro {
FutureOr<void> buildDeclarationsForExtension(
ExtensionDeclaration extension, MemberDeclarationBuilder builder);
}
/// The interface for [Macro]s that can be applied to any extension declaration,
/// and want to augment the definitions of the members of that extension.
abstract interface class ExtensionDefinitionMacro implements Macro {
FutureOr<void> buildDefinitionForExtension(
ExtensionDeclaration extension, TypeDefinitionBuilder builder);
}
/// The interface for [Macro]s that can be applied to any extension type
/// declaration, and want to contribute new type declarations to the program.
abstract interface class ExtensionTypeTypesMacro implements Macro {
FutureOr<void> buildTypesForExtensionType(
ExtensionTypeDeclaration extension, TypeBuilder builder);
}
/// The interface for [Macro]s that can be applied to any extension type
/// declaration, and want to contribute new non-type declarations to the
/// program.
abstract interface class ExtensionTypeDeclarationsMacro implements Macro {
FutureOr<void> buildDeclarationsForExtensionType(
ExtensionTypeDeclaration extension, MemberDeclarationBuilder builder);
}
/// The interface for [Macro]s that can be applied to any extension type
/// declaration, and want to augment the definitions of the members of that
/// extension.
abstract interface class ExtensionTypeDefinitionMacro implements Macro {
FutureOr<void> buildDefinitionForExtensionType(
ExtensionTypeDeclaration extension, TypeDefinitionBuilder builder);
}
/// The interface for [Macro]s that can be applied to any type alias
/// declaration, and want to contribute new type declarations to the program.
abstract interface class TypeAliasTypesMacro implements Macro {
FutureOr<void> buildTypesForTypeAlias(
TypeAliasDeclaration declaration,
TypeBuilder builder,
);
}
/// The interface for [Macro]s that can be applied to any type alias
/// declaration, and want to contribute new non-type declarations to the
/// program.
abstract interface class TypeAliasDeclarationsMacro implements Macro {
FutureOr<void> buildDeclarationsForTypeAlias(
TypeAliasDeclaration declaration,
DeclarationBuilder builder,
);
}
-67
View File
@@ -1,67 +0,0 @@
// 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 'executor/serialization.dart'
show SerializationMode, SerializationModeHelpers;
/// Generates a Dart program for a given set of macros, which can be compiled
/// and then passed as a precompiled kernel file to `MacroExecutor.loadMacro`.
///
/// The [macroDeclarations] is a map from library URIs to macro classes for the
/// macros supported. The macro classes are provided as a map from macro class
/// names to the names of the macro class constructors.
///
/// The [serializationMode] must be a client variant.
String bootstrapMacroIsolate(
Map<String, Map<String, List<String>>> macroDeclarations,
SerializationMode serializationMode) {
StringBuffer imports = StringBuffer();
StringBuffer constructorEntries = StringBuffer();
macroDeclarations
.forEach((String macroImport, Map<String, List<String>> macroClasses) {
imports.writeln('import \'$macroImport\';');
constructorEntries.writeln("Uri.parse('$macroImport'): {");
macroClasses.forEach((String macroName, List<String> constructorNames) {
constructorEntries.writeln("'$macroName': {");
for (String constructor in constructorNames) {
constructorEntries.writeln("'$constructor': "
"$macroName.${constructor.isEmpty ? 'new' : constructor},");
}
constructorEntries.writeln('},');
});
constructorEntries.writeln('},');
});
return template
.replaceFirst(_importMarker, imports.toString())
.replaceFirst(
_macroConstructorEntriesMarker, constructorEntries.toString())
.replaceFirst(_modeMarker, serializationMode.asCode);
}
const String _importMarker = '{{IMPORT}}';
const String _macroConstructorEntriesMarker = '{{MACRO_CONSTRUCTOR_ENTRIES}}';
const String _modeMarker = '{{SERIALIZATION_MODE}}';
const String template = '''
import 'dart:io';
import 'dart:isolate';
import 'package:_macros/src/executor/client.dart';
import 'package:_macros/src/executor/serialization.dart';
$_importMarker
/// Entrypoint to be spawned with [Isolate.spawnUri] or [Process.start].
///
/// Supports the client side of the macro expansion protocol.
void main(List<String> arguments, [SendPort? sendPort]) async {
await MacroExpansionClient.start(
$_modeMarker, _macroConstructors, arguments, sendPort);
}
/// Maps libraries by uri to macros by name, and then constructors by name.
final _macroConstructors = <Uri, Map<String, Map<String, Function>>>{
$_macroConstructorEntriesMarker
};
''';
-6
View File
@@ -1,6 +0,0 @@
// Copyright (c) 2024, 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.
export 'executor/client.dart';
export 'executor/serialization.dart';
-223
View File
@@ -1,223 +0,0 @@
// 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 'executor/serialization_extensions.dart';
import 'api.dart';
import 'executor/cast.dart';
import 'executor/introspection_impls.dart';
import 'executor/serialization.dart';
import 'executor/span.dart';
part 'executor/arguments.dart';
/// The interface used by Dart language implementations, in order to load
/// and execute macros, as well as produce library augmentations from those
/// macro applications.
///
/// This class more clearly defines the role of a Dart language implementation
/// during macro discovery and expansion, and unifies how augmentation libraries
/// are produced.
abstract class MacroExecutor {
/// Creates an instance of the macro [name] from [library] in the executor,
/// and returns an identifier for that instance.
///
/// Throws an exception if an instance is not created.
///
/// Instances may be re-used throughout a single build, but should be
/// re-created on subsequent builds (even incremental ones).
Future<MacroInstanceIdentifier> instantiateMacro(
Uri library, String name, String constructor, Arguments arguments);
/// Disposes a macro [instance] by its identifier.
///
/// All macros should be disposed once expanded to prevent memory leaks in the
/// client macro executor.
///
/// This is a fire and forget API, it does not happen synchronously but there
/// is no reason to wait for it to complete, and the client does not send a
/// response.
void disposeMacro(MacroInstanceIdentifier instance);
/// Runs the type phase for [macro] on a given [declaration].
///
/// Throws an exception if there is an error executing the macro.
Future<MacroExecutionResult> executeTypesPhase(MacroInstanceIdentifier macro,
MacroTarget target, TypePhaseIntrospector introspector);
/// 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,
MacroTarget target,
DeclarationPhaseIntrospector introspector);
/// Runs the definitions phase for [macro] on a given [declaration].
///
/// Throws an exception if there is an error executing the macro.
Future<MacroExecutionResult> executeDefinitionsPhase(
MacroInstanceIdentifier macro,
MacroTarget target,
DefinitionPhaseIntrospector introspector);
/// Combines multiple [MacroExecutionResult]s into a single library
/// augmentation file, and returns a [String] representing that file.
///
/// The [resolveDeclaration] argument should return the [TypeDeclaration] for
/// an [Identifier] pointing at a named type in the library being augmented
/// (note this could be a type that was added in the "types" phase).
///
/// The [resolveIdentifier] argument should return the import uri to be used
/// for that identifier.
///
/// The [inferOmittedType] argument is used to get the inferred type for a
/// given [OmittedTypeAnnotation].
///
/// If [omittedTypes] is provided, [inferOmittedType] is allowed to return
/// `null` for types that have not yet been inferred. In this case a fresh
/// name will be used for the omitted type in the generated library code and
/// the omitted type will be mapped to the fresh name in [omittedTypes].
///
/// The generated library files content must be deterministic, including the
/// generation of fresh names for import prefixes and omitted types.
///
/// If [spans] is provided, the [Span]s for the generated source are added
/// to [spans]. This is used to compute the offset relation between
/// intermediate augmentation libraries and the merged augmentation library.
String buildAugmentationLibrary(
Uri augmentedLibraryUri,
Iterable<MacroExecutionResult> macroResults,
TypeDeclaration Function(Identifier) resolveDeclaration,
ResolvedIdentifier Function(Identifier) resolveIdentifier,
TypeAnnotation? Function(OmittedTypeAnnotation) inferOmittedType,
{Map<OmittedTypeAnnotation, String>? omittedTypes,
List<Span>? spans});
/// Tell the executor to shut down and clean up any resources it may have
/// allocated.
Future<void> close();
}
/// A resolved [Identifier], this is used when creating augmentation libraries
/// to qualify identifiers where needed.
class ResolvedIdentifier implements Identifier {
/// The import URI for the library that defines the member that is referenced
/// by this identifier.
///
/// If this identifier is an instance member or a built-in type, like
/// `void`, [uri] is `null`.
final Uri? uri;
/// Type of identifier this is (instance, static, top level).
final IdentifierKind kind;
/// The unqualified name of this identifier.
@override
final String name;
/// If this is a static member, then the name of the fully qualified scope
/// surrounding this member. Should not contain a trailing `.`.
///
/// Typically this would just be the name of a type.
final String? staticScope;
ResolvedIdentifier({
required this.kind,
required this.name,
required this.staticScope,
required this.uri,
});
}
/// The types of identifiers.
enum IdentifierKind {
instanceMember,
local, // Parameters, local variables, etc.
staticInstanceMember,
topLevelMember,
}
/// An opaque identifier for an instance of a macro class, retrieved by
/// [MacroExecutor.instantiateMacro].
///
/// Used to execute or reload this macro in the future.
abstract class MacroInstanceIdentifier implements Serializable {
/// Whether or not this instance should run in [phase] on [declarationKind].
///
/// Attempting to execute a macro in a phase it doesn't support, or on a
/// declaration kind it doesn't support is an error.
bool shouldExecute(DeclarationKind declarationKind, Phase phase);
/// Whether or not this macro supports [declarationKind] in any phase.
bool supportsDeclarationKind(DeclarationKind declarationKind);
}
/// A summary of the results of running a macro in a given phase.
///
/// All modifications are expressed in terms of library augmentation
/// declarations.
abstract class MacroExecutionResult implements Serializable {
/// All [Diagnostic]s reported as a result of executing a macro.
List<Diagnostic> get diagnostics;
/// If execution was stopped by an exception, the exception.
MacroException? get exception;
/// Any augmentations to enum values that should be applied to an enum as a
/// result of executing a macro, indexed by the identifier of the enum.
Map<Identifier, Iterable<DeclarationCode>> get enumValueAugmentations;
/// Any extends clauses that should be added to types as a result of executing
/// a macro, indexed by the identifier of the augmented type declaration.
Map<Identifier, NamedTypeAnnotationCode> get extendsTypeAugmentations;
/// Any interfaces that should be added to types as a result of executing a
/// macro, indexed by the identifier of the augmented type declaration.
Map<Identifier, Iterable<TypeAnnotationCode>> get interfaceAugmentations;
/// Any augmentations that should be applied to the library as a result of
/// executing a macro.
Iterable<DeclarationCode> get libraryAugmentations;
/// Any mixins that should be added to types as a result of executing a macro,
/// indexed by the identifier of the augmented type declaration.
Map<Identifier, Iterable<TypeAnnotationCode>> get mixinAugmentations;
/// The names of any new types declared in [augmentations].
Iterable<String> get newTypeNames;
/// Any augmentations that should be applied to a class as a result of
/// executing a macro, indexed by the identifier of the class.
Map<Identifier, Iterable<DeclarationCode>> get typeAugmentations;
}
/// Each of the possible types of declarations a macro can be applied to
enum DeclarationKind {
classType,
constructor,
enumType,
enumValue,
extension,
extensionType,
field,
function,
library,
method,
mixinType,
typeAlias,
variable,
}
/// Each of the different macro execution phases.
enum Phase {
/// Only new types are added in this phase.
types,
/// New non-type declarations are added in this phase.
declarations,
/// This phase allows augmenting existing declarations.
definitions,
}
-416
View File
@@ -1,416 +0,0 @@
// Copyright (c) 2023, 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.
part of '../executor.dart';
/// Representation of an argument to a macro constructor.
sealed class Argument implements Serializable {
ArgumentKind get kind;
Object? get value;
Argument();
/// Reads the next argument from [Deserializer].
///
/// By default this will call `moveNext` on [deserializer] before reading the
/// argument kind, but this can be skipped by passing `true` for
/// [alreadyMoved].
factory Argument.deserialize(Deserializer deserializer,
{bool alreadyMoved = false}) {
if (!alreadyMoved) deserializer.moveNext();
final ArgumentKind kind = ArgumentKind.values[deserializer.expectInt()];
return switch (kind) {
ArgumentKind.string =>
StringArgument((deserializer..moveNext()).expectString()),
ArgumentKind.bool =>
BoolArgument((deserializer..moveNext()).expectBool()),
ArgumentKind.double =>
DoubleArgument((deserializer..moveNext()).expectDouble()),
ArgumentKind.int => IntArgument((deserializer..moveNext()).expectInt()),
ArgumentKind.list ||
ArgumentKind.set =>
_IterableArgument._deserialize(kind, deserializer),
ArgumentKind.map => MapArgument._deserialize(deserializer),
ArgumentKind.nil => NullArgument(),
ArgumentKind.typeAnnotation => TypeAnnotationArgument(
(deserializer..moveNext()).expectRemoteInstance()),
ArgumentKind.code =>
CodeArgument((deserializer..moveNext()).expectCode()),
// These are just for type arguments and aren't supported as actual args.
ArgumentKind.object ||
ArgumentKind.dynamic ||
ArgumentKind.num ||
ArgumentKind.nullable =>
throw StateError('Argument kind $kind is not deserializable'),
};
}
/// All subtypes should override this and call super.
@override
void serialize(Serializer serializer) {
serializer.addInt(kind.index);
}
@override
String toString() => '$runtimeType:$value';
}
final class BoolArgument extends Argument {
@override
ArgumentKind get kind => ArgumentKind.bool;
@override
final bool value;
BoolArgument(this.value);
@override
void serialize(Serializer serializer) {
super.serialize(serializer);
serializer.addBool(value);
}
}
final class DoubleArgument extends Argument {
@override
ArgumentKind get kind => ArgumentKind.double;
@override
final double value;
DoubleArgument(this.value);
@override
void serialize(Serializer serializer) {
super.serialize(serializer);
serializer.addDouble(value);
}
}
final class IntArgument extends Argument {
@override
ArgumentKind get kind => ArgumentKind.int;
@override
final int value;
IntArgument(this.value);
@override
void serialize(Serializer serializer) {
super.serialize(serializer);
serializer.addInt(value);
}
}
final class NullArgument extends Argument {
@override
ArgumentKind get kind => ArgumentKind.nil;
@override
Null get value => null;
}
final class StringArgument extends Argument {
@override
ArgumentKind get kind => ArgumentKind.string;
@override
final String value;
StringArgument(this.value);
@override
void serialize(Serializer serializer) {
super.serialize(serializer);
serializer.addString(value);
}
}
final class CodeArgument extends Argument {
@override
ArgumentKind get kind => ArgumentKind.code;
@override
void serialize(Serializer serializer) {
super.serialize(serializer);
value.serialize(serializer);
}
@override
final Code value;
CodeArgument(this.value);
}
final class TypeAnnotationArgument extends Argument {
@override
ArgumentKind get kind => ArgumentKind.typeAnnotation;
@override
void serialize(Serializer serializer) {
super.serialize(serializer);
value.serialize(serializer);
}
@override
final TypeAnnotationImpl value;
TypeAnnotationArgument(this.value);
}
abstract base class _CollectionArgument extends Argument {
/// Flat list of the actual reified type arguments for this list, in the order
/// they would appear if written in code.
///
/// For nullable types, they should be preceded by an [ArgumentKind.nullable].
///
/// Note that nested type arguments appear here and are just flattened, so
/// the type `List<Map<String, List<int>?>>` would have the type arguments:
///
/// [
/// ArgumentKind.map,
/// ArgumentKind.string,
/// ArgumentKind.nullable,
/// ArgumentKind.list,
/// ArgumentKind.int,
/// ]
final List<ArgumentKind> _typeArguments;
_CollectionArgument(this._typeArguments);
/// Creates a one or two element list, based on [_typeArguments], but
/// converted into deep [Cast] objects.
///
/// For an iterable, this will always have a single value, and for a map it
/// will always have two values.
List<Cast> _extractTypeArgumentCasts() {
List<Cast> castStack = [];
// We build up the list type backwards.
for (ArgumentKind type in _typeArguments.reversed) {
castStack.add(switch (type) {
ArgumentKind.bool => const Cast<bool>(),
ArgumentKind.double => const Cast<double>(),
ArgumentKind.int => const Cast<int>(),
ArgumentKind.map =>
MapCast.from(castStack.removeLast(), castStack.removeLast()),
ArgumentKind.nil => const Cast<Null>(),
ArgumentKind.set => SetCast.from(castStack.removeLast()),
ArgumentKind.string => const Cast<String>(),
ArgumentKind.list => ListCast.from(castStack.removeLast()),
ArgumentKind.typeAnnotation => const Cast<TypeAnnotation>(),
ArgumentKind.code => const Cast<Code>(),
ArgumentKind.object => const Cast<Object>(),
ArgumentKind.dynamic => const Cast<dynamic>(),
ArgumentKind.num => const Cast<num>(),
ArgumentKind.nullable => castStack.removeLast().nullable,
});
}
return castStack;
}
@override
void serialize(Serializer serializer) {
super.serialize(serializer);
serializer.startList();
for (ArgumentKind typeArgument in _typeArguments) {
serializer.addInt(typeArgument.index);
}
serializer.endList();
}
}
/// The base class for [ListArgument] and [SetArgument], most of the logic is
/// the same.
abstract base class _IterableArgument<T extends Iterable<Object?>>
extends _CollectionArgument {
/// These are the raw argument values for each entry in this iterable.
final List<Argument> _arguments;
_IterableArgument(this._arguments, super._typeArguments);
factory _IterableArgument._deserialize(
ArgumentKind kind, Deserializer deserializer) {
deserializer
..moveNext()
..expectList();
final List<ArgumentKind> typeArguments = [
for (; deserializer.moveNext();)
ArgumentKind.values[deserializer.expectInt()],
];
deserializer
..moveNext()
..expectList();
final List<Argument> values = [
for (; deserializer.moveNext();)
Argument.deserialize(deserializer, alreadyMoved: true),
];
return switch (kind) {
ArgumentKind.list => ListArgument(values, typeArguments),
ArgumentKind.set => SetArgument(values, typeArguments),
_ =>
throw UnsupportedError('Could not deserialize argument of kind $kind'),
} as _IterableArgument<T>;
}
@override
void serialize(Serializer serializer) {
super.serialize(serializer);
serializer.startList();
for (Argument argument in _arguments) {
argument.serialize(serializer);
}
serializer.endList();
}
}
final class ListArgument extends _IterableArgument<List<Object?>> {
@override
ArgumentKind get kind => ArgumentKind.list;
/// Materializes all the `_arguments` as actual values.
@override
List<Object?> get value =>
ListCast.from(_extractTypeArgumentCasts().single).cast([
for (Argument arg in _arguments) arg.value,
]);
ListArgument(super._arguments, super._typeArguments);
}
final class SetArgument extends _IterableArgument<Set<Object?>> {
@override
ArgumentKind get kind => ArgumentKind.set;
/// Materializes all the `_arguments` as actual values.
@override
Set<Object?> get value =>
SetCast.from(_extractTypeArgumentCasts().single).cast({
for (Argument arg in _arguments) arg.value,
});
SetArgument(super._arguments, super._typeArguments);
}
final class MapArgument extends _CollectionArgument {
@override
ArgumentKind get kind => ArgumentKind.map;
/// These are the raw argument values for the entries in this map.
final Map<Argument, Argument> _arguments;
/// Materializes all the `_arguments` as actual values.
@override
Map<Object?, Object?> get value {
// We should have exactly two type arguments, the key and value types.
final List<Cast> extractedTypes = _extractTypeArgumentCasts();
assert(extractedTypes.length == 2);
return MapCast.from(extractedTypes[1], extractedTypes[0]).cast({
for (MapEntry<Argument, Argument> argument in _arguments.entries)
argument.key.value: argument.value.value,
});
}
MapArgument(this._arguments, super._typeArguments);
factory MapArgument._deserialize(Deserializer deserializer) {
deserializer
..moveNext()
..expectList();
final List<ArgumentKind> typeArguments = [
for (; deserializer.moveNext();)
ArgumentKind.values[deserializer.expectInt()],
];
deserializer
..moveNext()
..expectList();
final Map<Argument, Argument> arguments = {
for (; deserializer.moveNext();)
Argument.deserialize(deserializer, alreadyMoved: true):
Argument.deserialize(deserializer),
};
return MapArgument(arguments, typeArguments);
}
@override
void serialize(Serializer serializer) {
super.serialize(serializer);
serializer.startList();
for (MapEntry<Argument, Argument> argument in _arguments.entries) {
argument.key.serialize(serializer);
argument.value.serialize(serializer);
}
serializer.endList();
}
}
/// The arguments passed to a macro constructor.
///
/// All argument instances must be of type [Code] or a built-in value type that
/// is serializable (num, bool, String, null, etc).
class Arguments implements Serializable {
final List<Argument> positional;
final Map<String, Argument> named;
Arguments(this.positional, this.named);
factory Arguments.deserialize(Deserializer deserializer) {
deserializer
..moveNext()
..expectList();
final List<Argument> positionalArgs = [
for (; deserializer.moveNext();)
Argument.deserialize(deserializer, alreadyMoved: true),
];
deserializer
..moveNext()
..expectList();
final Map<String, Argument> namedArgs = {
for (; deserializer.moveNext();)
deserializer.expectString(): Argument.deserialize(deserializer),
};
return Arguments(positionalArgs, namedArgs);
}
@override
void serialize(Serializer serializer) {
serializer.startList();
for (Argument arg in positional) {
arg.serialize(serializer);
}
serializer.endList();
serializer.startList();
for (MapEntry<String, Argument> arg in named.entries) {
serializer.addString(arg.key);
arg.value.serialize(serializer);
}
serializer.endList();
}
}
/// Used for serializing and deserializing arguments.
///
/// Note that the `nullable` variants, as well as `object`, `dynamic`, and `num`
/// are only used for type arguments. Instances should have an argument kind
/// that matches their their actual value.
enum ArgumentKind {
bool,
string,
double,
int,
list,
map,
set,
nil,
object,
dynamic,
num,
nullable,
typeAnnotation,
code,
}
@@ -1,411 +0,0 @@
// 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 '../api.dart';
import '../executor.dart';
import 'span.dart';
/// A mixin which provides a shared implementation of
/// [MacroExecutor.buildAugmentationLibrary].
mixin AugmentationLibraryBuilder on MacroExecutor {
@override
String buildAugmentationLibrary(
Uri augmentedLibraryUri,
Iterable<MacroExecutionResult> macroResults,
TypeDeclaration Function(Identifier) resolveDeclaration,
ResolvedIdentifier Function(Identifier) resolveIdentifier,
TypeAnnotation? Function(OmittedTypeAnnotation) inferOmittedType,
{Map<OmittedTypeAnnotation, String>? omittedTypes,
List<Span>? spans}) {
return _Builder(augmentedLibraryUri, resolveDeclaration, resolveIdentifier,
inferOmittedType, omittedTypes)
.build(macroResults, spans: spans);
}
}
class _Builder {
/// The import URI for the augmented library.
final Uri _augmentedLibraryUri;
final TypeDeclaration Function(Identifier) _resolveDeclaration;
final ResolvedIdentifier Function(Identifier) _resolveIdentifier;
final TypeAnnotation? Function(OmittedTypeAnnotation) _typeInferrer;
final Map<OmittedTypeAnnotation, String>? _omittedTypes;
final Map<Uri, _SynthesizedNamePart> _importNames = {};
final Map<OmittedTypeAnnotation, _SynthesizedNamePart> _typeNames = {};
final List<_AppliedPart<_Part>> _importParts = [];
final List<_AppliedPart<_Part>> _directivesParts = [];
final List<_AppliedPart<_StringPart>> _stringParts = [];
List<_AppliedPart<_StringPart>> _directivesStringPartBuffer = [];
// Keeps track of the last part written in `lastDirectivePart`.
String _lastDirectivePart = '';
_Builder(this._augmentedLibraryUri, this._resolveDeclaration,
this._resolveIdentifier, this._typeInferrer, this._omittedTypes);
void _flushStringParts() {
if (_directivesStringPartBuffer.isNotEmpty) {
_directivesParts.addAll(_directivesStringPartBuffer);
_stringParts.addAll(_directivesStringPartBuffer);
_directivesStringPartBuffer = [];
}
}
void _writeDirectiveStringPart(Key key, String part) {
_lastDirectivePart = part;
_directivesStringPartBuffer.add(_AppliedPart.string(key, part));
}
void _writeDirectiveSynthesizedNamePart(Key key, _SynthesizedNamePart part) {
_flushStringParts();
_lastDirectivePart = '';
_directivesParts.add(_AppliedPart.synthesized(key, part));
}
void _buildString(Key parent, int index, String part) {
_writeDirectiveStringPart(ContentKey.string(parent, index), part);
}
void _buildIdentifier(Key parent, int index, Identifier part) {
ResolvedIdentifier resolved = _resolveIdentifier(part);
_SynthesizedNamePart? prefix;
Uri? resolvedUri = resolved.uri;
if (resolvedUri != null) {
prefix = _importNames.putIfAbsent(resolvedUri, () {
_SynthesizedNamePart prefix = _SynthesizedNamePart();
_importParts.add(_AppliedPart.string(
UriKey.importPrefix(resolvedUri), "import '$resolvedUri' as "));
_importParts.add(_AppliedPart.synthesized(
UriKey.prefixDefinition(resolvedUri), prefix));
_importParts
.add(_AppliedPart.string(UriKey.importSuffix(resolvedUri), ";\n"));
return prefix;
});
}
if (resolved.kind == IdentifierKind.instanceMember) {
// Qualify with `this.` if we don't have a receiver.
if (!_lastDirectivePart.trimRight().endsWith('.')) {
_writeDirectiveStringPart(
ContentKey.implicitThis(parent, index), 'this.');
}
} else if (prefix != null) {
_writeDirectiveSynthesizedNamePart(
PrefixUseKey(parent, index, resolvedUri!), prefix);
_writeDirectiveStringPart(ContentKey.prefixDot(parent, index), '.');
}
if (resolved.kind == IdentifierKind.staticInstanceMember) {
_writeDirectiveStringPart(
ContentKey.staticScope(parent, index), '${resolved.staticScope!}.');
}
_writeDirectiveStringPart(
ContentKey.identifierName(parent, index), part.name);
}
void _buildOmittedTypeAnnotation(
Key parent, int index, OmittedTypeAnnotation part) {
TypeAnnotation? type = _typeInferrer(part);
Key typeAnnotationKey = OmittedTypeAnnotationKey(parent, index, part);
if (type == null) {
if (_omittedTypes != null) {
_SynthesizedNamePart name =
_typeNames.putIfAbsent(part, () => _SynthesizedNamePart());
_writeDirectiveSynthesizedNamePart(typeAnnotationKey, name);
} else {
throw ArgumentError("No type inferred for $part");
}
} else {
_buildCode(typeAnnotationKey, type.code);
}
}
void _buildCode(Key parent, Code code) {
List<Object> parts = code.parts;
for (int index = 0; index < parts.length; index++) {
Object part = parts[index];
if (part is String) {
_buildString(parent, index, part);
} else if (part is Code) {
_buildCode(ContentKey.code(parent, index), part);
} else if (part is Identifier) {
_buildIdentifier(parent, index, part);
} else if (part is OmittedTypeAnnotation) {
_buildOmittedTypeAnnotation(parent, index, part);
} else {
throw ArgumentError(
'Code objects only support String, Identifier, and Code '
'instances but got $part which was not one of those.');
}
}
}
String build(Iterable<MacroExecutionResult> macroResults,
{List<Span>? spans}) {
Map<Identifier, List<(Key, DeclarationCode)>> mergedTypeResults = {};
Map<Identifier, List<(Key, DeclarationCode)>> mergedEntryResults = {};
Map<Identifier, (Key, NamedTypeAnnotationCode)> mergedExtendsResults = {};
Map<Identifier, List<(Key, TypeAnnotationCode)>> mergedInterfaceResults =
{};
Map<Identifier, List<(Key, TypeAnnotationCode)>> mergedMixinResults = {};
for (MacroExecutionResult result in macroResults) {
Key key = MacroExecutionResultKey(result);
int index = 0;
for (DeclarationCode augmentation in result.libraryAugmentations) {
_buildCode(ContentKey.libraryAugmentation(key, index), augmentation);
_writeDirectiveStringPart(
ContentKey.libraryAugmentationSeparator(key, index), '\n');
index++;
}
result.enumValueAugmentations.forEach((identifier, value) {
int index = 0;
final Iterable<(Key, DeclarationCode)> values = value
.map((e) => (IdentifierKey.enum_(key, index++, identifier), e));
mergedEntryResults.update(
identifier, (enumValues) => enumValues..addAll(values),
ifAbsent: () => values.toList());
});
result.extendsTypeAugmentations.forEach((identifier, value) {
mergedExtendsResults.update(
identifier,
(existing) => throw StateError(
'A class cannot extend multiple classes, ${identifier.name} '
'tried to extend both ${existing.$2.name.name} and '
'${value.name.name}.'),
ifAbsent: () => (IdentifierKey.superclass(key, identifier), value));
});
result.interfaceAugmentations.forEach((identifier, value) {
int index = 0;
final Iterable<(Key, TypeAnnotationCode)> values = value
.map((e) => (IdentifierKey.interface(key, index++, identifier), e));
mergedInterfaceResults.update(
identifier, (declarations) => declarations..addAll(values),
ifAbsent: () => values.toList());
});
result.mixinAugmentations.forEach((identifier, value) {
int index = 0;
final Iterable<(Key, TypeAnnotationCode)> values = value
.map((e) => (IdentifierKey.mixin(key, index++, identifier), e));
mergedMixinResults.update(
identifier, (declarations) => declarations..addAll(values),
ifAbsent: () => values.toList());
});
result.typeAugmentations.forEach((identifier, value) {
int index = 0;
final Iterable<(Key, DeclarationCode)> values = value
.map((e) => (IdentifierKey.member(key, index++, identifier), e));
mergedTypeResults.update(
identifier, (declarations) => declarations..addAll(values),
ifAbsent: () => values.toList());
});
}
final Set<Identifier> mergedAugmentedTypes = {
...mergedEntryResults.keys,
...mergedExtendsResults.keys,
...mergedInterfaceResults.keys,
...mergedMixinResults.keys,
...mergedTypeResults.keys,
};
for (Identifier type in mergedAugmentedTypes) {
final TypeDeclaration typeDeclaration = _resolveDeclaration(type);
final TypeDeclarationKey key = TypeDeclarationKey(typeDeclaration);
String declarationKind = switch (typeDeclaration) {
ClassDeclaration() => 'class',
EnumDeclaration() => 'enum',
ExtensionDeclaration() => 'extension',
MixinDeclaration() => 'mixin',
_ => throw UnsupportedError(
'Unsupported augmentation type $typeDeclaration'),
};
final List<String> keywords = [
if (typeDeclaration is ClassDeclaration) ...[
if (typeDeclaration.hasAbstract) 'abstract',
if (typeDeclaration.hasBase) 'base',
if (typeDeclaration.hasExternal) 'external',
if (typeDeclaration.hasFinal) 'final',
if (typeDeclaration.hasInterface) 'interface',
if (typeDeclaration.hasMixin) 'mixin',
if (typeDeclaration.hasSealed) 'sealed',
] else if (typeDeclaration is MixinDeclaration &&
typeDeclaration.hasBase)
'base',
];
// Has the effect of adding a space after the keywords
if (keywords.isNotEmpty) keywords.add('');
var hasTypeParams = typeDeclaration is ParameterizedTypeDeclaration &&
typeDeclaration.typeParameters.isNotEmpty;
_writeDirectiveStringPart(TypeDeclarationContentKey.declaration(key),
'augment ${keywords.join(' ')}$declarationKind ${type.name}${hasTypeParams ? '' : ' '}');
if (hasTypeParams) {
var typeParameters = typeDeclaration.typeParameters;
_writeDirectiveStringPart(
TypeDeclarationContentKey.typeParametersStart(key), '<');
for (var param in typeParameters) {
_buildCode(
key,
param == typeParameters.first
? param.code
: RawCode.fromParts([', ', param.code]));
}
_writeDirectiveStringPart(
TypeDeclarationContentKey.typeParametersEnd(key), '> ');
}
if (mergedExtendsResults[type] case (var superclassKey, var superclass)) {
Key fixedKey = TypeDeclarationContentKey.superclass(key);
int index = 0;
_buildString(fixedKey, index++, 'extends ');
_buildCode(superclassKey, superclass);
_buildString(fixedKey, index++, ' ');
}
if (mergedMixinResults[type] case var mixins? when mixins.isNotEmpty) {
Key mixinsKey = TypeDeclarationContentKey.mixins(key);
int index = 0;
_buildString(mixinsKey, index++, 'with ');
bool needsComma = false;
for (var (Key key, TypeAnnotationCode mixin) in mixins) {
if (needsComma) {
_buildString(mixinsKey, index++, ', ');
}
_buildCode(key, mixin);
needsComma = true;
}
_buildString(mixinsKey, index++, ' ');
}
if (mergedInterfaceResults[type] case var interfaces?
when interfaces.isNotEmpty) {
Key interfacesKey = TypeDeclarationContentKey.interfaces(key);
int index = 0;
_buildString(interfacesKey, index++, 'implements ');
bool needsComma = false;
for (var (Key key, TypeAnnotationCode interface) in interfaces) {
if (needsComma) {
_buildString(interfacesKey, index++, ', ');
}
_buildCode(key, interface);
needsComma = true;
}
_buildString(interfacesKey, index++, ' ');
}
_writeDirectiveStringPart(
TypeDeclarationContentKey.bodyStart(key), '{\n');
if (typeDeclaration is EnumDeclaration) {
for (var (Key key, DeclarationCode entryAugmentation)
in mergedEntryResults[type] ?? []) {
_buildCode(key, entryAugmentation);
}
_writeDirectiveStringPart(
TypeDeclarationContentKey.enumValueEnd(key), ';\n');
}
for (var (Key key, DeclarationCode augmentation)
in mergedTypeResults[type] ?? []) {
_buildCode(key, augmentation);
_writeDirectiveStringPart(
TypeDeclarationContentKey.declarationSeparator(key), '\n');
}
_writeDirectiveStringPart(TypeDeclarationContentKey.bodyEnd(key), '}\n');
}
_flushStringParts();
if (_importNames.isNotEmpty) {
String prefix = _computeFreshPrefix(_stringParts, 'prefix');
int index = 0;
for (_SynthesizedNamePart part in _importNames.values) {
part.text = '$prefix${index++}';
}
}
if (_omittedTypes != null && _typeNames.isNotEmpty) {
String prefix = _computeFreshPrefix(_stringParts, 'OmittedType');
int index = 0;
_typeNames.forEach(
(OmittedTypeAnnotation omittedType, _SynthesizedNamePart part) {
String name = '$prefix${index++}';
part.text = name;
_omittedTypes[omittedType] = name;
});
}
StringBuffer sb = StringBuffer();
void addText(Key key, String text) {
spans?.add(Span(key, sb.length, text));
sb.write(text);
}
addText(const LibraryAugmentKey(),
'augment library \'$_augmentedLibraryUri\';\n\n');
for (_AppliedPart<_Part> appliedPart in _importParts) {
addText(appliedPart.key, appliedPart.part.text);
}
if (_importParts.isNotEmpty) {
addText(const ImportDeclarationSeparatorKey(), '\n');
}
for (_AppliedPart<_Part> appliedPart in _directivesParts) {
addText(appliedPart.key, appliedPart.part.text);
}
addText(const EndOfFileKey(), "");
return sb.toString();
}
}
class _AppliedPart<T extends _Part> {
final Key key;
final T part;
_AppliedPart(this.key, this.part);
static _AppliedPart<_StringPart> string(Key key, String part) =>
_AppliedPart<_StringPart>(key, _StringPart(part));
static _AppliedPart<_SynthesizedNamePart> synthesized(
Key key, _SynthesizedNamePart part) =>
_AppliedPart<_SynthesizedNamePart>(key, part);
}
abstract class _Part {
String get text;
}
class _SynthesizedNamePart implements _Part {
@override
late String text;
}
class _StringPart implements _Part {
@override
final String text;
_StringPart(this.text);
}
/// Computes a name starting with [name] that is unique with respect to the
/// text in [stringParts].
///
/// This algorithm assumes that no two parts in [stringParts] occur in direct
/// sequence where they are used, i.e. there is always at least one
/// [_SynthesizedNamePart] between them.
String _computeFreshPrefix(
List<_AppliedPart<_StringPart>> stringParts, String name) {
int index = -1;
String prefix = name;
for (_AppliedPart<_StringPart> appliedPart in stringParts) {
while (appliedPart.part.text.contains(prefix)) {
index++;
prefix = '$name$index';
}
}
if (index > 0) {
// Add a separator when an index was needed. This is to ensure that
// suffixing number to [prefix] doesn't blend the digits.
prefix = '${prefix}_';
}
return prefix;
}
@@ -1,752 +0,0 @@
// 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 'dart:async';
import '../api.dart';
import '../executor.dart';
import 'exception_impls.dart';
import 'introspection_impls.dart';
import 'response_impls.dart';
abstract class TypeBuilderBase implements TypePhaseIntrospector, Builder {
/// All the collected diagnostics for this builder.
final List<Diagnostic> _diagnostics;
/// If execution was stopped by an exception, the exception.
MacroExceptionImpl? _exception;
/// All the enum values to be added, indexed by the identifier for the
/// augmented enum declaration.
final Map<IdentifierImpl, List<DeclarationCode>> _enumValueAugmentations;
/// All the extends clauses to be added, indexed by the identifier for the
/// augmented type declaration.
final Map<IdentifierImpl, NamedTypeAnnotationCode> _extendsTypeAugmentations;
/// All the interfaces to be added, indexed by the identifier for the
/// augmented type declaration.
final Map<IdentifierImpl, List<TypeAnnotationCode>> _interfaceAugmentations;
/// All the top level declarations to add to the current library.
final List<DeclarationCode> _libraryAugmentations;
/// All the mixins to be added, indexed by the identifier for the
/// augmented type declaration.
final Map<IdentifierImpl, List<TypeAnnotationCode>> _mixinAugmentations;
/// The names of any new types added in [_libraryAugmentations].
final List<String> _newTypeNames = [];
/// All the declarations to be added to types, indexed by the identifier for
/// the augmented type.
final Map<IdentifierImpl, List<DeclarationCode>> _typeAugmentations;
TypePhaseIntrospector get introspector;
/// Creates and returns a [MacroExecutionResult] out of the [_augmentations]
/// created by this builder.
MacroExecutionResult get result => MacroExecutionResultImpl(
diagnostics: _diagnostics,
exception: _exception,
enumValueAugmentations: _enumValueAugmentations,
extendsTypeAugmentations: _extendsTypeAugmentations,
interfaceAugmentations: _interfaceAugmentations,
libraryAugmentations: _libraryAugmentations,
mixinAugmentations: _mixinAugmentations,
newTypeNames: _newTypeNames,
typeAugmentations: _typeAugmentations,
);
TypeBuilderBase()
: _diagnostics = [],
_enumValueAugmentations = {},
_extendsTypeAugmentations = {},
_interfaceAugmentations = {},
_libraryAugmentations = [],
_mixinAugmentations = {},
_typeAugmentations = {};
TypeBuilderBase.nested({
Map<IdentifierImpl, List<DeclarationCode>>? parentEnumValueAugmentations,
Map<IdentifierImpl, NamedTypeAnnotationCode>?
parentExtendsTypeAugmentations,
Map<IdentifierImpl, List<TypeAnnotationCode>>? parentInterfaceAugmentations,
List<DeclarationCode>? parentLibraryAugmentations,
Map<IdentifierImpl, List<TypeAnnotationCode>>? parentMixinAugmentations,
Map<IdentifierImpl, List<DeclarationCode>>? parentTypeAugmentations,
List<Diagnostic>? parentDiagnostics,
}) : _diagnostics = parentDiagnostics ?? [],
_enumValueAugmentations = parentEnumValueAugmentations ?? {},
_extendsTypeAugmentations = parentExtendsTypeAugmentations ?? {},
_interfaceAugmentations = parentInterfaceAugmentations ?? {},
_libraryAugmentations = parentLibraryAugmentations ?? [],
_mixinAugmentations = parentMixinAugmentations ?? {},
_typeAugmentations = parentTypeAugmentations ?? {};
@override
void report(Diagnostic diagnostic) => _diagnostics.add(diagnostic);
void failWithException(MacroExceptionImpl exception) {
if (_exception != null) throw StateError('Already set exception');
_exception = exception;
}
@override
Future<Identifier> resolveIdentifier(Uri library, String identifier) =>
// ignore: deprecated_member_use_from_same_package
introspector.resolveIdentifier(library, identifier);
}
class TypeBuilderImpl extends TypeBuilderBase implements TypeBuilder {
@override
final TypePhaseIntrospector introspector;
TypeBuilderImpl(this.introspector);
@override
void declareType(String name, DeclarationCode typeDeclaration) {
_newTypeNames.add(name);
_libraryAugmentations.add(typeDeclaration);
}
}
mixin ExtendsTypeBuilderImpl on TypeBuilderImpl implements ExtendsTypeBuilder {
/// The type that we are going to be adding an extends clause.
IdentifierImpl get originalType;
/// Sets the `extends` clause to [superclass].
///
/// The type must not already have an `extends` clause.
@override
void extendsType(NamedTypeAnnotationCode superclass) {
if (_extendsTypeAugmentations.containsKey(originalType)) {
throw ArgumentError.value(
originalType.name, null, 'A type cannot extend multiple types');
}
_extendsTypeAugmentations[originalType] = superclass;
}
}
mixin InterfaceTypesBuilderImpl on TypeBuilderImpl
implements InterfaceTypesBuilder {
/// The type that we are going to be adding interfaces to.
IdentifierImpl get originalType;
/// Appends [interfaces] to the list of interfaces for this type.
@override
void appendInterfaces(Iterable<TypeAnnotationCode> interfaces) {
_interfaceAugmentations
.putIfAbsent(originalType, () => [])
.addAll(interfaces);
}
}
mixin MixinTypesBuilderImpl on TypeBuilderImpl implements MixinTypesBuilder {
/// The type that we are going to be adding mixins to.
IdentifierImpl get originalType;
/// Appends [mixins] to the list of mixins for this type.
@override
void appendMixins(Iterable<TypeAnnotationCode> mixins) {
(_mixinAugmentations[originalType] ??= []).addAll(mixins);
}
}
class ClassTypeBuilderImpl extends TypeBuilderImpl
with
ExtendsTypeBuilderImpl,
InterfaceTypesBuilderImpl,
MixinTypesBuilderImpl
implements ClassTypeBuilder {
@override
final IdentifierImpl originalType;
ClassTypeBuilderImpl(this.originalType, super.introspector);
}
class EnumTypeBuilderImpl extends TypeBuilderImpl
with InterfaceTypesBuilderImpl, MixinTypesBuilderImpl
implements EnumTypeBuilder {
@override
final IdentifierImpl originalType;
EnumTypeBuilderImpl(this.originalType, super.introspector);
}
class MixinTypeBuilderImpl extends TypeBuilderImpl
with InterfaceTypesBuilderImpl
implements MixinTypeBuilder {
@override
final IdentifierImpl originalType;
MixinTypeBuilderImpl(this.originalType, super.introspector);
}
/// Base class for all [DeclarationBuilder]s.
abstract class DeclarationBuilderBase extends TypeBuilderBase
implements DeclarationPhaseIntrospector {
@override
DeclarationPhaseIntrospector get introspector;
DeclarationBuilderBase();
DeclarationBuilderBase.nested({
required super.parentDiagnostics,
required super.parentEnumValueAugmentations,
required super.parentExtendsTypeAugmentations,
required super.parentInterfaceAugmentations,
required super.parentLibraryAugmentations,
required super.parentTypeAugmentations,
required super.parentMixinAugmentations,
}) : super.nested();
@override
Future<TypeDeclaration> typeDeclarationOf(IdentifierImpl identifier) =>
introspector.typeDeclarationOf(identifier);
@override
Future<List<ConstructorDeclaration>> constructorsOf(TypeDeclaration type) =>
introspector.constructorsOf(type);
@override
Future<List<EnumValueDeclaration>> valuesOf(
covariant EnumDeclaration enuum) =>
introspector.valuesOf(enuum);
@override
Future<List<FieldDeclaration>> fieldsOf(TypeDeclaration type) =>
introspector.fieldsOf(type);
@override
Future<List<MethodDeclaration>> methodsOf(TypeDeclaration type) =>
introspector.methodsOf(type);
@override
Future<StaticType> resolve(TypeAnnotationCode code) =>
introspector.resolve(code);
@override
Future<List<TypeDeclaration>> typesOf(Library library) =>
introspector.typesOf(library);
}
class DeclarationBuilderImpl extends DeclarationBuilderBase
implements DeclarationBuilder {
@override
final DeclarationPhaseIntrospector introspector;
DeclarationBuilderImpl(this.introspector);
@override
void declareInLibrary(DeclarationCode declaration) {
_libraryAugmentations.add(declaration);
}
}
class MemberDeclarationBuilderImpl extends DeclarationBuilderImpl
implements MemberDeclarationBuilder {
final IdentifierImpl definingType;
MemberDeclarationBuilderImpl(
this.definingType,
super.introspector,
);
@override
void declareInType(DeclarationCode declaration) {
_typeAugmentations.update(definingType, (value) => value..add(declaration),
ifAbsent: () => [declaration]);
}
}
class EnumDeclarationBuilderImpl extends MemberDeclarationBuilderImpl
implements EnumDeclarationBuilder {
EnumDeclarationBuilderImpl(
super.definingType,
super.introspector,
);
@override
void declareEnumValue(DeclarationCode declaration) {
_enumValueAugmentations.update(
definingType, (value) => value..add(declaration),
ifAbsent: () => [declaration]);
}
}
/// Base class for all [DefinitionBuilder]s.
class DefinitionBuilderBase extends DeclarationBuilderBase
implements DefinitionPhaseIntrospector {
@override
final DefinitionPhaseIntrospector introspector;
DefinitionBuilderBase(this.introspector);
DefinitionBuilderBase.nested(
this.introspector, {
required super.parentDiagnostics,
required super.parentEnumValueAugmentations,
required super.parentExtendsTypeAugmentations,
required super.parentInterfaceAugmentations,
required super.parentLibraryAugmentations,
required super.parentTypeAugmentations,
required super.parentMixinAugmentations,
}) : super.nested();
@override
Future<Declaration> declarationOf(Identifier identifier) =>
introspector.declarationOf(identifier);
@override
Future<TypeAnnotation> inferType(OmittedTypeAnnotationImpl omittedType) =>
introspector.inferType(omittedType);
@override
Future<List<Declaration>> topLevelDeclarationsOf(Library library) =>
introspector.topLevelDeclarationsOf(library);
@override
Future<TypeDeclaration> typeDeclarationOf(Identifier identifier) =>
introspector.typeDeclarationOf(identifier);
}
class TypeDefinitionBuilderImpl extends DefinitionBuilderBase
implements TypeDefinitionBuilder {
/// The declaration this is a builder for.
final TypeDeclaration declaration;
TypeDefinitionBuilderImpl(this.declaration, super.introspector);
TypeDefinitionBuilderImpl.nested(
this.declaration,
super.introspector, {
required super.parentDiagnostics,
required super.parentEnumValueAugmentations,
required super.parentExtendsTypeAugmentations,
required super.parentInterfaceAugmentations,
required super.parentLibraryAugmentations,
required super.parentTypeAugmentations,
required super.parentMixinAugmentations,
}) : super.nested();
@override
Future<ConstructorDefinitionBuilder> buildConstructor(
Identifier identifier) async {
ConstructorDeclarationImpl constructor = (await introspector
.constructorsOf(declaration))
.firstWhere((constructor) => constructor.identifier == identifier)
as ConstructorDeclarationImpl;
return ConstructorDefinitionBuilderImpl.nested(constructor, introspector,
parentDiagnostics: _diagnostics,
parentEnumValueAugmentations: _enumValueAugmentations,
parentExtendsTypeAugmentations: _extendsTypeAugmentations,
parentInterfaceAugmentations: _interfaceAugmentations,
parentMixinAugmentations: _mixinAugmentations,
parentTypeAugmentations: _typeAugmentations,
parentLibraryAugmentations: _libraryAugmentations);
}
@override
Future<VariableDefinitionBuilder> buildField(Identifier identifier) async {
FieldDeclaration field = (await introspector.fieldsOf(declaration))
.firstWhere((field) => field.identifier == identifier);
return VariableDefinitionBuilderImpl.nested(field, introspector,
parentDiagnostics: _diagnostics,
parentEnumValueAugmentations: _enumValueAugmentations,
parentExtendsTypeAugmentations: _extendsTypeAugmentations,
parentInterfaceAugmentations: _interfaceAugmentations,
parentMixinAugmentations: _mixinAugmentations,
parentTypeAugmentations: _typeAugmentations,
parentLibraryAugmentations: _libraryAugmentations);
}
@override
Future<FunctionDefinitionBuilder> buildMethod(Identifier identifier) async {
MethodDeclarationImpl method = (await introspector.methodsOf(declaration))
.firstWhere((method) => method.identifier == identifier)
as MethodDeclarationImpl;
return FunctionDefinitionBuilderImpl.nested(method, introspector,
parentDiagnostics: _diagnostics,
parentEnumValueAugmentations: _enumValueAugmentations,
parentExtendsTypeAugmentations: _extendsTypeAugmentations,
parentInterfaceAugmentations: _interfaceAugmentations,
parentMixinAugmentations: _mixinAugmentations,
parentTypeAugmentations: _typeAugmentations,
parentLibraryAugmentations: _libraryAugmentations);
}
}
class EnumDefinitionBuilderImpl extends TypeDefinitionBuilderImpl
implements EnumDefinitionBuilder {
@override
EnumDeclaration get declaration => super.declaration as EnumDeclaration;
EnumDefinitionBuilderImpl(
EnumDeclaration super.declaration, super.introspector);
EnumDefinitionBuilderImpl.nested(
EnumDeclaration super.declaration,
super.introspector, {
required super.parentDiagnostics,
required super.parentEnumValueAugmentations,
required super.parentExtendsTypeAugmentations,
required super.parentInterfaceAugmentations,
required super.parentLibraryAugmentations,
required super.parentMixinAugmentations,
required super.parentTypeAugmentations,
}) : super.nested();
@override
Future<EnumValueDefinitionBuilder> buildEnumValue(
Identifier identifier) async {
EnumValueDeclarationImpl entry = (await introspector.valuesOf(declaration))
.firstWhere((entry) => entry.identifier == identifier)
as EnumValueDeclarationImpl;
return EnumValueDefinitionBuilderImpl.nested(
entry,
introspector,
parentDiagnostics: _diagnostics,
parentEnumValueAugmentations: _enumValueAugmentations,
parentExtendsTypeAugmentations: _extendsTypeAugmentations,
parentInterfaceAugmentations: _interfaceAugmentations,
parentLibraryAugmentations: _libraryAugmentations,
parentMixinAugmentations: _mixinAugmentations,
parentTypeAugmentations: _typeAugmentations,
);
}
}
class EnumValueDefinitionBuilderImpl extends DefinitionBuilderBase
implements EnumValueDefinitionBuilder {
final EnumValueDeclarationImpl declaration;
EnumValueDefinitionBuilderImpl(this.declaration, super.introspector);
EnumValueDefinitionBuilderImpl.nested(
this.declaration,
super.introspector, {
required super.parentDiagnostics,
required super.parentEnumValueAugmentations,
required super.parentExtendsTypeAugmentations,
required super.parentInterfaceAugmentations,
required super.parentLibraryAugmentations,
required super.parentMixinAugmentations,
required super.parentTypeAugmentations,
}) : super.nested();
@override
void augment(DeclarationCode entry) {
_enumValueAugmentations.update(
declaration.definingEnum, (value) => value..add(entry),
ifAbsent: () => [entry]);
}
}
/// Implementation of [FunctionDefinitionBuilder].
class FunctionDefinitionBuilderImpl extends DefinitionBuilderBase
implements FunctionDefinitionBuilder {
final FunctionDeclarationImpl declaration;
FunctionDefinitionBuilderImpl(this.declaration, super.introspector);
FunctionDefinitionBuilderImpl.nested(
this.declaration,
super.introspector, {
required super.parentDiagnostics,
required super.parentEnumValueAugmentations,
required super.parentExtendsTypeAugmentations,
required super.parentInterfaceAugmentations,
required super.parentLibraryAugmentations,
required super.parentMixinAugmentations,
required super.parentTypeAugmentations,
}) : super.nested();
@override
void augment(FunctionBodyCode body, {CommentCode? docComments}) {
DeclarationCode augmentation =
_buildFunctionAugmentation(body, declaration, docComments: docComments);
if (declaration is MemberDeclaration) {
_typeAugmentations.update(
(declaration as MethodDeclarationImpl).definingType,
(value) => value..add(augmentation),
ifAbsent: () => [augmentation]);
} else {
_libraryAugmentations.add(augmentation);
}
}
}
class ConstructorDefinitionBuilderImpl extends DefinitionBuilderBase
implements ConstructorDefinitionBuilder {
final ConstructorDeclarationImpl declaration;
ConstructorDefinitionBuilderImpl(this.declaration, super.introspector);
ConstructorDefinitionBuilderImpl.nested(
this.declaration,
super.introspector, {
required super.parentDiagnostics,
required super.parentEnumValueAugmentations,
required super.parentExtendsTypeAugmentations,
required super.parentInterfaceAugmentations,
required super.parentLibraryAugmentations,
required super.parentMixinAugmentations,
required super.parentTypeAugmentations,
}) : super.nested();
@override
void augment(
{FunctionBodyCode? body,
List<Code>? initializers,
CommentCode? docComments}) {
DeclarationCode augmentation = _buildFunctionAugmentation(body, declaration,
initializers: initializers, docComments: docComments);
_typeAugmentations.update(
declaration.definingType, (value) => value..add(augmentation),
ifAbsent: () => [augmentation]);
}
}
class VariableDefinitionBuilderImpl extends DefinitionBuilderBase
implements VariableDefinitionBuilder {
final VariableDeclaration declaration;
VariableDefinitionBuilderImpl(this.declaration, super.introspector);
VariableDefinitionBuilderImpl.nested(
this.declaration,
super.introspector, {
required super.parentDiagnostics,
required super.parentEnumValueAugmentations,
required super.parentExtendsTypeAugmentations,
required super.parentInterfaceAugmentations,
required super.parentLibraryAugmentations,
required super.parentMixinAugmentations,
required super.parentTypeAugmentations,
}) : super.nested();
@override
void augment(
{DeclarationCode? getter,
DeclarationCode? setter,
ExpressionCode? initializer,
CommentCode? initializerDocComments}) {
List<DeclarationCode> augmentations = _buildVariableAugmentations(
declaration,
getter: getter,
setter: setter,
initializer: initializer,
initializerDocComments: initializerDocComments);
if (declaration is MemberDeclaration) {
_typeAugmentations.update(
(declaration as FieldDeclarationImpl).definingType,
(value) => value..addAll(augmentations),
ifAbsent: () => augmentations);
} else {
_libraryAugmentations.addAll(augmentations);
}
}
}
class LibraryDefinitionBuilderImpl extends DefinitionBuilderBase
implements LibraryDefinitionBuilder {
final Library library;
LibraryDefinitionBuilderImpl(this.library, super.introspector);
LibraryDefinitionBuilderImpl.nested(
this.library,
super.introspector, {
required super.parentDiagnostics,
required super.parentEnumValueAugmentations,
required super.parentExtendsTypeAugmentations,
required super.parentInterfaceAugmentations,
required super.parentLibraryAugmentations,
required super.parentMixinAugmentations,
required super.parentTypeAugmentations,
}) : super.nested();
@override
Future<FunctionDefinitionBuilder> buildFunction(Identifier identifier) async {
FunctionDeclarationImpl function = (await introspector
.topLevelDeclarationsOf(library))
.firstWhere((declaration) => declaration.identifier == identifier)
as FunctionDeclarationImpl;
return FunctionDefinitionBuilderImpl.nested(
function,
introspector,
parentDiagnostics: _diagnostics,
parentEnumValueAugmentations: _enumValueAugmentations,
parentExtendsTypeAugmentations: _extendsTypeAugmentations,
parentInterfaceAugmentations: _interfaceAugmentations,
parentMixinAugmentations: _mixinAugmentations,
parentTypeAugmentations: _typeAugmentations,
parentLibraryAugmentations: _libraryAugmentations,
);
}
@override
Future<TypeDefinitionBuilder> buildType(Identifier identifier) async {
TypeDeclaration type = (await introspector.topLevelDeclarationsOf(library))
.firstWhere((declaration) => declaration.identifier == identifier)
as TypeDeclaration;
return TypeDefinitionBuilderImpl.nested(type, introspector,
parentDiagnostics: _diagnostics,
parentEnumValueAugmentations: _enumValueAugmentations,
parentExtendsTypeAugmentations: _extendsTypeAugmentations,
parentInterfaceAugmentations: _interfaceAugmentations,
parentMixinAugmentations: _mixinAugmentations,
parentTypeAugmentations: _typeAugmentations,
parentLibraryAugmentations: _libraryAugmentations);
}
@override
Future<VariableDefinitionBuilder> buildVariable(Identifier identifier) async {
VariableDeclarationImpl variable = (await introspector
.topLevelDeclarationsOf(library))
.firstWhere((declaration) => declaration.identifier == identifier)
as VariableDeclarationImpl;
return VariableDefinitionBuilderImpl.nested(variable, introspector,
parentDiagnostics: _diagnostics,
parentEnumValueAugmentations: _enumValueAugmentations,
parentExtendsTypeAugmentations: _extendsTypeAugmentations,
parentInterfaceAugmentations: _interfaceAugmentations,
parentMixinAugmentations: _mixinAugmentations,
parentTypeAugmentations: _typeAugmentations,
parentLibraryAugmentations: _libraryAugmentations);
}
}
/// Builds all the possible augmentations for a variable.
List<DeclarationCode> _buildVariableAugmentations(
VariableDeclaration declaration,
{DeclarationCode? getter,
DeclarationCode? setter,
ExpressionCode? initializer,
CommentCode? initializerDocComments}) {
if (initializerDocComments != null && initializer == null) {
throw ArgumentError(
'initializerDocComments cannot be provided if an initializer is not '
'provided.');
}
List<DeclarationCode> augmentations = [];
if (getter != null) {
augmentations.add(DeclarationCode.fromParts([
if (declaration is FieldDeclaration) ' ',
'augment ',
if (declaration is FieldDeclaration && declaration.hasStatic) 'static ',
getter,
]));
}
if (setter != null) {
augmentations.add(DeclarationCode.fromParts([
if (declaration is FieldDeclaration) ' ',
'augment ',
if (declaration is FieldDeclaration && declaration.hasStatic) 'static ',
setter,
]));
}
if (initializer != null) {
augmentations.add(DeclarationCode.fromParts([
if (initializerDocComments != null) initializerDocComments,
if (declaration is FieldDeclaration) ' ',
'augment ',
if (declaration is FieldDeclaration && declaration.hasStatic) 'static ',
if (declaration.hasFinal) 'final ',
declaration.type.code,
' ',
declaration.identifier.name,
' = ',
initializer,
';',
]));
}
return augmentations;
}
/// Builds the code to augment a function, method, or constructor with a new
/// body.
///
/// The [initializers] parameter can only be used if [declaration] is a
/// constructor.
DeclarationCode _buildFunctionAugmentation(
FunctionBodyCode? body, FunctionDeclaration declaration,
{List<Code>? initializers, CommentCode? docComments}) {
assert(initializers == null || declaration is ConstructorDeclaration);
return DeclarationCode.fromParts([
if (docComments != null) ...[docComments, '\n'],
if (declaration is MethodDeclaration) ' ',
'augment ',
if (declaration is ConstructorDeclaration) ...[
if (declaration.isConst) 'const ',
if (declaration.isFactory) 'factory ',
declaration.definingType.name,
if (declaration.identifier.name.isNotEmpty) '.',
] else ...[
if (declaration is MethodDeclaration && declaration.hasStatic) 'static ',
declaration.returnType.code,
' ',
if (declaration.isOperator) 'operator ',
],
if (declaration.isGetter) 'get ',
if (declaration.isSetter) 'set ',
declaration.identifier.name,
if (!declaration.isGetter) ...[
if (declaration.typeParameters.isNotEmpty) ...[
'<',
for (TypeParameterDeclaration typeParam
in declaration.typeParameters) ...[
typeParam.identifier.name,
if (typeParam.bound != null) ...[' extends ', typeParam.bound!.code],
if (typeParam != declaration.typeParameters.last) ', ',
],
'>',
],
'(',
for (FormalParameterDeclaration positionalRequired in declaration
.positionalParameters
.takeWhile((p) => p.isRequired)) ...[
positionalRequired.code,
', ',
],
if (declaration.positionalParameters.any((p) => !p.isRequired)) ...[
'[',
for (FormalParameterDeclaration positionalOptional in declaration
.positionalParameters
.where((p) => !p.isRequired)) ...[
positionalOptional.code,
', ',
],
']',
],
if (declaration.namedParameters.isNotEmpty) ...[
'{',
for (FormalParameterDeclaration named
in declaration.namedParameters) ...[
named.code,
', ',
],
'}',
],
')',
],
if (initializers != null && initializers.isNotEmpty) ...[
'\n : ',
initializers.first,
for (Code initializer in initializers.skip(1)) ...[
',\n ',
initializer,
],
],
if (body == null)
';'
else ...[
' ',
body,
]
]);
}
-141
View File
@@ -1,141 +0,0 @@
// Copyright (c) 2023, 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.
/// Enables building up dynamic schemas with deep casts.
///
/// These schemas are built up "inside out" using [getAsTypedCast] to extract
/// the reified type argument from a [Cast], and pass that to another [Cast]
/// instance.
class Cast<T> {
const Cast();
/// All casts happen in this method, custom [Cast] implementations must
/// override this method, and no other methods.
T _cast(Object? from) => from is T
? from
: throw FailedCast(
'expected type $T but got type ${from.runtimeType} for: $from');
T cast(Object? from) => _cast(from);
Cast<T?> get nullable => NullableCast._(this);
/// Enables building up deeply nested generic types without requiring any
/// static knowledge or type inference.
///
/// Example usage:
///
/// Cast<dynamic> x = Cast<int>();
/// final y = x.getAsTypedCast(<T>(_) => Cast<Foo<T>>());
/// print(y.runtimeType); // Cast<Foo<int>>
R getAsTypedCast<R>(R Function<CastType>(Cast<CastType> self) callback) =>
callback<T>(this);
}
/// Wraps a [Cast] such that it also accepts `null`.
class NullableCast<T> extends Cast<T?> {
final Cast<T> _original;
@override
Cast<T?> get nullable => this;
NullableCast._(this._original);
@override
T? _cast(Object? from) {
if (from == null) return null;
return _original._cast(from);
}
}
/// Specialized [Cast] implementation for [Map]s which does deep casting of keys
/// and values.
class MapCast<K, V> extends Cast<Map<K, V>> {
final Cast<K> _key;
final Cast<V> _value;
const MapCast._(Cast<K> key, Cast<V> value)
: _key = key,
_value = value;
/// Builds a [MapCast] whose runtime type is built from the runtime type
/// arguments of [keyCast] and [valueCast].
///
/// The static type arguments are generally not interesting for these objects,
/// and so `<Object?, Object?>` is used to avoid unnecessary casts.
static MapCast<Object?, Object?> from(
Cast<Object?> keyCast, Cast<Object?> valueCast) =>
keyCast.getAsTypedCast(<KK>(keyCast) => valueCast.getAsTypedCast(
<VV>(valueCast) => MapCast<KK, VV>._(keyCast, valueCast)));
@override
Map<K, V> _cast(Object? from) {
if (from is! Map) {
return super._cast(from);
}
Map<K, V> result = {};
for (Object? key in from.keys) {
K newKey = _key._cast(key);
result[newKey] = _value._cast(from[key]);
}
return result;
}
}
/// Specialized [Cast] implementation for [List]s which does deep casting of
/// entries.
class ListCast<E> extends Cast<List<E>> {
final Cast<E> _entryCast;
const ListCast._(this._entryCast);
/// Builds a [ListCast] whose runtime type is built from the runtime type
/// arguments of [entryCast].
///
/// The static type argument is generally not interesting for these objects,
/// and so `<Object?>` is used to avoid unnecessary casts.
static ListCast<Object?> from(Cast entryCast) =>
entryCast.getAsTypedCast(ListCast._);
@override
List<E> _cast(Object? from) {
if (from is! List) {
return super._cast(from);
}
return List<E>.generate(from.length, (i) => _entryCast._cast(from[i]));
}
}
/// Specialized [Cast] implementation for [Set]s which does deep casting of
/// entries.
class SetCast<E> extends Cast<Set<E>> {
final Cast<E> _entryCast;
const SetCast._(this._entryCast);
/// Builds a [SetCast] whose runtime type is built from the runtime type
/// arguments of [entryCast].
///
/// The static type argument is generally not interesting for these objects,
/// and so `<Object?>` is used to avoid unnecessary casts.
static SetCast<Object?> from(Cast entryCast) =>
entryCast.getAsTypedCast(SetCast._);
@override
Set<E> _cast(Object? from) {
if (from is! Set) {
return super._cast(from);
}
return {
for (int i = 0; i < from.length; i++) _entryCast._cast(from.elementAt(i)),
};
}
}
/// A specific [Exception] for failed casts with information about the full path
/// to the failed cast.
class FailedCast implements Exception {
String message;
FailedCast(this.message);
@override
toString() => "Failed cast: $message";
}
-423
View File
@@ -1,423 +0,0 @@
// Copyright (c) 2023, 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 'dart:convert';
import 'dart:io';
import 'dart:isolate';
import 'dart:typed_data';
import '../api.dart';
import '../executor.dart';
import 'exception_impls.dart';
import 'execute_macro.dart';
import 'message_grouper.dart';
import 'protocol.dart';
import 'remote_instance.dart';
import 'response_impls.dart';
import 'serialization.dart';
/// Implements the client side of the macro instantiation/expansion protocol.
final class MacroExpansionClient {
/// A map of the instantiable macro constructors.
///
/// The outer map is keyed by the URI of the library defining macros, whose
/// values are Maps keyed
final Map<Uri, Map<String, Map<String, Function>>> _macroConstructors;
/// Maps macro instance identifiers to instances.
final Map<MacroInstanceIdentifierImpl, Macro> _macroInstances = {};
/// Holds on to response completers by request id.
final Map<int, Completer<Response>> _responseCompleters = {};
MacroExpansionClient._(void Function(Serializer) sendResult,
Stream<Object?> messageStream, this._macroConstructors) {
messageStream.listen((message) => _handleMessage(message, sendResult));
}
/// Spawns a client connecting either to [sendPort] or a socket address and
/// port given in [arguments].
static Future<MacroExpansionClient> start(
SerializationMode serializationMode,
Map<Uri, Map<String, Map<String, Function>>> macroConstructors,
List<String> arguments,
SendPort? sendPort) {
return withSerializationMode(serializationMode, () async {
// Function that sends the result of a `Serializer` using either
// `sendPort` or `stdout`.
void Function(Serializer) sendResult;
// The stream for incoming messages, could be either a ReceivePort, stdin,
// or a socket.
Stream<Object?> messageStream;
String? socketAddress;
int? socketPort;
if (arguments.isNotEmpty) {
if (arguments.length != 2) {
throw ArgumentError(
'Expected exactly two or zero arguments, got $arguments.');
}
socketAddress = arguments.first;
socketPort = int.parse(arguments[1]);
}
if (sendPort != null) {
ReceivePort receivePort = ReceivePort();
messageStream = receivePort;
sendResult =
(Serializer serializer) => _sendIsolateResult(serializer, sendPort);
// If using isolate communication, first send a sendPort to the parent
// isolate.
sendPort.send(receivePort.sendPort);
} else {
late Stream<List<int>> inputStream;
if (socketAddress != null && socketPort != null) {
Socket socket = await Socket.connect(socketAddress, socketPort);
// Nagle's algorithm slows us down >100x, disable it.
socket.setOption(SocketOption.tcpNoDelay, true);
sendResult = _sendIOSinkResultFactory(socket);
inputStream = socket;
} else {
sendResult = _sendIOSinkResultFactory(stdout);
inputStream = stdin;
}
if (serializationMode == SerializationMode.byteData) {
messageStream = MessageGrouper(inputStream).messageStream;
} else if (serializationMode == SerializationMode.json) {
messageStream = const Utf8Decoder()
.bind(inputStream)
.transform(const LineSplitter())
.map((line) => jsonDecode(line)!);
} else {
throw UnsupportedError(
'Unsupported serialization mode $serializationMode for '
'ProcessExecutor');
}
}
return MacroExpansionClient._(
sendResult, messageStream, macroConstructors);
});
}
void _handleMessage(
Object? message, void Function(Serializer) sendResult) async {
// Serializes `request` and sends it using `sendResult`.
Future<Response> sendRequest(Request request) =>
_sendRequest(request, sendResult);
if (serializationMode == SerializationMode.byteData &&
message is TransferableTypedData) {
message = message.materialize().asUint8List();
}
Deserializer deserializer = deserializerFactory(message)..moveNext();
int zoneId = deserializer.expectInt();
await withRemoteInstanceZone(zoneId, () async {
deserializer.moveNext();
MessageType type = MessageType.values[deserializer.expectInt()];
Serializer serializer = serializerFactory();
switch (type) {
case MessageType.instantiateMacroRequest:
InstantiateMacroRequest request =
InstantiateMacroRequest.deserialize(deserializer, zoneId);
(await _instantiateMacro(request)).serialize(serializer);
case MessageType.disposeMacroRequest:
DisposeMacroRequest request =
DisposeMacroRequest.deserialize(deserializer, zoneId);
_macroInstances.remove(request.identifier);
return;
case MessageType.executeDeclarationsPhaseRequest:
ExecuteDeclarationsPhaseRequest request =
ExecuteDeclarationsPhaseRequest.deserialize(deserializer, zoneId);
(await _executeDeclarationsPhase(request, sendRequest))
.serialize(serializer);
case MessageType.executeDefinitionsPhaseRequest:
ExecuteDefinitionsPhaseRequest request =
ExecuteDefinitionsPhaseRequest.deserialize(deserializer, zoneId);
(await _executeDefinitionsPhase(request, sendRequest))
.serialize(serializer);
case MessageType.executeTypesPhaseRequest:
ExecuteTypesPhaseRequest request =
ExecuteTypesPhaseRequest.deserialize(deserializer, zoneId);
(await _executeTypesPhase(request, sendRequest))
.serialize(serializer);
case MessageType.response:
SerializableResponse response =
SerializableResponse.deserialize(deserializer, zoneId);
_responseCompleters.remove(response.requestId)!.complete(response);
return;
case MessageType.destroyRemoteInstanceZoneRequest:
DestroyRemoteInstanceZoneRequest request =
DestroyRemoteInstanceZoneRequest.deserialize(
deserializer, zoneId);
destroyRemoteInstanceZone(request.serializationZoneId);
return;
default:
throw StateError('Unhandled event type $type');
}
sendResult(serializer);
}, createIfMissing: true);
}
/// Handles [InstantiateMacroRequest]s.
Future<SerializableResponse> _instantiateMacro(
InstantiateMacroRequest request) async {
try {
Map<String, Map<String, Function>> classes =
_macroConstructors[request.library] ??
(throw ArgumentError(
'Unrecognized macro library ${request.library}'));
Map<String, Function> constructors = classes[request.name] ??
(throw ArgumentError(
'Unrecognized macro class ${request.name} for library '
'${request.library}'));
Function constructor = constructors[request.constructor] ??
(throw ArgumentError(
'Unrecognized constructor name "${request.constructor}" for '
'macro class "${request.name}".'));
Macro instance = Function.apply(constructor, [
for (Argument argument in request.arguments.positional) argument.value,
], {
for (MapEntry<String, Argument> entry
in request.arguments.named.entries)
Symbol(entry.key): entry.value.value,
}) as Macro;
MacroInstanceIdentifierImpl identifier =
MacroInstanceIdentifierImpl(instance, request.instanceId);
_macroInstances[identifier] = instance;
return SerializableResponse(
responseType: MessageType.macroInstanceIdentifier,
response: identifier,
requestId: request.id,
serializationZoneId: request.serializationZoneId);
} catch (e, s) {
return SerializableResponse(
responseType: MessageType.exception,
exception: MacroExceptionImpl.from(e, s),
requestId: request.id,
serializationZoneId: request.serializationZoneId);
}
}
Future<SerializableResponse> _executeTypesPhase(
ExecuteTypesPhaseRequest request,
Future<Response> Function(Request request) sendRequest) async {
try {
Macro instance = _macroInstances[request.macro] ??
(throw StateError('Unrecognized macro instance ${request.macro}\n'
'Known instances: $_macroInstances)'));
TypePhaseIntrospector introspector = ClientTypePhaseIntrospector(
sendRequest,
remoteInstance: request.introspector,
serializationZoneId: request.serializationZoneId);
MacroExecutionResult result = await runPhase(
() => executeTypesMacro(instance, request.target, introspector));
return SerializableResponse(
responseType: MessageType.macroExecutionResult,
response: result,
requestId: request.id,
serializationZoneId: request.serializationZoneId);
} catch (e, s) {
return SerializableResponse(
responseType: MessageType.exception,
exception: MacroExceptionImpl.from(e, s),
requestId: request.id,
serializationZoneId: request.serializationZoneId);
}
}
Future<SerializableResponse> _executeDeclarationsPhase(
ExecuteDeclarationsPhaseRequest request,
Future<Response> Function(Request request) sendRequest) async {
try {
Macro instance = _macroInstances[request.macro] ??
(throw StateError('Unrecognized macro instance ${request.macro}\n'
'Known instances: $_macroInstances)'));
DeclarationPhaseIntrospector introspector =
ClientDeclarationPhaseIntrospector(sendRequest,
remoteInstance: request.introspector,
serializationZoneId: request.serializationZoneId);
MacroExecutionResult result = await runPhase(() =>
executeDeclarationsMacro(instance, request.target, introspector));
return SerializableResponse(
responseType: MessageType.macroExecutionResult,
response: result,
requestId: request.id,
serializationZoneId: request.serializationZoneId);
} catch (e, s) {
return SerializableResponse(
responseType: MessageType.exception,
exception: MacroExceptionImpl.from(e, s),
requestId: request.id,
serializationZoneId: request.serializationZoneId);
}
}
Future<SerializableResponse> _executeDefinitionsPhase(
ExecuteDefinitionsPhaseRequest request,
Future<Response> Function(Request request) sendRequest) async {
try {
Macro instance = _macroInstances[request.macro] ??
(throw StateError('Unrecognized macro instance ${request.macro}\n'
'Known instances: $_macroInstances)'));
DefinitionPhaseIntrospector introspector =
ClientDefinitionPhaseIntrospector(sendRequest,
remoteInstance: request.introspector,
serializationZoneId: request.serializationZoneId);
MacroExecutionResult result = await runPhase(
() => executeDefinitionMacro(instance, request.target, introspector));
return SerializableResponse(
responseType: MessageType.macroExecutionResult,
response: result,
requestId: request.id,
serializationZoneId: request.serializationZoneId);
} catch (e, s) {
return SerializableResponse(
responseType: MessageType.exception,
exception: MacroExceptionImpl.from(e, s),
requestId: request.id,
serializationZoneId: request.serializationZoneId);
}
}
/// Serializes [request], passes it to [sendResult], and sets up a [Completer]
/// in [_responseCompleters] to handle the response.
Future<Response> _sendRequest(
Request request, void Function(Serializer serializer) sendResult) {
Completer<Response> completer = Completer();
_responseCompleters[request.id] = completer;
Serializer serializer = serializerFactory();
serializer.addInt(request.serializationZoneId);
request.serialize(serializer);
sendResult(serializer);
return completer.future;
}
}
/// Sends [serializer.result] to [sendPort], possibly wrapping it in a
/// [TransferableTypedData] object.
void _sendIsolateResult(Serializer serializer, SendPort sendPort) {
if (serializationMode == SerializationMode.byteData) {
sendPort
.send(TransferableTypedData.fromList([serializer.result as Uint8List]));
} else {
sendPort.send(serializer.result);
}
}
/// Returns a function which takes a [Serializer] and sends its result to
/// [sink].
///
/// Serializes the result to a string if using JSON.
void Function(Serializer) _sendIOSinkResultFactory(IOSink sink) =>
(Serializer serializer) {
if (serializationMode == SerializationMode.json) {
sink.writeln(jsonEncode(serializer.result));
} else if (serializationMode == SerializationMode.byteData) {
Uint8List result = (serializer as ByteDataSerializer).result;
int length = result.lengthInBytes;
BytesBuilder bytesBuilder = BytesBuilder(copy: false);
bytesBuilder.add([
length >> 24 & 0xff,
length >> 16 & 0xff,
length >> 8 & 0xff,
length & 0xff,
]);
bytesBuilder.add(result);
sink.add(bytesBuilder.takeBytes());
} else {
throw UnsupportedError(
'Unsupported serialization mode $serializationMode for '
'ProcessExecutor');
}
};
/// Runs [phase] in a [Zone] which tracks scheduled tasks, completing with a
/// [StateError] if [phase] returns a value while additional tasks or timers
/// are still scheduled.
Future<MacroExecutionResult> runPhase(
Future<MacroExecutionResult> Function() phase) {
final completer = Completer<MacroExecutionResult>();
var pendingMicrotasks = 0;
var activeTimers = 0;
Zone.current
.fork(
specification: ZoneSpecification(
handleUncaughtError: (self, parent, zone, error, stackTrace) {
if (completer.isCompleted) return;
completer.completeError(error, stackTrace);
},
createTimer: (self, parent, zone, duration, f) {
activeTimers++;
return _WrappedTimer(
parent.createTimer(zone, duration, () {
activeTimers--;
f();
}),
onCancel: () => activeTimers--);
},
createPeriodicTimer: (self, parent, zone, duration, f) {
activeTimers++;
return _WrappedTimer(parent.createPeriodicTimer(zone, duration, f),
onCancel: () => activeTimers--);
},
scheduleMicrotask: (self, parent, zone, f) {
pendingMicrotasks++;
parent.scheduleMicrotask(zone, () {
pendingMicrotasks--;
assert(pendingMicrotasks >= 0);
// This should only happen if we have previously competed with an
// error. Just skip this scheduled task in that case.
if (completer.isCompleted) return;
f();
});
},
))
.runGuarded(() => phase().then((value) {
if (completer.isCompleted) return;
if (pendingMicrotasks != 0) {
throw StateError(
'Macro completed but has $pendingMicrotasks async tasks still '
'pending. Macros must complete all async work prior to '
'returning.');
}
if (activeTimers != 0) {
throw StateError(
'Macro completed but has $activeTimers active timers. '
'Macros must cancel all timers prior to returning.');
}
completer.complete(value);
}));
return completer.future;
}
/// Wraps a [Timer] to track when it is cancelled and calls [onCancel], if the
/// timer is still active.
class _WrappedTimer implements Timer {
final Timer timer;
final void Function() onCancel;
_WrappedTimer(this.timer, {required this.onCancel});
@override
void cancel() {
if (isActive) onCancel();
timer.cancel();
}
@override
bool get isActive => timer.isActive;
@override
int get tick => timer.tick;
}
@@ -1,129 +0,0 @@
// Copyright (c) 2024, 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 '../api.dart';
import 'remote_instance.dart';
import 'serialization.dart';
/// Base class for exceptions thrown during macro execution.
///
/// Macro implementations can catch these exceptions to provide more
/// information to the user. In case an exception results from user error, they
/// can provide a pointer to the likely fix. If the exception results from an
/// implementation error or unknown error, the macro implementation might give
/// the user information on where and how to file an issue.
///
/// If a `MacroException` is not caught by a macro implementation then it will
/// be reported in a user-oriented way, for example for
/// `MacroImplementationException` the displayed message suggests that there
/// is a bug in the macro implementation.
abstract base class MacroExceptionImpl extends RemoteInstance
implements MacroException {
@override
final String message;
@override
final String? stackTrace;
MacroExceptionImpl._({int? id, required this.message, this.stackTrace})
: super(id ?? RemoteInstance.uniqueId);
factory MacroExceptionImpl(
{required int id,
required RemoteInstanceKind kind,
required String message,
String? stackTrace}) {
switch (kind) {
case RemoteInstanceKind.unexpectedMacroException:
return UnexpectedMacroExceptionImpl(message,
id: id, stackTrace: stackTrace);
case RemoteInstanceKind.macroImplementationException:
return MacroImplementationExceptionImpl(message,
id: id, stackTrace: stackTrace);
case RemoteInstanceKind.macroIntrospectionCycleException:
return MacroIntrospectionCycleExceptionImpl(message,
id: id, stackTrace: stackTrace);
default:
throw ArgumentError.value(kind, 'kind');
}
}
/// Instantiates from a throwable caught during macro execution.
///
/// If [throwable] is already a subclass of `MacroException`, return it.
/// Otherwise it's an unexpected type, return an [UnexpectedMacroException].
factory MacroExceptionImpl.from(Object throwable, StackTrace stackTrace) {
if (throwable is MacroExceptionImpl) return throwable;
return UnexpectedMacroExceptionImpl(throwable.toString(),
stackTrace: stackTrace.toString());
}
@override
String toString() => '$message${stackTrace == null ? '' : '\n\n$stackTrace'}';
@override
void serializeUncached(Serializer serializer) {
super.serializeUncached(serializer);
serializer.addString(message);
serializer.addNullableString(stackTrace);
}
}
/// Something unexpected happened during macro execution.
///
/// For example, a bug in the SDK.
final class UnexpectedMacroExceptionImpl extends MacroExceptionImpl
implements UnexpectedMacroException {
UnexpectedMacroExceptionImpl(String message, {super.id, super.stackTrace})
: super._(message: message);
@override
RemoteInstanceKind get kind => RemoteInstanceKind.unexpectedMacroException;
@override
String toString() => 'UnexpectedMacroException: ${super.toString()}';
}
/// An error due to incorrect implementation was thrown during macro execution.
///
/// For example, an incorrect argument was passed to the macro API.
///
/// The type `Error` is usually used for such throwables, and it's common to
/// allow the program to crash when one is thrown.
///
/// In the case of macros, however, type `Exception` is used because the macro
/// implementation can usefully catch it in order to give the user information
/// about how to notify the macro author about the bug.
final class MacroImplementationExceptionImpl extends MacroExceptionImpl
implements MacroImplementationException {
MacroImplementationExceptionImpl(String message, {super.id, super.stackTrace})
: super._(message: message);
@override
RemoteInstanceKind get kind =>
RemoteInstanceKind.macroImplementationException;
@override
String toString() => 'MacroImplementationException: ${super.toString()}';
}
/// A cycle was detected in macro applications introspecting targets of other
/// macro applications.
///
/// The order the macros should run in is not defined, so allowing
/// introspection in this case would make the macro output non-deterministic.
/// Instead, all the introspection calls in the cycle fail with this exception.
base class MacroIntrospectionCycleExceptionImpl extends MacroExceptionImpl
implements MacroIntrospectionCycleException {
MacroIntrospectionCycleExceptionImpl(String message,
{super.id, super.stackTrace})
: super._(message: message);
@override
RemoteInstanceKind get kind =>
RemoteInstanceKind.macroIntrospectionCycleException;
@override
String toString() => 'MacroIntrospectionCycleException: ${super.toString()}';
}
@@ -1,346 +0,0 @@
// 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 '../api.dart';
import '../executor.dart';
import 'builder_impls.dart';
import 'exception_impls.dart';
import 'introspection_impls.dart';
/// Runs [macro] in the types phase and returns a [MacroExecutionResult].
Future<MacroExecutionResult> executeTypesMacro(
Macro macro, Object target, TypePhaseIntrospector introspector) async {
// Must be assigned, used for error reporting.
late final TypeBuilderBase builder;
// TODO(jakemac): More robust handling for unawaited async errors?
try {
// Shared code for most branches. If we do create it, assign it to
// `builder`.
late final TypeBuilderImpl typeBuilder =
builder = TypeBuilderImpl(introspector);
switch ((target, macro)) {
case (Library target, LibraryTypesMacro macro):
await macro.buildTypesForLibrary(target, typeBuilder);
case (ConstructorDeclaration target, ConstructorTypesMacro macro):
await macro.buildTypesForConstructor(target, typeBuilder);
case (MethodDeclaration target, MethodTypesMacro macro):
await macro.buildTypesForMethod(target, typeBuilder);
case (FunctionDeclaration target, FunctionTypesMacro macro):
await macro.buildTypesForFunction(target, typeBuilder);
case (FieldDeclaration target, FieldTypesMacro macro):
await macro.buildTypesForField(target, typeBuilder);
case (VariableDeclaration target, VariableTypesMacro macro):
await macro.buildTypesForVariable(target, typeBuilder);
case (ClassDeclaration target, ClassTypesMacro macro):
await macro.buildTypesForClass(
target,
builder = ClassTypeBuilderImpl(
target.identifier as IdentifierImpl, introspector));
case (EnumDeclaration target, EnumTypesMacro macro):
await macro.buildTypesForEnum(
target,
builder = EnumTypeBuilderImpl(
target.identifier as IdentifierImpl, introspector));
case (ExtensionDeclaration target, ExtensionTypesMacro macro):
await macro.buildTypesForExtension(target, typeBuilder);
case (ExtensionTypeDeclaration target, ExtensionTypeTypesMacro macro):
await macro.buildTypesForExtensionType(target, typeBuilder);
case (MixinDeclaration target, MixinTypesMacro macro):
await macro.buildTypesForMixin(
target,
builder = MixinTypeBuilderImpl(
target.identifier as IdentifierImpl, introspector));
case (EnumValueDeclaration target, EnumValueTypesMacro macro):
await macro.buildTypesForEnumValue(target, typeBuilder);
case (TypeAliasDeclaration target, TypeAliasTypesMacro macro):
await macro.buildTypesForTypeAlias(target, typeBuilder);
default:
throw UnsupportedError('Unsupported macro type or invalid target:\n'
'macro: $macro\ntarget: $target');
}
} catch (e, s) {
_handleError(e, s, builder);
}
return builder.result;
}
/// Runs [macro] in the declaration phase and returns a [MacroExecutionResult].
Future<MacroExecutionResult> executeDeclarationsMacro(Macro macro,
Object target, DeclarationPhaseIntrospector introspector) async {
// Must be assigned, used for error reporting.
late final DeclarationBuilderBase builder;
// At most one of these will be used below.
late MemberDeclarationBuilderImpl memberBuilder =
builder = MemberDeclarationBuilderImpl(
switch (target) {
MemberDeclaration() => target.definingType as IdentifierImpl,
TypeDeclarationImpl() => target.identifier,
_ => throw StateError(
'Can only create member declaration builders for types or '
'member declarations, but got $target'),
},
introspector);
late DeclarationBuilderImpl topLevelBuilder =
builder = DeclarationBuilderImpl(introspector);
late EnumDeclarationBuilderImpl enumBuilder =
builder = EnumDeclarationBuilderImpl(
switch (target) {
EnumDeclarationImpl() => target.identifier,
EnumValueDeclarationImpl() => target.definingEnum,
_ => throw StateError(
'Can only create enum declaration builders for enum or enum '
'value declarations, but got $target'),
},
introspector);
// TODO(jakemac): More robust handling for unawaited async errors?
try {
switch ((target, macro)) {
case (Library target, LibraryDeclarationsMacro macro):
await macro.buildDeclarationsForLibrary(target, topLevelBuilder);
case (ClassDeclaration target, ClassDeclarationsMacro macro):
await macro.buildDeclarationsForClass(target, memberBuilder);
case (EnumDeclaration target, EnumDeclarationsMacro macro):
await macro.buildDeclarationsForEnum(target, enumBuilder);
case (ExtensionDeclaration target, ExtensionDeclarationsMacro macro):
await macro.buildDeclarationsForExtension(target, memberBuilder);
case (
ExtensionTypeDeclaration target,
ExtensionTypeDeclarationsMacro macro
):
await macro.buildDeclarationsForExtensionType(target, memberBuilder);
case (MixinDeclaration target, MixinDeclarationsMacro macro):
await macro.buildDeclarationsForMixin(target, memberBuilder);
case (EnumValueDeclaration target, EnumValueDeclarationsMacro macro):
await macro.buildDeclarationsForEnumValue(target, enumBuilder);
case (ConstructorDeclaration target, ConstructorDeclarationsMacro macro):
await macro.buildDeclarationsForConstructor(target, memberBuilder);
case (MethodDeclaration target, MethodDeclarationsMacro macro):
await macro.buildDeclarationsForMethod(target, memberBuilder);
case (FieldDeclaration target, FieldDeclarationsMacro macro):
await macro.buildDeclarationsForField(target, memberBuilder);
case (FunctionDeclaration target, FunctionDeclarationsMacro macro):
await macro.buildDeclarationsForFunction(target, topLevelBuilder);
case (VariableDeclaration target, VariableDeclarationsMacro macro):
await macro.buildDeclarationsForVariable(target, topLevelBuilder);
case (TypeAliasDeclaration target, TypeAliasDeclarationsMacro macro):
await macro.buildDeclarationsForTypeAlias(target, topLevelBuilder);
default:
throw UnsupportedError('Unsupported macro type or invalid target:\n'
'macro: $macro\ntarget: $target');
}
} catch (e, s) {
_handleError(e, s, builder);
}
return builder.result;
}
/// Runs [macro] in the definition phase and returns a [MacroExecutionResult].
Future<MacroExecutionResult> executeDefinitionMacro(Macro macro, Object target,
DefinitionPhaseIntrospector introspector) async {
// Must be assigned, used for error reporting and returning a value.
late final DefinitionBuilderBase builder;
// At most one of these will be used below.
late FunctionDefinitionBuilderImpl functionBuilder = builder =
FunctionDefinitionBuilderImpl(
target as FunctionDeclarationImpl, introspector);
late VariableDefinitionBuilderImpl variableBuilder = builder =
VariableDefinitionBuilderImpl(
target as VariableDeclaration, introspector);
late TypeDefinitionBuilderImpl typeBuilder = builder =
TypeDefinitionBuilderImpl(target as TypeDeclaration, introspector);
// TODO(jakemac): More robust handling for unawaited async errors?
try {
switch ((target, macro)) {
case (Library target, LibraryDefinitionMacro macro):
LibraryDefinitionBuilderImpl libraryBuilder =
builder = LibraryDefinitionBuilderImpl(target, introspector);
await macro.buildDefinitionForLibrary(target, libraryBuilder);
case (ClassDeclaration target, ClassDefinitionMacro macro):
await macro.buildDefinitionForClass(target, typeBuilder);
case (EnumDeclaration target, EnumDefinitionMacro macro):
EnumDefinitionBuilderImpl enumBuilder =
builder = EnumDefinitionBuilderImpl(target, introspector);
await macro.buildDefinitionForEnum(target, enumBuilder);
case (ExtensionDeclaration target, ExtensionDefinitionMacro macro):
await macro.buildDefinitionForExtension(target, typeBuilder);
case (
ExtensionTypeDeclaration target,
ExtensionTypeDefinitionMacro macro
):
await macro.buildDefinitionForExtensionType(target, typeBuilder);
case (MixinDeclaration target, MixinDefinitionMacro macro):
await macro.buildDefinitionForMixin(target, typeBuilder);
case (EnumValueDeclaration target, EnumValueDefinitionMacro macro):
EnumValueDefinitionBuilderImpl enumValueBuilder = builder =
EnumValueDefinitionBuilderImpl(
target as EnumValueDeclarationImpl, introspector);
await macro.buildDefinitionForEnumValue(target, enumValueBuilder);
case (ConstructorDeclaration target, ConstructorDefinitionMacro macro):
ConstructorDefinitionBuilderImpl constructorBuilder = builder =
ConstructorDefinitionBuilderImpl(
target as ConstructorDeclarationImpl, introspector);
await macro.buildDefinitionForConstructor(target, constructorBuilder);
case (MethodDeclaration target, MethodDefinitionMacro macro):
await macro.buildDefinitionForMethod(
target as MethodDeclarationImpl, functionBuilder);
case (FieldDeclaration target, FieldDefinitionMacro macro):
await macro.buildDefinitionForField(target, variableBuilder);
case (FunctionDeclaration target, FunctionDefinitionMacro macro):
await macro.buildDefinitionForFunction(target, functionBuilder);
case (VariableDeclaration target, VariableDefinitionMacro macro):
await macro.buildDefinitionForVariable(target, variableBuilder);
default:
throw UnsupportedError('Unsupported macro type or invalid target:\n'
'macro: $macro\ntarget: $target');
}
} catch (e, s) {
_handleError(e, s, builder);
}
return builder.result;
}
/// Handles macro execution errors, specifically handling [DiagnosticException]s
/// and [MacroException]s in the expected ways.
///
/// Also unwraps [ParallelWaitError]s and [AsyncError]s, such that we can
/// recognize properly the nested errors if they are of specially handled types.
void _handleError(
Object error, StackTrace stackTrace, TypeBuilderBase builder) {
switch (error) {
case ParallelWaitError(errors: List<Object?> errors):
_handleErrors(errors, stackTrace, builder);
case ParallelWaitError(errors: (var e1,)):
_handleErrors([e1], stackTrace, builder);
case ParallelWaitError(
errors: (
var e1,
var e2,
)
):
_handleErrors([e1, e2], stackTrace, builder);
case ParallelWaitError(
errors: (
var e1,
var e2,
var e3,
)
):
_handleErrors([e1, e2, e3], stackTrace, builder);
case ParallelWaitError(
errors: (
var e1,
var e2,
var e3,
var e4,
)
):
_handleErrors([e1, e2, e3, e4], stackTrace, builder);
case ParallelWaitError(
errors: (
var e1,
var e2,
var e3,
var e4,
var e5,
)
):
_handleErrors([e1, e2, e3, e4, e5], stackTrace, builder);
case ParallelWaitError(
errors: (
var e1,
var e2,
var e3,
var e4,
var e5,
var e6,
)
):
_handleErrors([e1, e2, e3, e4, e5, e6], stackTrace, builder);
case ParallelWaitError(
errors: (
var e1,
var e2,
var e3,
var e4,
var e5,
var e6,
var e7,
)
):
_handleErrors([e1, e2, e3, e4, e5, e6, e7], stackTrace, builder);
case ParallelWaitError(
errors: (
var e1,
var e2,
var e3,
var e4,
var e5,
var e6,
var e7,
var e8,
)
):
_handleErrors([e1, e2, e3, e4, e5, e6, e7, e8], stackTrace, builder);
case ParallelWaitError(
errors: (
var e1,
var e2,
var e3,
var e4,
var e5,
var e6,
var e7,
var e8,
var e9,
)
):
_handleErrors([e1, e2, e3, e4, e5, e6, e7, e8, e9], stackTrace, builder);
// Unwrap async errors.
case AsyncError():
_handleError(error.error, error.stackTrace, builder);
// Custom diagnostics from macros, these should just be reported.
case DiagnosticException():
builder.report(error.diagnostic);
// Preserve `MacroException`s thrown by SDK tools.
case MacroExceptionImpl():
builder.failWithException(error);
case _:
// Convert exceptions thrown by macro implementations into diagnostics.
builder.report(_unexpectedExceptionDiagnostic(error, stackTrace));
}
}
/// Handles a number of [errors], ignoring null values.
///
/// This is used for parallel wait scenarios such as [Future.wait].
void _handleErrors(
List<Object?> errors, StackTrace outerStackTrace, TypeBuilderBase builder) {
for (var error in errors) {
if (error == null) continue;
// Passing the outerStackTrace here is the best we can do - but most of the
// time `error` will actually be an `AsyncError`, and we will end up using
// that stack trace anyways.
_handleError(error, outerStackTrace, builder);
}
}
// It's a bug in the macro but we need to show something to the user; put the
// debug detail in a context message and suggest reporting to the author.
Diagnostic _unexpectedExceptionDiagnostic(
Object thrown, StackTrace stackTrace) =>
Diagnostic(
DiagnosticMessage(
'Macro application failed due to a bug in the macro.'),
Severity.error,
contextMessages: [
DiagnosticMessage('$thrown\n$stackTrace'),
],
correctionMessage: 'Try reporting the failure to the macro author.');
@@ -1,338 +0,0 @@
// 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 'dart:async';
import 'dart:isolate';
import '../api.dart';
import '../executor.dart';
import 'exception_impls.dart';
import 'introspection_impls.dart';
import 'protocol.dart';
import 'remote_instance.dart';
import 'serialization.dart';
import 'span.dart';
/// Base implementation for macro executors which communicate with some external
/// process to run macros.
///
/// Subtypes must extend this class and implement the [close] and [sendResult]
/// apis to handle communication with the external macro program.
abstract class ExternalMacroExecutorBase extends MacroExecutor {
/// The stream on which we receive messages from the external macro executor.
final Stream<Object> messageStream;
/// The mode to use for serialization - must be a `server` variant.
final SerializationMode serializationMode;
/// A map of response completers by request id.
final _responseCompleters = <int, Completer<Response>>{};
bool isClosed = false;
ExternalMacroExecutorBase(
{required this.messageStream, required this.serializationMode}) {
withSerializationMode(serializationMode, () {
messageStream.listen((message) {
// No need for a remote cache in this zone we only read a zone ID and
// then immediately run in that zone.
Deserializer deserializer = deserializerFactory(message);
// Every object starts with a zone ID which dictates the zone in which
// we should deserialize the message.
deserializer.moveNext();
int zoneId = deserializer.expectInt();
withRemoteInstanceZone(zoneId, () async {
deserializer.moveNext();
MessageType messageType =
MessageType.values[deserializer.expectInt()];
// A response to a request we sent, everything else is a request from
// the client.
if (messageType == MessageType.response) {
SerializableResponse response =
SerializableResponse.deserialize(deserializer, zoneId);
Completer<Response>? completer =
_responseCompleters.remove(response.requestId);
if (completer == null) {
throw StateError('Got a response for an unrecognized request id '
'${response.requestId}');
}
completer.complete(response);
return;
}
// These are initialized in the switch below.
final Serializable? result;
final MessageType resultType;
int? requestId;
// Initialized after the switch or in the catch handler.
late final SerializableResponse response;
try {
switch (messageType) {
case MessageType.resolveIdentifierRequest:
ResolveIdentifierRequest request =
ResolveIdentifierRequest.deserialize(deserializer, zoneId);
requestId = request.id;
result = await (request.introspector.instance
as TypePhaseIntrospector)
// ignore: deprecated_member_use_from_same_package
.resolveIdentifier(request.library, request.name)
as IdentifierImpl;
resultType = MessageType.remoteInstance;
case MessageType.resolveTypeRequest:
ResolveTypeRequest request =
ResolveTypeRequest.deserialize(deserializer, zoneId);
requestId = request.id;
result = await (request.introspector.instance
as DeclarationPhaseIntrospector)
.resolve(request.typeAnnotationCode) as StaticTypeImpl;
resultType = MessageType.remoteInstance;
case MessageType.inferTypeRequest:
InferTypeRequest request =
InferTypeRequest.deserialize(deserializer, zoneId);
requestId = request.id;
result = await (request.introspector.instance
as DefinitionPhaseIntrospector)
.inferType(request.omittedType) as TypeAnnotationImpl;
resultType = MessageType.remoteInstance;
case MessageType.isExactlyTypeRequest:
IsExactlyTypeRequest request =
IsExactlyTypeRequest.deserialize(deserializer, zoneId);
requestId = request.id;
StaticType leftType = request.leftType as StaticType;
StaticType rightType = request.rightType as StaticType;
result = BooleanValue(await leftType.isExactly(rightType));
resultType = MessageType.boolean;
case MessageType.isSubtypeOfRequest:
IsSubtypeOfRequest request =
IsSubtypeOfRequest.deserialize(deserializer, zoneId);
requestId = request.id;
StaticType leftType = request.leftType as StaticType;
StaticType rightType = request.rightType as StaticType;
result = BooleanValue(await leftType.isSubtypeOf(rightType));
resultType = MessageType.boolean;
case MessageType.declarationOfRequest:
DeclarationOfRequest request = DeclarationOfRequest.deserialize(
deserializer, zoneId, messageType);
requestId = request.id;
DefinitionPhaseIntrospector introspector = request
.introspector.instance as DefinitionPhaseIntrospector;
result = (await introspector.declarationOf(request.identifier))
// TODO: Consider refactoring to avoid the need for
// this cast.
as Serializable;
resultType = MessageType.remoteInstance;
case MessageType.typeDeclarationOfRequest:
DeclarationOfRequest request = DeclarationOfRequest.deserialize(
deserializer, zoneId, messageType);
requestId = request.id;
DeclarationPhaseIntrospector introspector = request
.introspector.instance as DeclarationPhaseIntrospector;
result =
(await introspector.typeDeclarationOf(request.identifier))
// TODO: Consider refactoring to avoid the need for
// this cast.
as Serializable;
resultType = MessageType.remoteInstance;
case MessageType.constructorsOfRequest:
TypeIntrospectorRequest request =
TypeIntrospectorRequest.deserialize(
deserializer, messageType, zoneId);
requestId = request.id;
DeclarationPhaseIntrospector introspector = request
.introspector.instance as DeclarationPhaseIntrospector;
result = DeclarationList((await introspector
.constructorsOf(request.declaration as TypeDeclaration))
// TODO: Consider refactoring to avoid the need for this.
.cast<ConstructorDeclarationImpl>());
resultType = MessageType.declarationList;
case MessageType.topLevelDeclarationsOfRequest:
DeclarationsOfRequest request =
DeclarationsOfRequest.deserialize(deserializer, zoneId);
requestId = request.id;
DefinitionPhaseIntrospector introspector = request
.introspector.instance as DefinitionPhaseIntrospector;
result = DeclarationList(// force newline
(await introspector.topLevelDeclarationsOf(request.library))
// TODO: Consider refactoring to avoid the need for
// this.
.cast<DeclarationImpl>());
resultType = MessageType.declarationList;
case MessageType.fieldsOfRequest:
TypeIntrospectorRequest request =
TypeIntrospectorRequest.deserialize(
deserializer, messageType, zoneId);
requestId = request.id;
DeclarationPhaseIntrospector introspector = request
.introspector.instance as DeclarationPhaseIntrospector;
result = DeclarationList((await introspector
.fieldsOf(request.declaration as TypeDeclaration))
// TODO: Consider refactoring to avoid the need for this.
.cast<FieldDeclarationImpl>());
resultType = MessageType.declarationList;
case MessageType.methodsOfRequest:
TypeIntrospectorRequest request =
TypeIntrospectorRequest.deserialize(
deserializer, messageType, zoneId);
requestId = request.id;
DeclarationPhaseIntrospector introspector = request
.introspector.instance as DeclarationPhaseIntrospector;
result = DeclarationList((await introspector
.methodsOf(request.declaration as TypeDeclaration))
// TODO: Consider refactoring to avoid the need for this.
.cast<MethodDeclarationImpl>());
resultType = MessageType.declarationList;
case MessageType.typesOfRequest:
TypeIntrospectorRequest request =
TypeIntrospectorRequest.deserialize(
deserializer, messageType, zoneId);
requestId = request.id;
DeclarationPhaseIntrospector introspector = request
.introspector.instance as DeclarationPhaseIntrospector;
result = DeclarationList((await introspector
.typesOf(request.declaration as Library))
// TODO: Consider refactoring to avoid the need for this.
.cast<TypeDeclarationImpl>());
resultType = MessageType.declarationList;
case MessageType.valuesOfRequest:
TypeIntrospectorRequest request =
TypeIntrospectorRequest.deserialize(
deserializer, messageType, zoneId);
requestId = request.id;
DeclarationPhaseIntrospector introspector = request
.introspector.instance as DeclarationPhaseIntrospector;
result = DeclarationList((await introspector
.valuesOf(request.declaration as EnumDeclaration))
// TODO: Consider refactoring to avoid the need for this.
.cast<EnumValueDeclarationImpl>());
resultType = MessageType.declarationList;
default:
throw StateError('Unexpected message type $messageType');
}
response = SerializableResponse(
response: result,
requestId: requestId,
responseType: resultType,
serializationZoneId: zoneId);
} catch (error, stackTrace) {
// TODO: Something better here.
if (requestId == null) rethrow;
response = SerializableResponse(
exception: MacroExceptionImpl.from(error, stackTrace),
requestId: requestId,
responseType: MessageType.exception,
serializationZoneId: zoneId);
}
Serializer serializer = serializerFactory();
response.serialize(serializer);
sendResult(serializer);
});
});
});
}
/// These calls are handled by the higher level executor.
@override
String buildAugmentationLibrary(
Uri augmentedLibraryUri,
Iterable<MacroExecutionResult> macroResults,
TypeDeclaration Function(Identifier) resolveDeclaration,
ResolvedIdentifier Function(Identifier) resolveIdentifier,
TypeAnnotation? Function(OmittedTypeAnnotation) inferOmittedType,
{Map<OmittedTypeAnnotation, String>? omittedTypes,
List<Span>? spans}) =>
throw StateError('Unreachable');
@override
Future<MacroExecutionResult> executeDeclarationsPhase(
MacroInstanceIdentifier macro,
MacroTarget target,
DeclarationPhaseIntrospector introspector) =>
_sendRequest((zoneId) => ExecuteDeclarationsPhaseRequest(
macro,
target as RemoteInstance,
RemoteInstanceImpl(
instance: introspector,
id: RemoteInstance.uniqueId,
kind: RemoteInstanceKind.declarationPhaseIntrospector),
serializationZoneId: zoneId));
@override
Future<MacroExecutionResult> executeDefinitionsPhase(
MacroInstanceIdentifier macro,
MacroTarget target,
DefinitionPhaseIntrospector introspector) =>
_sendRequest((zoneId) => ExecuteDefinitionsPhaseRequest(
macro,
target as RemoteInstance,
RemoteInstanceImpl(
instance: introspector,
id: RemoteInstance.uniqueId,
kind: RemoteInstanceKind.definitionPhaseIntrospector),
serializationZoneId: zoneId));
@override
Future<MacroExecutionResult> executeTypesPhase(MacroInstanceIdentifier macro,
MacroTarget target, TypePhaseIntrospector introspector) =>
_sendRequest((zoneId) => ExecuteTypesPhaseRequest(
macro,
target as RemoteInstance,
RemoteInstanceImpl(
instance: introspector,
id: RemoteInstance.uniqueId,
kind: RemoteInstanceKind.typePhaseIntrospector),
serializationZoneId: zoneId));
@override
Future<MacroInstanceIdentifier> instantiateMacro(
Uri library, String name, String constructor, Arguments arguments) =>
_sendRequest((zoneId) => InstantiateMacroRequest(
library, name, constructor, arguments, RemoteInstance.uniqueId,
serializationZoneId: zoneId));
@override
void disposeMacro(MacroInstanceIdentifier instance) => _sendRequest(
(zoneId) => DisposeMacroRequest(instance, serializationZoneId: zoneId));
/// Sends [serializer.result] to [sendPort], possibly wrapping it in a
/// [TransferableTypedData] object.
void sendResult(Serializer serializer);
/// Creates a [Request] with a given serialization zone ID, and handles the
/// response, casting it to the expected type or throwing the error provided.
Future<T> _sendRequest<T>(Request Function(int) requestFactory) {
if (isClosed) {
throw UnexpectedMacroExceptionImpl(
"Can't send request - $runtimeType is closed!");
}
return withSerializationMode(serializationMode, () {
final int zoneId = newRemoteInstanceZone();
return withRemoteInstanceZone(zoneId, () async {
Request request = requestFactory(zoneId);
Serializer serializer = serializerFactory();
// It is our responsibility to add the zone ID header.
serializer.addInt(zoneId);
request.serialize(serializer);
sendResult(serializer);
Completer<Response> completer = Completer<Response>();
_responseCompleters[request.id] = completer;
try {
Response response = await completer.future;
T? result = response.response as T?;
if (result != null) return result;
throw response.exception!;
} finally {
// Clean up the zone after the request is done.
destroyRemoteInstanceZone(zoneId);
// Tell the remote client to clean it up as well.
Serializer serializer = serializerFactory();
serializer.addInt(zoneId);
DestroyRemoteInstanceZoneRequest(serializationZoneId: zoneId)
.serialize(serializer);
sendResult(serializer);
}
});
});
}
}
File diff suppressed because it is too large Load Diff
@@ -1,96 +0,0 @@
// 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 'dart:async';
import 'dart:isolate';
import 'dart:typed_data';
import '../executor.dart';
import '../executor/executor_base.dart';
import '../executor/serialization.dart';
/// Spawns a [MacroExecutor] as an isolate by passing [uriToSpawn] to
/// [Isolate.spawnUri], and communicating using [serializationMode].
///
/// The [uriToSpawn] can be any valid Uri for [Isolate.spawnUri].
///
/// Both [arguments] and [packageConfigUri] will be forwarded to
/// [Isolate.spawnUri] if provided.
///
/// The [serializationMode] must be a `server` variant, and [uriToSpawn] must
/// use the corresponding `client` variant.
Future<MacroExecutor> start(SerializationMode serializationMode, Uri uriToSpawn,
{List<String> arguments = const [], Uri? packageConfigUri}) async =>
_SingleIsolatedMacroExecutor.start(
uriToSpawn, serializationMode, arguments, packageConfigUri);
/// Actual implementation of the isolate based macro executor.
class _SingleIsolatedMacroExecutor extends ExternalMacroExecutorBase {
/// The send port where we should send requests.
final SendPort sendPort;
/// A function that should be invoked when shutting down this executor
/// to perform any necessary cleanup.
final void Function() onClose;
_SingleIsolatedMacroExecutor(
{required super.messageStream,
required this.onClose,
required this.sendPort,
required super.serializationMode});
static Future<_SingleIsolatedMacroExecutor> start(
Uri uriToSpawn,
SerializationMode serializationMode,
List<String> arguments,
Uri? packageConfig) async {
ReceivePort receivePort = ReceivePort();
Isolate isolate = await Isolate.spawnUri(
uriToSpawn, arguments, receivePort.sendPort,
packageConfig: packageConfig,
debugName: 'macro-executor ($uriToSpawn)');
Completer<SendPort> sendPortCompleter = Completer();
StreamController<Object> messageStreamController =
StreamController(sync: true);
receivePort.listen((message) {
if (!sendPortCompleter.isCompleted) {
sendPortCompleter.complete(message as SendPort);
} else {
if (serializationMode == SerializationMode.byteData) {
message =
(message as TransferableTypedData).materialize().asUint8List();
}
messageStreamController.add(message as Object);
}
}).onDone(messageStreamController.close);
return _SingleIsolatedMacroExecutor(
onClose: () {
receivePort.close();
isolate.kill();
},
messageStream: messageStreamController.stream,
sendPort: await sendPortCompleter.future,
serializationMode: serializationMode);
}
@override
Future<void> close() {
if (isClosed) return Future.value();
isClosed = true;
return Future.sync(onClose);
}
/// Sends the [Serializer.result] to [sendPort], possibly wrapping it in a
/// [TransferableTypedData] object.
@override
void sendResult(Serializer serializer) {
if (serializationMode == SerializationMode.byteData) {
sendPort.send(
TransferableTypedData.fromList([serializer.result as Uint8List]));
} else {
sendPort.send(serializer.result);
}
}
}
@@ -1,67 +0,0 @@
// Copyright (c) 2024, 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 'dart:io';
import 'dart:isolate';
import 'dart:typed_data';
import '../executor.dart';
import '../executor/serialization.dart';
import 'isolated_executor.dart' as isolated_executor;
import 'process_executor.dart' as process_executor;
/// Spawns a [MacroExecutor] as an isolate if possible, or with a new Dart
/// process if not.
///
/// Throws [StateError] if a Dart process is needed but the `dart` executable
/// can't be found next to [Platform.executable].
///
/// This is the only public api exposed by this library.
Future<MacroExecutor> start(SerializationMode serializationMode, Uri uriToSpawn,
{List<String> arguments = const [], Uri? packageConfigUri}) {
if (_isKernelRuntime) {
return isolated_executor.start(serializationMode, uriToSpawn,
arguments: arguments, packageConfigUri: packageConfigUri);
}
// Not running on the JIT, assume `dartaotruntime` or some other executable
// in the SDK `bin` folder.
File dartAotRuntime = File(Platform.resolvedExecutable);
List<File> dartExecutables = ['dart', 'dart.exe']
.map((name) => File.fromUri(dartAotRuntime.parent.uri.resolve(name)))
.where((f) => f.existsSync())
.toList();
if (dartExecutables.isEmpty) {
throw StateError('Failed to start macro executor from kernel: '
"can't launch isolate and can't find dart executable next to "
'${dartAotRuntime.path}.');
}
return process_executor.start(
serializationMode,
process_executor.CommunicationChannel.socket,
dartExecutables.first.path,
['run', uriToSpawn.path, ...arguments]);
}
/// Note that this is lazy, by nature of being a final top level variable.
final bool _isKernelRuntime = _checkForKernelRuntime();
bool _checkForKernelRuntime() {
// `createUriForKernelBlob` throws `UnsupportedError` if kernel blobs are not
// supported at all. We don't actually want to register kernel so pass
// invalid kernel, an empty list, resulting in an `ArgumentError` if kernel
// blobs are supported.
try {
(Isolate.current as dynamic)
.createUriForKernelBlob(Uint8List.fromList(const []));
throw StateError('Expected failure.');
} on UnsupportedError {
return false;
} on ArgumentError {
return true;
}
}
@@ -1,107 +0,0 @@
// 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 'dart:math' as math;
import 'dart:typed_data';
/// Collects messages from an input stream of bytes.
///
/// Each message should start with a 32 bit big endian uint indicating its size,
/// followed by that many bytes.
class MessageGrouper {
/// The input bytes stream subscription.
late final StreamSubscription _inputStreamSubscription;
/// The buffer to store the length bytes in.
final _FixedBuffer _lengthBuffer = _FixedBuffer(4);
/// If reading raw data, buffer for the data.
_FixedBuffer? _messageBuffer;
late final StreamController<Uint8List> _messageStreamController =
StreamController<Uint8List>(onCancel: () {
_inputStreamSubscription.cancel();
});
Stream<Uint8List> get messageStream => _messageStreamController.stream;
MessageGrouper(Stream<List<int>> inputStream) {
_inputStreamSubscription = inputStream.listen(_handleBytes, onDone: cancel);
}
/// Stop listening to the input stream for further updates, and close the
/// output stream.
void cancel() {
_inputStreamSubscription.cancel();
_messageStreamController.close();
}
void _handleBytes(List<int> bytes, [int offset = 0]) {
final _FixedBuffer? messageBuffer = _messageBuffer;
if (messageBuffer == null) {
while (offset < bytes.length && !_lengthBuffer.isReady) {
_lengthBuffer.addByte(bytes[offset++]);
}
if (_lengthBuffer.isReady) {
int length = _lengthBuffer[0] << 24 |
_lengthBuffer[1] << 16 |
_lengthBuffer[2] << 8 |
_lengthBuffer[3];
// Reset the length reading state.
_lengthBuffer.reset();
// Switch to the message payload reading state.
_messageBuffer = _FixedBuffer(length);
_handleBytes(bytes, offset);
} else {
// Continue reading the length.
return;
}
} else {
// Read the data from `bytes`.
offset += messageBuffer.addBytes(bytes, offset);
// If we completed a message, add it to the output stream.
if (messageBuffer.isReady) {
_messageStreamController.add(messageBuffer.bytes);
// Switch to the length reading state.
_messageBuffer = null;
_handleBytes(bytes, offset);
}
}
}
}
/// A buffer of fixed length.
class _FixedBuffer {
final Uint8List bytes;
/// The offset in [bytes].
int _offset = 0;
_FixedBuffer(int length) : bytes = Uint8List(length);
/// Return `true` when the required number of bytes added.
bool get isReady => _offset == bytes.length;
int operator [](int index) => bytes[index];
void addByte(int byte) {
bytes[_offset++] = byte;
}
/// Consume at most as many bytes from [source] as required by fill [bytes].
/// Return the number of consumed bytes.
int addBytes(List<int> source, int offset) {
int toConsume = math.min(source.length - offset, bytes.length - _offset);
bytes.setRange(_offset, _offset + toConsume, source, offset);
_offset += toConsume;
return toConsume;
}
/// Reset the number of added bytes to zero.
void reset() {
_offset = 0;
}
}
@@ -1,178 +0,0 @@
// 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 'dart:async';
import '../api.dart';
import '../executor.dart';
import '../executor/augmentation_library.dart';
/// A [MacroExecutor] implementation which delegates most work to other
/// executors which are spawned through a provided callback.
class MultiMacroExecutor extends MacroExecutor with AugmentationLibraryBuilder {
/// Executors by [MacroInstanceIdentifier].
///
/// Using an expando means we don't have to worry about cleaning up instances
/// for executors that were shut down.
final Expando<ExecutorFactoryToken> _instanceExecutors = Expando();
/// Registered factories for starting up a new macro executor for a library.
final Map<Uri, ExecutorFactoryToken> _libraryExecutorFactories = {};
/// All known registered executor factories.
final Set<ExecutorFactoryToken> _executorFactoryTokens = {};
/// Whether or not an executor factory for [library] is currently registered.
bool libraryIsRegistered(Uri library) =>
_libraryExecutorFactories.containsKey(library);
/// Registers a [factory] which can produce a [MacroExecutor] that can be
/// used to run any macro defined in [libraries].
///
/// Throws an [ArgumentError] if a library in [libraries] already has a
/// factory registered.
///
/// Returns a token which can be used to shut down any executors spawned in
/// this way via [unregisterExecutorFactory].
ExecutorFactoryToken registerExecutorFactory(
FutureOr<MacroExecutor> Function() factory, Set<Uri> libraries) {
ExecutorFactoryToken token = ExecutorFactoryToken._(factory, libraries);
_executorFactoryTokens.add(token);
for (Uri library in libraries) {
if (_libraryExecutorFactories.containsKey(library)) {
throw ArgumentError(
'Attempted to register a macro executor factory for library '
'$library which already has one assigned.');
}
_libraryExecutorFactories[library] = token;
}
return token;
}
/// Unregisters [token] for all [libraries].
///
/// If [libraries] is not passed (or `null`), then the token is unregistered
/// for all libraries.
///
/// If no libraries are registered for [token] after this call, then the
/// executor mapped to [token] will be shut down and the token will be freed.
///
/// This should be called whenever the executors might be stale, or as an
/// optimization to shut them down when they are known to be not used any
/// longer.
Future<void> unregisterExecutorFactory(ExecutorFactoryToken token,
{Set<Uri>? libraries}) async {
bool shouldClose;
if (libraries == null) {
libraries = token._libraries;
shouldClose = true;
} else {
token._libraries.removeAll(libraries);
shouldClose = token._libraries.isEmpty;
}
for (Uri library in libraries) {
_libraryExecutorFactories.remove(library);
}
if (shouldClose) {
_executorFactoryTokens.remove(token);
token._libraries.clear();
await token._close();
}
}
/// Shuts down all executors and clears all configuration.
Future<void> closeAndReset() async {
await Future.wait(_executorFactoryTokens
.toList()
.map((token) => unregisterExecutorFactory(token)));
}
/// Shuts down all executors, but does not clear [_libraryExecutorFactories]
/// or [_executorFactoryTokens].
@override
Future<void> close() {
Future done = Future.wait([
for (ExecutorFactoryToken token in _executorFactoryTokens) token._close(),
]);
return done;
}
@override
Future<MacroExecutionResult> executeDeclarationsPhase(
MacroInstanceIdentifier macro,
MacroTarget target,
DeclarationPhaseIntrospector introspector) =>
_instanceExecutors[macro]!._withInstance((executor) =>
executor.executeDeclarationsPhase(macro, target, introspector));
@override
Future<MacroExecutionResult> executeDefinitionsPhase(
MacroInstanceIdentifier macro,
MacroTarget target,
DefinitionPhaseIntrospector introspector) =>
_instanceExecutors[macro]!._withInstance((executor) =>
executor.executeDefinitionsPhase(macro, target, introspector));
@override
Future<MacroExecutionResult> executeTypesPhase(MacroInstanceIdentifier macro,
MacroTarget target, TypePhaseIntrospector introspector) =>
_instanceExecutors[macro]!._withInstance((executor) =>
executor.executeTypesPhase(macro, target, introspector));
@override
Future<MacroInstanceIdentifier> instantiateMacro(
Uri library, String name, String constructor, Arguments arguments) {
ExecutorFactoryToken? token = _libraryExecutorFactories[library];
if (token == null) {
throw ArgumentError('No executor registered to run macros from $library');
}
return token._withInstance((executor) async {
MacroInstanceIdentifier instance = await executor.instantiateMacro(
library, name, constructor, arguments);
_instanceExecutors[instance] = token;
return instance;
});
}
@override
void disposeMacro(MacroInstanceIdentifier instance) {
_instanceExecutors[instance]!._withInstance((executor) {
executor.disposeMacro(instance);
});
}
}
/// A token to track registered [MacroExecutor] factories.
///
/// Used to unregister them later on, and also handles bookkeeping for the
/// factory and actual instances.
class ExecutorFactoryToken {
final FutureOr<MacroExecutor> Function() _factory;
FutureOr<MacroExecutor>? _instance;
final Set<Uri> _libraries;
ExecutorFactoryToken._(this._factory, this._libraries);
/// Runs [callback] with an actual instance once available.
///
/// This will spin up an instance if one is not currently running.
Future<T> _withInstance<T>(
FutureOr<T> Function(MacroExecutor) callback) async =>
callback(await (_instance ??= _factory()));
/// Closes [_instance] if non-null, and sets it to `null`.
Future<void> _close() async {
FutureOr<MacroExecutor>? instance = _instance;
_instance = null;
if (instance != null) {
if (instance is Future<MacroExecutor>) {
await (await instance).close();
} else {
await instance.close();
}
}
}
}
@@ -1,191 +0,0 @@
// 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 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import '../executor.dart';
import 'exception_impls.dart';
import 'executor_base.dart';
import 'message_grouper.dart';
import 'serialization.dart';
/// Spawns a [MacroExecutor] as a separate process, by running [program] with
/// [arguments], and communicating using [serializationMode].
///
/// The [serializationMode] must be a `server` variant, and [program] must use
/// the corresponding `client` variant.
///
/// This is the only public api exposed by this library.
Future<MacroExecutor> start(SerializationMode serializationMode,
CommunicationChannel communicationChannel, String program,
[List<String> arguments = const []]) {
switch (communicationChannel) {
case CommunicationChannel.stdio:
return _SingleProcessMacroExecutor.startWithStdio(
serializationMode, program, arguments);
case CommunicationChannel.socket:
return _SingleProcessMacroExecutor.startWithSocket(
serializationMode, program, arguments);
}
}
/// Actual implementation of the separate process based macro executor.
class _SingleProcessMacroExecutor extends ExternalMacroExecutorBase {
/// The IOSink that writes to stdin of the external process.
final IOSink outSink;
/// A function that should be invoked when shutting down this executor
/// to perform any necessary cleanup.
final void Function() onClose;
_SingleProcessMacroExecutor(
{required super.messageStream,
required this.onClose,
required this.outSink,
required super.serializationMode});
static Future<_SingleProcessMacroExecutor> startWithSocket(
SerializationMode serializationMode,
String programPath,
List<String> arguments) async {
ServerSocket serverSocket;
// Try an ipv6 address loopback first, and fall back on ipv4.
try {
serverSocket = await ServerSocket.bind(InternetAddress.loopbackIPv6, 0);
} on SocketException catch (_) {
serverSocket = await ServerSocket.bind(InternetAddress.loopbackIPv4, 0);
}
Process process;
try {
process = await Process.start(programPath, [
...arguments,
serverSocket.address.address,
serverSocket.port.toString(),
]);
} catch (e) {
await serverSocket.close();
rethrow;
}
process.stderr.transform(const Utf8Decoder()).listen((content) =>
throw UnexpectedMacroExceptionImpl(
'stderr output by macro process: $content'));
process.stdout.transform(const Utf8Decoder()).listen(
(event) => print('Stdout from MacroExecutor at $programPath:\n$event'));
Completer<Socket> clientCompleter = Completer();
serverSocket.listen((client) {
clientCompleter.complete(client);
});
Socket client = await clientCompleter.future;
// Nagle's algorithm slows us down >100x, disable it.
client.setOption(SocketOption.tcpNoDelay, true);
Stream<Object> messageStream;
if (serializationMode == SerializationMode.byteData) {
messageStream = MessageGrouper(client).messageStream;
} else if (serializationMode == SerializationMode.json) {
messageStream = const Utf8Decoder()
.bind(client)
.transform(const LineSplitter())
.map((line) => jsonDecode(line) as Object);
} else {
throw UnsupportedError(
'Unsupported serialization mode \$serializationMode for '
'ProcessExecutor');
}
return _SingleProcessMacroExecutor(
onClose: () {
try {
client.close();
} catch (_) {
// The `process.kill` two lines down can trigger an exception here
// because the remote side closes the socket first. Ignore it.
}
serverSocket.close();
process.kill();
},
messageStream: messageStream,
outSink: client,
serializationMode: serializationMode);
}
static Future<_SingleProcessMacroExecutor> startWithStdio(
SerializationMode serializationMode,
String programPath,
List<String> arguments) async {
Process process = await Process.start(programPath, arguments);
process.stderr.transform(const Utf8Decoder()).listen((content) =>
throw UnexpectedMacroExceptionImpl(
'stderr output by macro process: $content'));
Stream<Object> messageStream;
if (serializationMode == SerializationMode.byteData) {
messageStream = MessageGrouper(process.stdout).messageStream;
} else if (serializationMode == SerializationMode.json) {
messageStream = process.stdout
.transform(const Utf8Decoder())
.transform(const LineSplitter())
.map((line) => jsonDecode(line) as Object);
} else {
throw UnsupportedError(
'Unsupported serialization mode \$serializationMode for '
'ProcessExecutor');
}
return _SingleProcessMacroExecutor(
onClose: () {
process.kill();
},
messageStream: messageStream,
outSink: process.stdin,
serializationMode: serializationMode);
}
@override
Future<void> close() {
if (isClosed) return Future.value();
isClosed = true;
return Future.sync(onClose);
}
/// Sends the [Serializer.result] to [stdin].
///
/// Json results are serialized to a `String`, and separated by newlines.
@override
void sendResult(Serializer serializer) {
if (serializationMode == SerializationMode.json) {
outSink.writeln(jsonEncode(serializer.result));
} else if (serializationMode == SerializationMode.byteData) {
Uint8List result = (serializer as ByteDataSerializer).result;
int length = result.lengthInBytes;
if (length > 0xffffffff) {
throw StateError('Message was larger than the allowed size!');
}
BytesBuilder bytesBuilder = BytesBuilder(copy: false);
bytesBuilder.add([
length >> 24 & 0xff,
length >> 16 & 0xff,
length >> 8 & 0xff,
length & 0xff
]);
bytesBuilder.add(result);
outSink.add(bytesBuilder.takeBytes());
} else {
throw UnsupportedError(
'Unsupported serialization mode $serializationMode for '
'ProcessExecutor');
}
}
}
enum CommunicationChannel {
socket,
stdio,
}
-873
View File
@@ -1,873 +0,0 @@
// 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.
/// Defines the objects used for communication between the macro executor and
/// the isolate or process doing the work of macro loading and execution.
library _fe_analyzer_shared.src.macros.executor_shared.protocol;
import '../api.dart';
import '../executor.dart';
import 'exception_impls.dart';
import 'introspection_impls.dart';
import 'remote_instance.dart';
import 'response_impls.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;
final int serializationZoneId;
Request({int? id, required this.serializationZoneId}) : id = id ?? _next++;
/// The [serializationZoneId] is a part of the header and needs to be parsed
/// before deserializing objects, and then passed in here.
Request.deserialize(Deserializer deserializer, this.serializationZoneId)
: id = (deserializer..moveNext()).expectInt();
/// The [serializationZoneId] needs to be separately serialized before the
/// rest of the object. This is not done by the instances themselves but by
/// the macro implementations.
@override
void serialize(Serializer serializer) => serializer.addInt(id);
static int _next = 0;
}
/// A generic response object that contains either a response or an exception,
/// and a unique ID.
class Response {
final Object? response;
final MacroException? exception;
final int requestId;
final MessageType responseType;
Response({
this.response,
this.exception,
required this.requestId,
required this.responseType,
}) : assert(response != null || exception != null),
assert(response == null || exception == null);
}
/// A serializable [Response], contains the message type as an enum.
class SerializableResponse implements Response, Serializable {
@override
final Serializable? response;
@override
final MessageType responseType;
@override
final MacroExceptionImpl? exception;
@override
final int requestId;
final int serializationZoneId;
SerializableResponse({
this.exception,
required this.requestId,
this.response,
required this.responseType,
required this.serializationZoneId,
});
/// 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.expectInt()];
Serializable? response;
MacroExceptionImpl? exception;
switch (responseType) {
case MessageType.exception:
deserializer.moveNext();
exception = deserializer.expectRemoteInstance();
break;
case MessageType.macroInstanceIdentifier:
response = MacroInstanceIdentifierImpl.deserialize(deserializer);
break;
case MessageType.macroExecutionResult:
response = MacroExecutionResultImpl.deserialize(deserializer);
break;
case MessageType.staticType:
case MessageType.namedStaticType:
response = RemoteInstance.deserialize(deserializer);
break;
case MessageType.boolean:
response = BooleanValue.deserialize(deserializer);
break;
case MessageType.declarationList:
response = DeclarationList.deserialize(deserializer);
break;
case MessageType.remoteInstance:
deserializer.moveNext();
if (!deserializer.checkNull()) {
response = deserializer.expectRemoteInstance();
}
break;
default:
throw StateError('Unexpected response type $responseType');
}
return SerializableResponse(
responseType: responseType,
response: response,
exception: exception,
requestId: (deserializer..moveNext()).expectInt(),
serializationZoneId: serializationZoneId);
}
@override
void serialize(Serializer serializer) {
serializer
..addInt(serializationZoneId)
..addInt(MessageType.response.index)
..addInt(responseType.index);
switch (responseType) {
case MessageType.exception:
exception!.serialize(serializer);
break;
default:
response.serializeNullable(serializer);
}
serializer.addInt(requestId);
}
}
class BooleanValue implements Serializable {
final bool value;
BooleanValue(this.value);
BooleanValue.deserialize(Deserializer deserializer)
: value = (deserializer..moveNext()).expectBool();
@override
void serialize(Serializer serializer) => serializer..addBool(value);
}
/// A serialized list of [Declaration]s.
class DeclarationList<T extends DeclarationImpl> implements Serializable {
final List<T> declarations;
DeclarationList(this.declarations);
DeclarationList.deserialize(Deserializer deserializer)
: declarations = [
for (bool hasNext = (deserializer
..moveNext()
..expectList())
.moveNext();
hasNext;
hasNext = deserializer.moveNext())
deserializer.expectRemoteInstance(),
];
@override
void serialize(Serializer serializer) {
serializer.startList();
for (DeclarationImpl declaration in declarations) {
declaration.serialize(serializer);
}
serializer.endList();
}
}
/// A request to load a macro in this isolate.
class LoadMacroRequest extends Request {
final Uri library;
final String name;
LoadMacroRequest(this.library, this.name,
{required super.serializationZoneId});
LoadMacroRequest.deserialize(super.deserializer, super.serializationZoneId)
: library = Uri.parse((deserializer..moveNext()).expectString()),
name = (deserializer..moveNext()).expectString(),
super.deserialize();
@override
void serialize(Serializer serializer) {
serializer
..addInt(MessageType.loadMacroRequest.index)
..addString(library.toString())
..addString(name);
super.serialize(serializer);
}
}
/// A request to instantiate a macro instance.
class InstantiateMacroRequest extends Request {
final Uri library;
final String name;
final String constructor;
final Arguments arguments;
/// The ID to assign to the identifier, this needs to come from the requesting
/// side so that it is unique.
final int instanceId;
InstantiateMacroRequest(this.library, this.name, this.constructor,
this.arguments, this.instanceId,
{required super.serializationZoneId});
InstantiateMacroRequest.deserialize(
super.deserializer, super.serializationZoneId)
: library = (deserializer..moveNext()).expectUri(),
name = (deserializer..moveNext()).expectString(),
constructor = (deserializer..moveNext()).expectString(),
arguments = Arguments.deserialize(deserializer),
instanceId = (deserializer..moveNext()).expectInt(),
super.deserialize();
@override
void serialize(Serializer serializer) {
serializer
..addInt(MessageType.instantiateMacroRequest.index)
..addUri(library)
..addString(name)
..addString(constructor)
..addSerializable(arguments)
..addInt(instanceId);
super.serialize(serializer);
}
}
/// A request to dispose a macro instance by ID.
class DisposeMacroRequest extends Request {
final MacroInstanceIdentifier identifier;
DisposeMacroRequest(this.identifier, {required super.serializationZoneId});
DisposeMacroRequest.deserialize(super.deserializer, super.serializationZoneId)
: identifier = MacroInstanceIdentifierImpl.deserialize(deserializer),
super.deserialize();
@override
void serialize(Serializer serializer) {
serializer
..addInt(MessageType.disposeMacroRequest.index)
..addSerializable(identifier);
super.serialize(serializer);
}
}
/// Base class for the requests to execute a macro in a certain phase.
abstract class ExecutePhaseRequest extends Request {
final MacroInstanceIdentifier macro;
final RemoteInstance target;
final RemoteInstanceImpl introspector;
MessageType get kind;
ExecutePhaseRequest(this.macro, this.target, this.introspector,
{required super.serializationZoneId});
/// When deserializing we have already consumed the message type, so we don't
/// consume it again.
ExecutePhaseRequest.deserialize(super.deserializer, super.serializationZoneId)
: macro = MacroInstanceIdentifierImpl.deserialize(deserializer),
target = RemoteInstance.deserialize(deserializer),
introspector = RemoteInstance.deserialize(deserializer),
super.deserialize();
@override
void serialize(Serializer serializer) {
serializer.addInt(kind.index);
macro.serialize(serializer);
target.serialize(serializer);
introspector.serialize(serializer);
super.serialize(serializer);
}
}
/// A request to execute a macro on a particular declaration in the types phase.
class ExecuteTypesPhaseRequest extends ExecutePhaseRequest {
@override
MessageType get kind => MessageType.executeTypesPhaseRequest;
ExecuteTypesPhaseRequest(super.macro, super.target, super.identifierResolver,
{required super.serializationZoneId});
ExecuteTypesPhaseRequest.deserialize(
super.deserializer, super.serializationZoneId)
: super.deserialize();
}
/// A request to execute a macro on a particular declaration in the types phase.
class ExecuteDeclarationsPhaseRequest extends ExecutePhaseRequest {
@override
MessageType get kind => MessageType.executeDeclarationsPhaseRequest;
ExecuteDeclarationsPhaseRequest(
super.macro, super.target, super.identifierResolver,
{required super.serializationZoneId});
ExecuteDeclarationsPhaseRequest.deserialize(
super.deserializer, super.serializationZoneId)
: super.deserialize();
}
/// A request to execute a macro on a particular declaration in the types phase.
class ExecuteDefinitionsPhaseRequest extends ExecutePhaseRequest {
@override
MessageType get kind => MessageType.executeDefinitionsPhaseRequest;
ExecuteDefinitionsPhaseRequest(
super.macro, super.target, super.identifierResolver,
{required super.serializationZoneId});
ExecuteDefinitionsPhaseRequest.deserialize(
super.deserializer, super.serializationZoneId)
: super.deserialize();
}
/// A request to destroy a remote instance zone by id.
class DestroyRemoteInstanceZoneRequest extends Request {
DestroyRemoteInstanceZoneRequest({required super.serializationZoneId});
DestroyRemoteInstanceZoneRequest.deserialize(
super.deserializer, super.serializationZoneId)
: super.deserialize();
@override
void serialize(Serializer serializer) {
serializer.addInt(MessageType.destroyRemoteInstanceZoneRequest.index);
super.serialize(serializer);
}
}
class IntrospectionRequest extends Request {
final RemoteInstanceImpl introspector;
IntrospectionRequest(this.introspector, {required super.serializationZoneId});
IntrospectionRequest.deserialize(
super.deserializer, super.serializationZoneId)
: introspector = RemoteInstance.deserialize(deserializer),
super.deserialize();
@override
void serialize(Serializer serializer) {
introspector.serialize(serializer);
super.serialize(serializer);
}
}
/// A request to create a resolved identifier.
class ResolveIdentifierRequest extends IntrospectionRequest {
final Uri library;
final String name;
/// When deserializing we have already consumed the message type, so we don't
/// consume it again.
ResolveIdentifierRequest(this.library, this.name, super.introspector,
{required super.serializationZoneId});
ResolveIdentifierRequest.deserialize(
super.deserializer, super.serializationZoneId)
: library = Uri.parse((deserializer..moveNext()).expectString()),
name = (deserializer..moveNext()).expectString(),
super.deserialize();
@override
void serialize(Serializer serializer) {
serializer
..addInt(MessageType.resolveIdentifierRequest.index)
..addString(library.toString())
..addString(name);
super.serialize(serializer);
}
}
/// A request to resolve on a type annotation code object
class ResolveTypeRequest extends IntrospectionRequest {
final TypeAnnotationCode typeAnnotationCode;
ResolveTypeRequest(this.typeAnnotationCode, super.introspector,
{required super.serializationZoneId});
/// When deserializing we have already consumed the message type, so we don't
/// consume it again.
ResolveTypeRequest.deserialize(super.deserializer, super.serializationZoneId)
: typeAnnotationCode = (deserializer..moveNext()).expectCode(),
super.deserialize();
@override
void serialize(Serializer serializer) {
serializer.addInt(MessageType.resolveTypeRequest.index);
typeAnnotationCode.serialize(serializer);
super.serialize(serializer);
}
}
/// A request to check if a type is exactly another type.
class IsExactlyTypeRequest extends Request {
final RemoteInstance leftType;
final RemoteInstance rightType;
IsExactlyTypeRequest(this.leftType, this.rightType,
{required super.serializationZoneId});
/// When deserializing we have already consumed the message type, so we don't
/// consume it again.
IsExactlyTypeRequest.deserialize(
super.deserializer, super.serializationZoneId)
: leftType = RemoteInstance.deserialize(deserializer),
rightType = RemoteInstance.deserialize(deserializer),
super.deserialize();
@override
void serialize(Serializer serializer) {
serializer.addInt(MessageType.isExactlyTypeRequest.index);
leftType.serialize(serializer);
rightType.serialize(serializer);
super.serialize(serializer);
}
}
/// A request to check if a type is exactly another type.
class IsSubtypeOfRequest extends Request {
final RemoteInstance leftType;
final RemoteInstance rightType;
IsSubtypeOfRequest(this.leftType, this.rightType,
{required super.serializationZoneId});
/// When deserializing we have already consumed the message type, so we don't
/// consume it again.
IsSubtypeOfRequest.deserialize(super.deserializer, super.serializationZoneId)
: leftType = RemoteInstance.deserialize(deserializer),
rightType = RemoteInstance.deserialize(deserializer),
super.deserialize();
@override
void serialize(Serializer serializer) {
serializer.addInt(MessageType.isSubtypeOfRequest.index);
leftType.serialize(serializer);
rightType.serialize(serializer);
super.serialize(serializer);
}
}
/// A request to check if a type is a subtype of the type defined by an
/// identifier, and, if so, also obtain the matching instantiation.
class AsInstanceOfRequest extends Request {
final RemoteInstance left;
final TypeDeclarationImpl right;
AsInstanceOfRequest(this.left, this.right,
{required super.serializationZoneId});
/// When deserializing we have already consumed the message type, so we don't
/// consume it again.
AsInstanceOfRequest.deserialize(super.deserializer, super.serializationZoneId)
: left = RemoteInstance.deserialize(deserializer),
right = RemoteInstance.deserialize(deserializer),
super.deserialize();
@override
void serialize(Serializer serializer) {
serializer.addInt(MessageType.asInstanceOfRequest.index);
left.serialize(serializer);
right.serialize(serializer);
super.serialize(serializer);
}
}
/// A general request class for all requests coming from methods on the
/// [DeclarationPhaseIntrospector] interface that are related to a single type.
class TypeIntrospectorRequest extends IntrospectionRequest {
final Object declaration;
final MessageType requestKind;
TypeIntrospectorRequest(
this.declaration, super.introspector, this.requestKind,
{required super.serializationZoneId});
/// When deserializing we have already consumed the message type, so we don't
/// consume it again and it should instead be passed in here.
TypeIntrospectorRequest.deserialize(
Deserializer deserializer, this.requestKind, int serializationZoneId)
: declaration = RemoteInstance.deserialize(deserializer),
super.deserialize(deserializer, serializationZoneId);
@override
void serialize(Serializer serializer) {
serializer.addInt(requestKind.index);
(declaration as Serializable).serialize(serializer);
super.serialize(serializer);
}
}
/// A request to get a [Declaration] for an [identifier].
///
/// Used for both the `typeDeclarationOf` and `declarationOf` requests. A cast
/// is done on the client side to ensure only [TypeDeclaration]s are returned
/// from `typeDeclarationOf`.
class DeclarationOfRequest extends IntrospectionRequest {
final IdentifierImpl identifier;
final MessageType kind;
DeclarationOfRequest(this.identifier, this.kind, super.introspector,
{required super.serializationZoneId})
: assert(kind == MessageType.typeDeclarationOfRequest ||
kind == MessageType.declarationOfRequest);
/// When deserializing we have already consumed the message type, so we don't
/// consume it again.
DeclarationOfRequest.deserialize(
super.deserializer, super.serializationZoneId, this.kind)
: assert(kind == MessageType.typeDeclarationOfRequest ||
kind == MessageType.declarationOfRequest),
identifier = RemoteInstance.deserialize(deserializer),
super.deserialize();
@override
void serialize(Serializer serializer) {
serializer.addInt(kind.index);
identifier.serialize(serializer);
super.serialize(serializer);
}
}
/// A request to get an inferred [TypeAnnotation] for an
/// [OmittedTypeAnnotation].
class InferTypeRequest extends IntrospectionRequest {
final OmittedTypeAnnotationImpl omittedType;
InferTypeRequest(this.omittedType, super.introspector,
{required super.serializationZoneId});
/// When deserializing we have already consumed the message type, so we don't
/// consume it again.
InferTypeRequest.deserialize(super.deserializer, super.serializationZoneId)
: omittedType = RemoteInstance.deserialize(deserializer),
super.deserialize();
@override
void serialize(Serializer serializer) {
serializer.addInt(MessageType.inferTypeRequest.index);
omittedType.serialize(serializer);
super.serialize(serializer);
}
}
/// A request to get all the top level [Declaration]s in a [Library].
class DeclarationsOfRequest extends IntrospectionRequest {
final LibraryImpl library;
DeclarationsOfRequest(this.library, super.introspector,
{required super.serializationZoneId});
/// When deserializing we have already consumed the message type, so we don't
/// consume it again.
DeclarationsOfRequest.deserialize(
super.deserializer, super.serializationZoneId)
: library = RemoteInstance.deserialize(deserializer),
super.deserialize();
@override
void serialize(Serializer serializer) {
serializer.addInt(MessageType.topLevelDeclarationsOfRequest.index);
library.serialize(serializer);
super.serialize(serializer);
}
}
/// Signature of a function able to send requests and return a response using
/// an arbitrary communication channel.
typedef SendRequest = Future<Response> Function(Request request);
/// The base class for the client side introspectors from any phase, as well as
/// client side [StaticType]s.
///
/// These convert all method calls into RPCs, sent via [_sendRequest].
base class ClientIntrospector {
/// The actual remote instance to call methods on.
final RemoteInstanceImpl remoteInstance;
/// The ID of the zone in which to find the original builder.
final int serializationZoneId;
/// A function that can send a request and return a response using an
/// arbitrary communication channel.
final SendRequest _sendRequest;
ClientIntrospector(this._sendRequest,
{required this.remoteInstance, required this.serializationZoneId});
}
/// Client side implementation of an [TypeBuilder], which creates converts all
/// method calls to remote procedure calls and sends them using [_sendRequest].
final class ClientTypePhaseIntrospector extends ClientIntrospector
implements TypePhaseIntrospector {
ClientTypePhaseIntrospector(super._sendRequest,
{required super.remoteInstance, required super.serializationZoneId});
@override
Future<Identifier> resolveIdentifier(Uri library, String name) async {
ResolveIdentifierRequest request = ResolveIdentifierRequest(
library, name, remoteInstance,
serializationZoneId: serializationZoneId);
return _handleResponse(await _sendRequest(request));
}
}
/// Client side implementation of a [DeclarationBuilder].
final class ClientDeclarationPhaseIntrospector
extends ClientTypePhaseIntrospector
implements DeclarationPhaseIntrospector {
static final _constructorsCache =
Expando<Future<List<ConstructorDeclaration>>>();
static final _enumValuesCache = Expando<Future<List<EnumValueDeclaration>>>();
static final _fieldsCache = Expando<Future<List<FieldDeclaration>>>();
static final _methodsCache = Expando<Future<List<MethodDeclaration>>>();
static final _typeDeclarationCache = Expando<Future<TypeDeclaration>>();
ClientDeclarationPhaseIntrospector(super._sendRequest,
{required super.remoteInstance, required super.serializationZoneId});
@override
Future<StaticType> resolve(TypeAnnotationCode typeAnnotation) async {
ResolveTypeRequest request = ResolveTypeRequest(
typeAnnotation, remoteInstance,
serializationZoneId: serializationZoneId);
StaticTypeImpl remoteType = _handleResponse(await _sendRequest(request));
return ClientStaticTypeImpl.ofRemote(
instance: remoteType,
serializationZoneId: serializationZoneId,
sendRequest: _sendRequest,
);
}
@override
Future<List<ConstructorDeclaration>> constructorsOf(TypeDeclaration type) {
return _constructorsCache[type] ??= Future(() async {
final request = TypeIntrospectorRequest(
type, remoteInstance, MessageType.constructorsOfRequest,
serializationZoneId: serializationZoneId);
return _handleResponse<DeclarationList>(await _sendRequest(request))
.declarations
// TODO: Refactor so we can remove this cast
.cast();
});
}
@override
Future<List<EnumValueDeclaration>> valuesOf(EnumDeclaration type) {
return _enumValuesCache[type] ??= Future(() async {
final request = TypeIntrospectorRequest(
type, remoteInstance, MessageType.valuesOfRequest,
serializationZoneId: serializationZoneId);
return _handleResponse<DeclarationList>(await _sendRequest(request))
.declarations
// TODO: Refactor so we can remove this cast
.cast();
});
}
@override
Future<List<FieldDeclaration>> fieldsOf(TypeDeclaration type) {
return _fieldsCache[type] ??= Future(() async {
final request = TypeIntrospectorRequest(
type, remoteInstance, MessageType.fieldsOfRequest,
serializationZoneId: serializationZoneId);
return _handleResponse<DeclarationList>(await _sendRequest(request))
.declarations
// TODO: Refactor so we can remove this cast
.cast();
});
}
@override
Future<List<MethodDeclaration>> methodsOf(TypeDeclaration type) {
return _methodsCache[type] ??= Future(() async {
final request = TypeIntrospectorRequest(
type, remoteInstance, MessageType.methodsOfRequest,
serializationZoneId: serializationZoneId);
return _handleResponse<DeclarationList>(await _sendRequest(request))
.declarations
// TODO: Refactor so we can remove this cast
.cast();
});
}
@override
Future<List<TypeDeclaration>> typesOf(Library library) async {
TypeIntrospectorRequest request = TypeIntrospectorRequest(
library, remoteInstance, MessageType.typesOfRequest,
serializationZoneId: serializationZoneId);
return _handleResponse<DeclarationList>(await _sendRequest(request))
.declarations
// TODO: Refactor so we can remove this cast.
.cast();
}
@override
Future<TypeDeclaration> typeDeclarationOf(IdentifierImpl identifier) async {
return _typeDeclarationCache[identifier] ??= Future(() async {
final request = DeclarationOfRequest(
identifier, MessageType.typeDeclarationOfRequest, remoteInstance,
serializationZoneId: serializationZoneId);
return _handleResponse<TypeDeclaration>(await _sendRequest(request));
});
}
}
/// Client side implementation of a [StaticType].
base class ClientStaticTypeImpl extends ClientIntrospector
implements StaticType {
ClientStaticTypeImpl(super._sendRequest,
{required super.remoteInstance, required super.serializationZoneId});
factory ClientStaticTypeImpl.ofRemote({
required StaticTypeImpl instance,
required SendRequest sendRequest,
required int serializationZoneId,
}) {
RemoteInstanceImpl remoteInstance =
RemoteInstanceImpl(id: instance.id, kind: instance.kind);
return switch (instance.kind) {
RemoteInstanceKind.namedStaticType => ClientNamedStaticTypeImpl(
sendRequest,
staticType: instance as NamedStaticTypeImpl,
remoteInstance: remoteInstance,
serializationZoneId: serializationZoneId),
RemoteInstanceKind.staticType => ClientStaticTypeImpl(sendRequest,
remoteInstance: remoteInstance,
serializationZoneId: serializationZoneId),
_ => throw StateError(
'Expected either a StaticType or NamedStaticType but got '
'${instance.kind}'),
};
}
@override
Future<bool> isExactly(ClientStaticTypeImpl other) async {
IsExactlyTypeRequest request = IsExactlyTypeRequest(
remoteInstance, other.remoteInstance,
serializationZoneId: serializationZoneId);
return _handleResponse<BooleanValue>(await _sendRequest(request)).value;
}
@override
Future<bool> isSubtypeOf(ClientStaticTypeImpl other) async {
IsSubtypeOfRequest request = IsSubtypeOfRequest(
remoteInstance, other.remoteInstance,
serializationZoneId: serializationZoneId);
return _handleResponse<BooleanValue>(await _sendRequest(request)).value;
}
@override
Future<NamedStaticType?> asInstanceOf(TypeDeclaration declaration) async {
AsInstanceOfRequest request = AsInstanceOfRequest(
remoteInstance, declaration as TypeDeclarationImpl,
serializationZoneId: serializationZoneId);
return _handleResponse<NamedStaticType?>(await _sendRequest(request));
}
}
/// Named variant of the [ClientStaticTypeImpl].
final class ClientNamedStaticTypeImpl extends ClientStaticTypeImpl
implements NamedStaticType {
@override
final ParameterizedTypeDeclaration declaration;
@override
final List<StaticType> typeArguments;
ClientNamedStaticTypeImpl(
super.sendRequest, {
required NamedStaticTypeImpl staticType,
required super.serializationZoneId,
required super.remoteInstance,
}) : declaration = staticType.declaration,
typeArguments = staticType.typeArguments
.map((raw) => ClientStaticTypeImpl.ofRemote(
instance: raw,
sendRequest: sendRequest,
serializationZoneId: serializationZoneId))
.toList();
}
/// Client side implementation of a [DeclarationBuilder].
final class ClientDefinitionPhaseIntrospector
extends ClientDeclarationPhaseIntrospector
implements DefinitionPhaseIntrospector {
ClientDefinitionPhaseIntrospector(super._sendRequest,
{required super.remoteInstance, required super.serializationZoneId});
@override
Future<Declaration> declarationOf(IdentifierImpl identifier) async {
DeclarationOfRequest request = DeclarationOfRequest(
identifier, MessageType.declarationOfRequest, remoteInstance,
serializationZoneId: serializationZoneId);
return _handleResponse<Declaration>(await _sendRequest(request));
}
@override
Future<TypeAnnotation> inferType(
OmittedTypeAnnotationImpl omittedType) async {
InferTypeRequest request = InferTypeRequest(omittedType, remoteInstance,
serializationZoneId: serializationZoneId);
return _handleResponse<TypeAnnotation>(await _sendRequest(request));
}
@override
Future<List<Declaration>> topLevelDeclarationsOf(LibraryImpl library) async {
DeclarationsOfRequest request = DeclarationsOfRequest(
library, remoteInstance,
serializationZoneId: serializationZoneId);
return _handleResponse<DeclarationList>(await _sendRequest(request))
.declarations;
}
}
/// Either returns the actual response from [response], casted to [T], or throws
/// a [MacroException].
T _handleResponse<T>(Response response) {
if (response.responseType == MessageType.exception) {
throw response.exception!;
}
return response.response as T;
}
enum MessageType {
boolean,
constructorsOfRequest,
declarationOfRequest,
declarationList,
destroyRemoteInstanceZoneRequest,
disposeMacroRequest,
exception,
valuesOfRequest,
fieldsOfRequest,
methodsOfRequest,
executeDeclarationsPhaseRequest,
executeDefinitionsPhaseRequest,
executeTypesPhaseRequest,
instantiateMacroRequest,
resolveIdentifierRequest,
resolveTypeRequest,
inferTypeRequest,
isExactlyTypeRequest,
isSubtypeOfRequest,
asInstanceOfRequest,
loadMacroRequest,
remoteInstance,
macroInstanceIdentifier,
macroExecutionResult,
namedStaticType,
response,
staticType,
topLevelDeclarationsOfRequest,
typeDeclarationOfRequest,
typesOfRequest,
}
@@ -1,204 +0,0 @@
// 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 'serialization.dart';
import 'serialization_extensions.dart';
/// 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, if present.
static RemoteInstance? cached(int id) => _remoteInstanceCache[id];
/// Adds [instance] to the cache for this zone.
static void cache(RemoteInstance instance) =>
_remoteInstanceCache[instance.id] = instance;
/// Deserializes an instance based on the current [serializationMode].
///
// TODO: Ideally this would be `T extends RemoteInstance` but that interacts
// poorly with inference in other places, we end up with Never and null as
// inferred types due to only the impl versions of objects extending
// `RemoteInstance`.
static T deserialize<T extends Object>(Deserializer deserializer) =>
(deserializer..moveNext()).expectRemoteInstance();
/// This method should be overridden by any subclasses, they should instead
/// implement [serializeUncached].
@override
void serialize(Serializer serializer) {
serializer.addInt(id);
// We only send the ID if it's in the cache, it's only in our cache if it is
// also in the remote cache.
if (_remoteInstanceCache.containsKey(id)) return;
serializeUncached(serializer);
}
/// This method should be overridden by all subclasses, which should on their
/// first line call this super method.
///
/// This method should not be directly invoked, instead only [serialize]
/// should call it (if serializing an uncached value).
///
/// Only new fields added by the subtype should be serialized here, rely on
/// super classes to have their own implementations for their fields.
void serializeUncached(Serializer serializer) {
serializer.addInt(kind.index);
// Now we can add it to the cache, we know the other side has a copy of it
// and don't need to serialize it in the future.
_remoteInstanceCache[id] = this;
}
@override
bool operator ==(Object other) => other is RemoteInstance && id == other.id;
@override
int get hashCode => id;
}
/// A remote instance which is just a pointer to some server side instance of
/// a generic object.
///
/// The wrapped object is not serialized.
final class RemoteInstanceImpl extends RemoteInstance {
/// Always null on the client side, has an actual instance on the server side.
final Object? instance;
@override
final RemoteInstanceKind kind;
RemoteInstanceImpl({
required int id,
this.instance,
required this.kind,
}) : super(id);
}
// The kinds of instances.
enum RemoteInstanceKind {
classDeclaration,
constructorDeclaration,
constructorMetadataAnnotation,
declarationPhaseIntrospector,
definitionPhaseIntrospector,
enumDeclaration,
enumValueDeclaration,
extensionDeclaration,
extensionTypeDeclaration,
fieldDeclaration,
formalParameter,
formalParameterDeclaration,
functionDeclaration,
functionTypeAnnotation,
identifier,
identifierMetadataAnnotation,
library,
methodDeclaration,
mixinDeclaration,
namedStaticType,
namedTypeAnnotation,
omittedTypeAnnotation,
recordField,
recordTypeAnnotation,
staticType,
typeAliasDeclaration,
typeParameter,
typeParameterDeclaration,
typePhaseIntrospector,
variableDeclaration,
// Exceptions.
macroImplementationException,
macroIntrospectionCycleException,
unexpectedMacroException,
}
/// Creates a new zone with a remote instance cache and an id, which it uses to
/// avoid sending the same remote instances across the wire multiple times.
///
/// The lifecycle of one of these zones should be no longer than that of a
/// single full compile, at which point [destroyRemoteInstanceZone] should be
/// called.
///
/// In order to keep these caches in sync between the server and client, the
/// server always creates new zone IDs and passes those to the client.
int newRemoteInstanceZone<T>() {
final int id = _nextSerializationZoneId++;
final Zone zone = Zone.current.fork(zoneValues: {
_remoteInstanceZoneCacheKey: <int, RemoteInstance>{},
});
_remoteInstanceCacheZones[id] = zone;
return id;
}
/// Runs [fn] in the remote instance zone identified by [zoneId].
///
/// If [createIfMissing] is `true`, then a new zone will be created with
/// [zoneId] if one does not already exist (this should only be `true` in client
/// code).
T withRemoteInstanceZone<T>(int zoneId, T Function() fn,
{bool createIfMissing = false}) {
Zone? zone = _remoteInstanceCacheZones[zoneId];
if (zone == null) {
if (!createIfMissing) {
throw StateError('No remote instance zone with id `$zoneId` exists.');
}
zone = _remoteInstanceCacheZones[zoneId] = Zone.current.fork(zoneValues: {
_remoteInstanceZoneCacheKey: <int, RemoteInstance>{},
});
}
return zone.run(fn);
}
/// Removes the remote instance zone identified by [zoneId] from the known list
/// of zones and forcibly clears its cache.
///
/// Throws if a zone identified by [zoneId] does not exist.
void destroyRemoteInstanceZone(int zoneId) {
final Zone? zone = _remoteInstanceCacheZones.remove(zoneId);
if (zone == null) {
throw StateError('No remote instance zone with id `$zoneId` exists.');
}
(zone[_remoteInstanceZoneCacheKey] as Map<int, RemoteInstance>).clear();
}
/// The key used to store the remote instance cache in the current zone.
const Symbol _remoteInstanceZoneCacheKey = #_remoteInstanceCache;
/// We cache remote instances by their ID, which allows us to not repeatedly
/// send the same information over the wire.
///
/// These are a part of the current remote instance cache zone, which all
/// serialization and deserialization of remote instances must be done in.
Map<int, RemoteInstance> get _remoteInstanceCache =>
Zone.current[_remoteInstanceZoneCacheKey] as Map<int, RemoteInstance>? ??
(throw StateError('Not running in a remote instance cache zone, call '
'`withRemoteInstanceZone` to set one up.'));
/// Remote instance cache zones by ID.
final _remoteInstanceCacheZones = <int, Zone>{};
/// Incrementing identifier for the serialization zone ids.
int _nextSerializationZoneId = 0;
@@ -1,506 +0,0 @@
// 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 '../api.dart';
import '../executor.dart';
import 'exception_impls.dart';
import 'introspection_impls.dart';
import 'serialization.dart';
import 'serialization_extensions.dart';
/// Implementation of [MacroInstanceIdentifier].
class MacroInstanceIdentifierImpl implements MacroInstanceIdentifier {
/// Unique identifier for this instance, passed in from the server.
final int id;
/// A single int where each bit indicates whether a specific macro interface
/// is implemented by this macro.
final int _interfaces;
MacroInstanceIdentifierImpl._(this.id, this._interfaces);
factory MacroInstanceIdentifierImpl(Macro macro, int instanceId) {
// Build up the interfaces value, there is a bit for each declaration/phase
// combination (as there is an interface for each).
int interfaces = 0;
for (DeclarationKind declarationKind in DeclarationKind.values) {
for (Phase phase in Phase.values) {
int interfaceMask = _interfaceMask(declarationKind, phase);
switch (declarationKind) {
case DeclarationKind.classType:
switch (phase) {
case Phase.types:
if (macro is ClassTypesMacro) {
interfaces |= interfaceMask;
}
case Phase.declarations:
if (macro is ClassDeclarationsMacro) {
interfaces |= interfaceMask;
}
case Phase.definitions:
if (macro is ClassDefinitionMacro) {
interfaces |= interfaceMask;
}
}
case DeclarationKind.constructor:
switch (phase) {
case Phase.types:
if (macro is ConstructorTypesMacro) {
interfaces |= interfaceMask;
}
case Phase.declarations:
if (macro is ConstructorDeclarationsMacro) {
interfaces |= interfaceMask;
}
case Phase.definitions:
if (macro is ConstructorDefinitionMacro) {
interfaces |= interfaceMask;
}
}
case DeclarationKind.extension:
switch (phase) {
case Phase.types:
if (macro is ExtensionTypesMacro) {
interfaces |= interfaceMask;
}
case Phase.declarations:
if (macro is ExtensionDeclarationsMacro) {
interfaces |= interfaceMask;
}
case Phase.definitions:
if (macro is ExtensionDefinitionMacro) {
interfaces |= interfaceMask;
}
}
case DeclarationKind.extensionType:
switch (phase) {
case Phase.types:
if (macro is ExtensionTypeTypesMacro) {
interfaces |= interfaceMask;
}
case Phase.declarations:
if (macro is ExtensionTypeDeclarationsMacro) {
interfaces |= interfaceMask;
}
case Phase.definitions:
if (macro is ExtensionTypeDefinitionMacro) {
interfaces |= interfaceMask;
}
}
case DeclarationKind.field:
switch (phase) {
case Phase.types:
if (macro is FieldTypesMacro) {
interfaces |= interfaceMask;
}
case Phase.declarations:
if (macro is FieldDeclarationsMacro) {
interfaces |= interfaceMask;
}
case Phase.definitions:
if (macro is FieldDefinitionMacro) {
interfaces |= interfaceMask;
}
}
case DeclarationKind.function:
switch (phase) {
case Phase.types:
if (macro is FunctionTypesMacro) {
interfaces |= interfaceMask;
}
case Phase.declarations:
if (macro is FunctionDeclarationsMacro) {
interfaces |= interfaceMask;
}
case Phase.definitions:
if (macro is FunctionDefinitionMacro) {
interfaces |= interfaceMask;
}
}
case DeclarationKind.library:
switch (phase) {
case Phase.types:
if (macro is LibraryTypesMacro) {
interfaces |= interfaceMask;
}
case Phase.declarations:
if (macro is LibraryDeclarationsMacro) {
interfaces |= interfaceMask;
}
case Phase.definitions:
if (macro is LibraryDefinitionMacro) {
interfaces |= interfaceMask;
}
}
case DeclarationKind.method:
switch (phase) {
case Phase.types:
if (macro is MethodTypesMacro) {
interfaces |= interfaceMask;
}
case Phase.declarations:
if (macro is MethodDeclarationsMacro) {
interfaces |= interfaceMask;
}
case Phase.definitions:
if (macro is MethodDefinitionMacro) {
interfaces |= interfaceMask;
}
}
case DeclarationKind.enumType:
switch (phase) {
case Phase.types:
if (macro is EnumTypesMacro) {
interfaces |= interfaceMask;
}
case Phase.declarations:
if (macro is EnumDeclarationsMacro) {
interfaces |= interfaceMask;
}
case Phase.definitions:
if (macro is EnumDefinitionMacro) {
interfaces |= interfaceMask;
}
}
case DeclarationKind.enumValue:
switch (phase) {
case Phase.types:
if (macro is EnumValueTypesMacro) {
interfaces |= interfaceMask;
}
case Phase.declarations:
if (macro is EnumValueDeclarationsMacro) {
interfaces |= interfaceMask;
}
case Phase.definitions:
if (macro is EnumValueDefinitionMacro) {
interfaces |= interfaceMask;
}
}
case DeclarationKind.mixinType:
switch (phase) {
case Phase.types:
if (macro is MixinTypesMacro) {
interfaces |= interfaceMask;
}
case Phase.declarations:
if (macro is MixinDeclarationsMacro) {
interfaces |= interfaceMask;
}
case Phase.definitions:
if (macro is MixinDefinitionMacro) {
interfaces |= interfaceMask;
}
}
case DeclarationKind.typeAlias:
switch (phase) {
case Phase.types:
if (macro is TypeAliasTypesMacro) {
interfaces |= interfaceMask;
}
case Phase.declarations:
if (macro is TypeAliasDeclarationsMacro) {
interfaces |= interfaceMask;
}
case Phase.definitions:
// Does not have definitions.
break;
}
case DeclarationKind.variable:
switch (phase) {
case Phase.types:
if (macro is VariableTypesMacro) {
interfaces |= interfaceMask;
}
case Phase.declarations:
if (macro is VariableDeclarationsMacro) {
interfaces |= interfaceMask;
}
case Phase.definitions:
if (macro is VariableDefinitionMacro) {
interfaces |= interfaceMask;
}
}
}
}
}
return MacroInstanceIdentifierImpl._(instanceId, interfaces);
}
MacroInstanceIdentifierImpl.deserialize(Deserializer deserializer)
: id = (deserializer..moveNext()).expectInt(),
_interfaces = (deserializer..moveNext()).expectInt();
@override
void serialize(Serializer serializer) => serializer
..addInt(id)
..addInt(_interfaces);
@override
operator ==(other) => other is MacroInstanceIdentifierImpl && id == other.id;
@override
int get hashCode => id;
@override
bool shouldExecute(DeclarationKind declarationKind, Phase phase) {
int mask = _interfaceMask(declarationKind, phase);
if (declarationKind == DeclarationKind.method) {
// Apply function macros to methods.
mask |= _interfaceMask(DeclarationKind.function, phase);
} else if (declarationKind == DeclarationKind.field) {
// Apply variable macros to fields.
mask |= _interfaceMask(DeclarationKind.variable, phase);
}
return _interfaces & mask != 0x0;
}
@override
bool supportsDeclarationKind(DeclarationKind declarationKind) {
for (Phase phase in Phase.values) {
if (shouldExecute(declarationKind, phase)) {
return true;
}
}
return false;
}
/// The mask for a particular interface, which is a combination of a kind of
/// declaration and a phase.
static int _interfaceMask(DeclarationKind declarationKind, Phase phase) =>
0x1 << (declarationKind.index * Phase.values.length) << phase.index;
}
/// Implementation of [MacroExecutionResult].
class MacroExecutionResultImpl implements MacroExecutionResult {
@override
final List<Diagnostic> diagnostics;
@override
final MacroExceptionImpl? exception;
@override
final Map<IdentifierImpl, List<DeclarationCode>> enumValueAugmentations;
@override
final Map<IdentifierImpl, NamedTypeAnnotationCode> extendsTypeAugmentations;
@override
final Map<IdentifierImpl, List<TypeAnnotationCode>> interfaceAugmentations;
@override
final List<DeclarationCode> libraryAugmentations;
@override
final Map<IdentifierImpl, List<TypeAnnotationCode>> mixinAugmentations;
@override
final List<String> newTypeNames;
@override
final Map<IdentifierImpl, List<DeclarationCode>> typeAugmentations;
MacroExecutionResultImpl({
required this.diagnostics,
this.exception,
required this.enumValueAugmentations,
required this.extendsTypeAugmentations,
required this.interfaceAugmentations,
required this.libraryAugmentations,
required this.mixinAugmentations,
required this.newTypeNames,
required this.typeAugmentations,
});
factory MacroExecutionResultImpl.deserialize(Deserializer deserializer) {
deserializer
..moveNext()
..expectList();
List<Diagnostic> diagnostics = [
for (; deserializer.moveNext();) deserializer.expectDiagnostic(),
];
MacroExceptionImpl? exception = (deserializer..moveNext()).checkNull()
? null
: deserializer.expectRemoteInstance();
deserializer
..moveNext()
..expectList();
Map<IdentifierImpl, List<DeclarationCode>> enumValueAugmentations = {
for (; deserializer.moveNext();)
deserializer.expectRemoteInstance(): [
for (bool hasNextCode = (deserializer
..moveNext()
..expectList())
.moveNext();
hasNextCode;
hasNextCode = deserializer.moveNext())
deserializer.expectCode(),
]
};
deserializer
..moveNext()
..expectList();
Map<IdentifierImpl, NamedTypeAnnotationCode> extendsTypeAugmentations = {
for (; deserializer.moveNext();)
deserializer.expectRemoteInstance():
(deserializer..moveNext()).expectCode(),
};
deserializer
..moveNext()
..expectList();
Map<IdentifierImpl, List<TypeAnnotationCode>> interfaceAugmentations = {
for (; deserializer.moveNext();)
deserializer.expectRemoteInstance(): [
for (bool hasNextCode = (deserializer
..moveNext()
..expectList())
.moveNext();
hasNextCode;
hasNextCode = deserializer.moveNext())
deserializer.expectCode(),
]
};
deserializer
..moveNext()
..expectList();
List<DeclarationCode> libraryAugmentations = [
for (; deserializer.moveNext();) deserializer.expectCode()
];
deserializer
..moveNext()
..expectList();
Map<IdentifierImpl, List<TypeAnnotationCode>> mixinAugmentations = {
for (; deserializer.moveNext();)
deserializer.expectRemoteInstance(): [
for (bool hasNextCode = (deserializer
..moveNext()
..expectList())
.moveNext();
hasNextCode;
hasNextCode = deserializer.moveNext())
deserializer.expectCode(),
]
};
deserializer
..moveNext()
..expectList();
List<String> newTypeNames = [
for (; deserializer.moveNext();) deserializer.expectString()
];
deserializer
..moveNext()
..expectList();
Map<IdentifierImpl, List<DeclarationCode>> typeAugmentations = {
for (; deserializer.moveNext();)
deserializer.expectRemoteInstance(): [
for (bool hasNextCode = (deserializer
..moveNext()
..expectList())
.moveNext();
hasNextCode;
hasNextCode = deserializer.moveNext())
deserializer.expectCode(),
]
};
return MacroExecutionResultImpl(
diagnostics: diagnostics,
exception: exception,
enumValueAugmentations: enumValueAugmentations,
extendsTypeAugmentations: extendsTypeAugmentations,
interfaceAugmentations: interfaceAugmentations,
libraryAugmentations: libraryAugmentations,
mixinAugmentations: mixinAugmentations,
newTypeNames: newTypeNames,
typeAugmentations: typeAugmentations,
);
}
@override
void serialize(Serializer serializer) {
serializer.startList();
for (Diagnostic diagnostic in diagnostics) {
diagnostic.serialize(serializer);
}
serializer.endList();
if (exception == null) {
serializer.addNull();
} else {
exception!.serialize(serializer);
}
serializer.startList();
for (IdentifierImpl enuum in enumValueAugmentations.keys) {
enuum.serialize(serializer);
serializer.startList();
for (DeclarationCode augmentation in enumValueAugmentations[enuum]!) {
augmentation.serialize(serializer);
}
serializer.endList();
}
serializer.endList();
serializer.startList();
for (IdentifierImpl type in extendsTypeAugmentations.keys) {
type.serialize(serializer);
extendsTypeAugmentations[type]!.serialize(serializer);
}
serializer.endList();
serializer.startList();
for (IdentifierImpl type in interfaceAugmentations.keys) {
type.serialize(serializer);
serializer.startList();
for (TypeAnnotationCode interface in interfaceAugmentations[type]!) {
interface.serialize(serializer);
}
serializer.endList();
}
serializer.endList();
serializer.startList();
for (DeclarationCode augmentation in libraryAugmentations) {
augmentation.serialize(serializer);
}
serializer.endList();
serializer.startList();
for (IdentifierImpl type in mixinAugmentations.keys) {
type.serialize(serializer);
serializer.startList();
for (TypeAnnotationCode mixin in mixinAugmentations[type]!) {
mixin.serialize(serializer);
}
serializer.endList();
}
serializer.endList();
serializer.startList();
for (String name in newTypeNames) {
serializer.addString(name);
}
serializer.endList();
serializer.startList();
for (IdentifierImpl type in typeAugmentations.keys) {
type.serialize(serializer);
serializer.startList();
for (DeclarationCode augmentation in typeAugmentations[type]!) {
augmentation.serialize(serializer);
}
serializer.endList();
}
serializer.endList();
}
}
@@ -1,742 +0,0 @@
// 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 'dart:async';
import 'dart:typed_data';
/// All serialization must be done in a serialization Zone, as well as a remote
/// instance cache zone. This outer serialization zone informs the code of which
/// protocol we are using for serialization.
T withSerializationMode<T>(
SerializationMode mode,
T Function() fn, {
Serializer Function()? serializerFactory,
Deserializer Function(Object? data)? deserializerFactory,
}) =>
runZoned(fn, zoneValues: {
#serializationMode: mode,
});
/// Serializable interface
abstract class Serializable {
/// Serializes this object using [serializer].
void serialize(Serializer serializer);
}
/// A push based object serialization interface.
abstract class Serializer {
/// Serializes a [String].
void addString(String value);
/// Serializes a nullable [String].
void addNullableString(String? value) =>
value == null ? addNull() : addString(value);
/// Serializes a [double].
void addDouble(double value);
/// Serializes a nullable [double].
void addNullableDouble(double? value) =>
value == null ? addNull() : addDouble(value);
/// Serializes an [int].
void addInt(int value);
/// Serializes a nullable [int].
void addNullableInt(int? value) => value == null ? addNull() : addInt(value);
/// Serializes a [bool].
void addBool(bool value);
/// Serializes a nullable [bool].
void addNullableBool(bool? value) =>
value == null ? addNull() : addBool(value);
/// Serializes a `null` literal.
void addNull();
/// Used to signal the start of an arbitrary length list of items.
void startList();
/// Used to signal the end of an arbitrary length list of items.
void endList();
/// Returns the resulting serialized object.
Object get result;
}
/// A pull based object deserialization interface.
///
/// You must call [moveNext] before reading any items, and in order to advance
/// to the next item.
abstract class Deserializer {
/// Checks if the current value is a null, returns `true` if so and `false`
/// otherwise.
bool checkNull();
/// Reads the current value as a non-nullable [String].
bool expectBool();
/// Reads the current value as a nullable [bool].
bool? expectNullableBool() => checkNull() ? null : expectBool();
/// Reads the current value as a non-nullable [double].
double expectDouble();
/// Reads the current value as a nullable [double].
double? expectNullableDouble() => checkNull() ? null : expectDouble();
/// Reads the current value as a non-nullable [int].
int expectInt();
/// Reads the current value as a nullable [int].
int? expectNullableInt() => checkNull() ? null : expectInt();
/// Reads the current value as a non-nullable [String].
String expectString();
/// Reads the current value as a nullable [String].
String? expectNullableString() => checkNull() ? null : expectString();
/// Asserts that the current item is the start of a list.
///
/// An example for how to read from a list is as follows:
///
/// var json = JsonReader.fromString(source);
/// I know it's a list of strings.
///
/// ```
/// var result = <String>[];
/// deserializer.moveNext();
/// deserializer.expectList();
/// while (json.moveNext()) {
/// result.add(json.expectString());
/// }
/// // Can now read later items, but need to call `moveNext` again to move
/// // past the list.
/// deserializer.moveNext();
/// deserializer.expectBool();
/// ```
void expectList();
/// Moves to the next item, returns `false` if there are no more items to
/// read.
///
/// If inside of a list, this returns `false` when the end of the list is
/// reached, and moves back to the parent, but does not advance it, so another
/// call to `moveNext` is needed. See example in the [expectList] docs.
bool moveNext();
}
class JsonSerializer implements Serializer {
/// The full result.
final _result = <Object?>[];
/// A path to the current list we are modifying.
late final List<List<Object?>> _path = [_result];
/// Returns the result as an unmodifiable [Iterable].
///
/// Asserts that all [List] entries have not been closed with [endList].
@override
Iterable<Object?> get result {
assert(_path.length == 1);
return _result;
}
@override
void addBool(bool value) => _path.last.add(value);
@override
void addNullableBool(bool? value) => _path.last.add(value);
@override
void addDouble(double value) => _path.last.add(value);
@override
void addNullableDouble(double? value) => _path.last.add(value);
@override
void addInt(int value) => _path.last.add(value);
@override
void addNullableInt(int? value) => _path.last.add(value);
@override
void addString(String value) => _path.last.add(value);
@override
void addNullableString(String? value) => _path.last.add(value);
@override
void addNull() => _path.last.add(null);
@override
void startList() {
List<Object?> sublist = [];
_path.last.add(sublist);
_path.add(sublist);
}
@override
void endList() {
_path.removeLast();
}
}
class JsonDeserializer implements Deserializer {
/// The root source list to read from.
final Iterable<Object?> _source;
/// The path to the current iterator we are reading from.
late final List<Iterator<Object?>> _path = [];
/// Whether we have received our first [moveNext] call.
bool _initialized = false;
/// Initialize this deserializer from `_source`.
JsonDeserializer(this._source);
@override
bool checkNull() => _expectValue<Object?>() == null;
@override
void expectList() => _path.add(_expectValue<Iterable<Object?>>().iterator);
@override
bool expectBool() => _expectValue();
@override
bool? expectNullableBool() => _expectValue();
@override
double expectDouble() => _expectValue();
@override
double? expectNullableDouble() => _expectValue();
@override
int expectInt() => _expectValue();
@override
int? expectNullableInt() => _expectValue();
@override
String expectString() => _expectValue();
@override
String? expectNullableString() => _expectValue();
/// Reads the current value and casts it to [T].
T _expectValue<T>() {
if (!_initialized) {
throw StateError('You must call `moveNext()` before reading any values.');
}
Object? current = _path.last.current;
if (current is! T) {
throw StateError('Expected $T, got: ${_path.last.current}');
}
return current;
}
@override
bool moveNext() {
if (!_initialized) {
_path.add(_source.iterator);
_initialized = true;
}
// Move the current iterable, if it's at the end of its items remove it from
// the current path and return false.
if (!_path.last.moveNext()) {
_path.removeLast();
return false;
}
return true;
}
}
class ByteDataSerializer extends Serializer {
final BytesBuilder _builder = BytesBuilder();
// Re-usable 8 byte list and view for encoding doubles.
final Uint8List _eightByteList = Uint8List(8);
late final ByteData _eightByteListData = ByteData.sublistView(_eightByteList);
@override
void addBool(bool value) => _builder
.addByte(value ? DataKind.boolTrue.index : DataKind.boolFalse.index);
@override
void addDouble(double value) {
_eightByteListData.setFloat64(0, value);
_builder
..addByte(DataKind.float64.index)
..add(_eightByteList);
}
@override
void addNull() => _builder.addByte(DataKind.nil.index);
@override
void addInt(int value) {
if (value >= 0x0) {
assert(DataKind.values.length < 0xff);
if (value <= 0xff - DataKind.values.length) {
_builder.addByte(value + DataKind.values.length);
} else if (value <= 0xff) {
_builder
..addByte(DataKind.uint8.index)
..addByte(value);
} else if (value <= 0xffff) {
_builder
..addByte(DataKind.uint16.index)
..addByte(value >> 8)
..addByte(value);
} else if (value <= 0xffffffff) {
_builder
..addByte(DataKind.uint32.index)
..addByte(value >> 24)
..addByte(value >> 16)
..addByte(value >> 8)
..addByte(value);
} else {
_builder
..addByte(DataKind.uint64.index)
..addByte(value >> 56)
..addByte(value >> 48)
..addByte(value >> 40)
..addByte(value >> 32)
..addByte(value >> 24)
..addByte(value >> 16)
..addByte(value >> 8)
..addByte(value);
}
} else {
if (value >= -0x80) {
_builder
..addByte(DataKind.int8.index)
..addByte(value);
} else if (value >= -0x8000) {
_builder
..addByte(DataKind.int16.index)
..addByte(value >> 8)
..addByte(value);
} else if (value >= -0x8000000) {
_builder
..addByte(DataKind.int32.index)
..addByte(value >> 24)
..addByte(value >> 16)
..addByte(value >> 8)
..addByte(value);
} else {
_builder
..addByte(DataKind.int64.index)
..addByte(value >> 56)
..addByte(value >> 48)
..addByte(value >> 40)
..addByte(value >> 32)
..addByte(value >> 24)
..addByte(value >> 16)
..addByte(value >> 8)
..addByte(value);
}
}
}
@override
void addString(String value) {
for (int i = 0; i < value.length; i++) {
if (value.codeUnitAt(i) > 0xff) {
_addTwoByteString(value);
return;
}
}
_addOneByteString(value);
}
void _addOneByteString(String value) {
_builder.addByte(DataKind.oneByteString.index);
addInt(value.length);
for (int i = 0; i < value.length; i++) {
_builder.addByte(value.codeUnitAt(i));
}
}
void _addTwoByteString(String value) {
_builder.addByte(DataKind.twoByteString.index);
addInt(value.length);
for (int i = 0; i < value.length; i++) {
int codeUnit = value.codeUnitAt(i);
switch (Endian.host) {
case Endian.little:
_builder
..addByte(codeUnit)
..addByte(codeUnit >> 8);
break;
case Endian.big:
_builder
..addByte(codeUnit >> 8)
..addByte(codeUnit);
break;
}
}
}
@override
void startList() => _builder.addByte(DataKind.startList.index);
@override
void endList() => _builder.addByte(DataKind.endList.index);
/// Used to signal the start of an arbitrary length list of map entries.
void startMap() => _builder.addByte(DataKind.startMap.index);
/// Used to signal the end of an arbitrary length list of map entries.
void endMap() => _builder.addByte(DataKind.endMap.index);
/// Serializes a [Uint8List].
void addUint8List(Uint8List value) {
_builder.addByte(DataKind.uint8List.index);
addInt(value.length);
_builder.add(value);
}
/// Serializes an object with arbitrary structure. It supports `bool`,
/// `int`, `String`, `null`, `Uint8List`, `List`, `Map`.
void addAny(Object? value) {
if (value == null) {
addNull();
} else if (value is bool) {
addBool(value);
} else if (value is int) {
addInt(value);
} else if (value is String) {
addString(value);
} else if (value is Uint8List) {
addUint8List(value);
} else if (value is List) {
startList();
value.forEach(addAny);
endList();
} else if (value is Map) {
startMap();
for (MapEntry<Object?, Object?> entry in value.entries) {
addAny(entry.key);
addAny(entry.value);
}
endMap();
} else {
throw ArgumentError('(${value.runtimeType}) $value');
}
}
@override
Uint8List get result => _builder.takeBytes();
}
class ByteDataDeserializer extends Deserializer {
final ByteData _bytes;
int _byteOffset = 0;
int? _byteOffsetIncrement = 0;
ByteDataDeserializer(this._bytes);
/// Reads the next [DataKind] and advances [_byteOffset].
DataKind _readKind([int offset = 0]) {
int value = _bytes.getUint8(_byteOffset + offset);
if (value < DataKind.values.length) {
return DataKind.values[value];
} else {
return DataKind.directEncodedUint8;
}
}
@override
bool checkNull() {
_byteOffsetIncrement = 1;
return _readKind() == DataKind.nil;
}
@override
bool expectBool() {
DataKind kind = _readKind();
_byteOffsetIncrement = 1;
if (kind == DataKind.boolTrue) {
return true;
} else if (kind == DataKind.boolFalse) {
return false;
} else {
throw StateError('Expected a bool but found a $kind');
}
}
@override
double expectDouble() {
DataKind kind = _readKind();
if (kind != DataKind.float64) {
throw StateError('Expected a double but found a $kind');
}
_byteOffsetIncrement = 9;
return _bytes.getFloat64(_byteOffset + 1);
}
@override
int expectInt() => _expectInt(0);
int _expectInt(int offset) {
DataKind kind = _readKind(offset);
if (kind == DataKind.directEncodedUint8) {
_byteOffsetIncrement = offset + 1;
return _bytes.getUint8(_byteOffset + offset) - DataKind.values.length;
}
offset += 1;
int result;
switch (kind) {
case DataKind.int8:
result = _bytes.getInt8(_byteOffset + offset);
_byteOffsetIncrement = 1 + offset;
break;
case DataKind.int16:
result = _bytes.getInt16(_byteOffset + offset);
_byteOffsetIncrement = 2 + offset;
break;
case DataKind.int32:
result = _bytes.getInt32(_byteOffset + offset);
_byteOffsetIncrement = 4 + offset;
break;
case DataKind.int64:
result = _bytes.getInt64(_byteOffset + offset);
_byteOffsetIncrement = 8 + offset;
break;
case DataKind.uint8:
result = _bytes.getUint8(_byteOffset + offset);
_byteOffsetIncrement = 1 + offset;
break;
case DataKind.uint16:
result = _bytes.getUint16(_byteOffset + offset);
_byteOffsetIncrement = 2 + offset;
break;
case DataKind.uint32:
result = _bytes.getUint32(_byteOffset + offset);
_byteOffsetIncrement = 4 + offset;
break;
case DataKind.uint64:
result = _bytes.getUint64(_byteOffset + offset);
_byteOffsetIncrement = 8 + offset;
break;
default:
throw StateError('Expected an int but found a $kind');
}
return result;
}
@override
void expectList() {
DataKind kind = _readKind();
if (kind != DataKind.startList) {
throw StateError('Expected the start to a list but found a $kind');
}
_byteOffsetIncrement = 1;
}
/// Asserts that the current item is the start of a map.
///
/// An example for how to read from a map is as follows:
///
/// I know it's a map of ints to strings.
///
/// ```
/// var result = <int, String>[];
/// deserializer.expectMap();
/// while (deserializer.moveNext()) {
/// var key = deserializer.expectInt();
/// deserializer.next();
/// var value = deserializer.expectString();
/// result[key] = value;
/// }
/// // We have already called `moveNext` to move past the map.
/// deserializer.expectBool();
/// ```
void expectMap() {
DataKind kind = _readKind();
if (kind != DataKind.startMap) {
throw StateError('Expected the start to a map but found a $kind');
}
_byteOffsetIncrement = 1;
}
@override
String expectString() {
DataKind kind = _readKind();
int length = _expectInt(1);
int offset = _byteOffsetIncrement! + _byteOffset;
if (kind == DataKind.oneByteString) {
_byteOffsetIncrement = _byteOffsetIncrement! + length;
return String.fromCharCodes(_bytes.buffer.asUint8List(offset, length));
} else if (kind == DataKind.twoByteString) {
length = length * 2;
_byteOffsetIncrement = _byteOffsetIncrement! + length;
Uint8List bytes =
Uint8List.fromList(_bytes.buffer.asUint8List(offset, length));
return String.fromCharCodes(bytes.buffer.asUint16List());
} else {
throw StateError('Expected a string but found a $kind');
}
}
/// Reads the current value as [Uint8List].
Uint8List expectUint8List() {
_byteOffsetIncrement = 1;
moveNext();
int length = expectInt();
int offset = _byteOffset + _byteOffsetIncrement!;
_byteOffsetIncrement = _byteOffsetIncrement! + length;
return _bytes.buffer.asUint8List(offset, length);
}
/// Reads the current value as an object of arbitrary structure.
Object? expectAny() {
const Set<DataKind> boolKinds = {
DataKind.boolFalse,
DataKind.boolTrue,
};
const Set<DataKind> intKinds = {
DataKind.directEncodedUint8,
DataKind.int8,
DataKind.int16,
DataKind.int32,
DataKind.int64,
DataKind.uint8,
DataKind.uint16,
DataKind.uint32,
DataKind.uint64,
};
const Set<DataKind> stringKinds = {
DataKind.oneByteString,
DataKind.twoByteString,
};
DataKind kind = _readKind();
if (boolKinds.contains(kind)) {
return expectBool();
} else if (kind == DataKind.nil) {
checkNull();
return null;
} else if (intKinds.contains(kind)) {
return expectInt();
} else if (stringKinds.contains(kind)) {
return expectString();
} else if (kind == DataKind.startList) {
List<Object?> result = [];
expectList();
while (moveNext()) {
Object? element = expectAny();
result.add(element);
}
return result;
} else if (kind == DataKind.startMap) {
Map<Object?, Object?> result = {};
expectMap();
while (moveNext()) {
Object? key = expectAny();
moveNext();
Object? value = expectAny();
result[key] = value;
}
return result;
} else if (kind == DataKind.uint8List) {
return expectUint8List();
} else {
throw StateError('Expected: $kind');
}
}
@override
bool moveNext() {
int? increment = _byteOffsetIncrement;
_byteOffsetIncrement = null;
if (increment == null) {
throw StateError("Can't move until consuming the current element");
}
_byteOffset += increment;
if (_byteOffset >= _bytes.lengthInBytes) {
return false;
} else if (_readKind() == DataKind.endList ||
_readKind() == DataKind.endMap) {
// You don't explicitly consume list/map end markers.
_byteOffsetIncrement = 1;
return false;
} else {
return true;
}
}
}
enum DataKind {
nil,
boolTrue,
boolFalse,
directEncodedUint8, // Encoded in the kind byte.
startList,
endList,
startMap,
endMap,
int8,
int16,
int32,
int64,
uint8,
uint16,
uint32,
uint64,
float64,
oneByteString,
twoByteString,
uint8List,
}
/// Must be set using `withSerializationMode` before doing any serialization or
/// deserialization.
SerializationMode get serializationMode {
SerializationMode? mode =
Zone.current[#serializationMode] as SerializationMode?;
if (mode == null) {
throw StateError('No SerializationMode set, you must do all '
'serialization inside a call to `withSerializationMode`.');
}
return mode;
}
/// Returns the current deserializer factory for the zone.
Deserializer Function(Object?) get deserializerFactory =>
switch (serializationMode) {
SerializationMode.byteData => (Object? message) =>
ByteDataDeserializer(ByteData.sublistView(message as Uint8List)),
SerializationMode.json => (Object? message) =>
JsonDeserializer(message as Iterable<Object?>),
};
/// Returns the current serializer factory for the zone.
Serializer Function() get serializerFactory => switch (serializationMode) {
SerializationMode.byteData => ByteDataSerializer.new,
SerializationMode.json => JsonSerializer.new,
};
/// Some objects are serialized differently on the client side versus the server
/// side. This indicates the different modes, as well as the format used.
enum SerializationMode {
byteData,
json;
factory SerializationMode.fromOption(String option) => switch (option) {
'json' => SerializationMode.json,
'bytedata' => SerializationMode.byteData,
_ => throw ArgumentError('Unrecognized macro serialization mode '
'$option'),
};
}
extension SerializationModeHelpers on SerializationMode {
/// A stable string to write in code.
String get asCode => switch (this) {
SerializationMode.byteData => 'SerializationMode.byteData',
SerializationMode.json => 'SerializationMode.json',
};
}
@@ -1,672 +0,0 @@
// Copyright (c) 2024, 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 '../api.dart';
import 'exception_impls.dart';
import 'introspection_impls.dart';
import 'remote_instance.dart';
import 'serialization.dart';
extension DeserializerExtensions on Deserializer {
T expectRemoteInstance<T extends Object>() {
int id = expectInt();
// If cached, just return the instance. Only the ID should be sent.
RemoteInstance? cached = RemoteInstance.cached(id);
if (cached != null) {
return cached as T;
}
moveNext();
RemoteInstanceKind kind = RemoteInstanceKind.values[expectInt()];
final RemoteInstance instance = switch (kind) {
RemoteInstanceKind.declarationPhaseIntrospector ||
RemoteInstanceKind.definitionPhaseIntrospector ||
RemoteInstanceKind.typePhaseIntrospector =>
// These are simple wrappers, just pass in the kind
RemoteInstanceImpl(id: id, kind: kind),
RemoteInstanceKind.classDeclaration =>
(this..moveNext())._expectClassDeclaration(id),
RemoteInstanceKind.constructorMetadataAnnotation =>
(this..moveNext())._expectConstructorMetadataAnnotation(id),
RemoteInstanceKind.enumDeclaration =>
(this..moveNext())._expectEnumDeclaration(id),
RemoteInstanceKind.enumValueDeclaration =>
(this..moveNext())._expectEnumValueDeclaration(id),
RemoteInstanceKind.extensionDeclaration =>
(this..moveNext())._expectExtensionDeclaration(id),
RemoteInstanceKind.extensionTypeDeclaration =>
(this..moveNext())._expectExtensionTypeDeclaration(id),
RemoteInstanceKind.mixinDeclaration =>
(this..moveNext())._expectMixinDeclaration(id),
RemoteInstanceKind.constructorDeclaration =>
(this..moveNext())._expectConstructorDeclaration(id),
RemoteInstanceKind.fieldDeclaration =>
(this..moveNext())._expectFieldDeclaration(id),
RemoteInstanceKind.functionDeclaration =>
(this..moveNext())._expectFunctionDeclaration(id),
RemoteInstanceKind.functionTypeAnnotation =>
(this..moveNext())._expectFunctionTypeAnnotation(id),
RemoteInstanceKind.formalParameter =>
(this..moveNext())._expectFormalParameter(id),
RemoteInstanceKind.identifier => (this..moveNext())._expectIdentifier(id),
RemoteInstanceKind.identifierMetadataAnnotation =>
(this..moveNext())._expectIdentifierMetadataAnnotation(id),
RemoteInstanceKind.library => (this..moveNext())._expectLibrary(id),
RemoteInstanceKind.methodDeclaration =>
(this..moveNext())._expectMethodDeclaration(id),
RemoteInstanceKind.namedStaticType =>
(this..moveNext())._expectNamedStaticType(id),
RemoteInstanceKind.namedTypeAnnotation =>
(this..moveNext())._expectNamedTypeAnnotation(id),
RemoteInstanceKind.omittedTypeAnnotation =>
(this..moveNext())._expectOmittedTypeAnnotation(id),
RemoteInstanceKind.formalParameterDeclaration =>
(this..moveNext())._expectFormalParameterDeclaration(id),
RemoteInstanceKind.recordField =>
(this..moveNext())._expectRecordField(id),
RemoteInstanceKind.recordTypeAnnotation =>
(this..moveNext())._expectRecordTypeAnnotation(id),
RemoteInstanceKind.staticType => StaticTypeImpl(id),
RemoteInstanceKind.typeAliasDeclaration =>
(this..moveNext())._expectTypeAliasDeclaration(id),
RemoteInstanceKind.typeParameter =>
(this..moveNext())._expectTypeParameter(id),
RemoteInstanceKind.typeParameterDeclaration =>
(this..moveNext())._expectTypeParameterDeclaration(id),
RemoteInstanceKind.variableDeclaration =>
(this..moveNext())._expectVariableDeclaration(id),
// Exceptions.
RemoteInstanceKind.macroImplementationException ||
RemoteInstanceKind.macroIntrospectionCycleException ||
RemoteInstanceKind.unexpectedMacroException =>
(this..moveNext())._expectException(kind, id),
};
RemoteInstance.cache(instance);
return instance as T;
}
Uri expectUri() => Uri.parse(expectString());
/// Reads a list of [RemoteInstance]s.
List<T> _expectRemoteInstanceList<T extends RemoteInstance>() {
expectList();
return [
for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
expectRemoteInstance(),
];
}
/// Reads a list of [Code]s.
List<T> _expectCodeList<T extends Code>() {
expectList();
return [
for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
expectCode(),
];
}
/// Reads a `Map<String, T extends Code>`.
Map<String, T> _expectStringCodeMap<T extends Code>() {
expectList();
return {
for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
expectString(): (this..moveNext()).expectCode(),
};
}
NamedStaticTypeImpl _expectNamedStaticType(int id) {
return NamedStaticTypeImpl(
id,
declaration: expectRemoteInstance(),
typeArguments: (this..moveNext())._expectRemoteInstanceList(),
);
}
NamedTypeAnnotationImpl _expectNamedTypeAnnotation(int id) =>
NamedTypeAnnotationImpl(
id: id,
isNullable: expectBool(),
identifier: RemoteInstance.deserialize(this),
typeArguments: (this..moveNext())._expectRemoteInstanceList(),
);
OmittedTypeAnnotationImpl _expectOmittedTypeAnnotation(int id) {
expectBool(); // Always `false`.
return OmittedTypeAnnotationImpl(
id: id,
);
}
FunctionTypeAnnotationImpl _expectFunctionTypeAnnotation(int id) =>
FunctionTypeAnnotationImpl(
id: id,
isNullable: expectBool(),
returnType: RemoteInstance.deserialize(this),
positionalParameters: (this..moveNext())._expectRemoteInstanceList(),
namedParameters: (this..moveNext())._expectRemoteInstanceList(),
typeParameters: (this..moveNext())._expectRemoteInstanceList(),
);
FormalParameterImpl _expectFormalParameter(int id) =>
FormalParameterImpl.fromBitMask(
id: id,
bitMask: BitMask(expectInt()),
metadata: (this..moveNext())._expectRemoteInstanceList(),
name: (this..moveNext()).expectNullableString(),
type: RemoteInstance.deserialize(this),
);
IdentifierImpl _expectIdentifier(int id) => IdentifierImpl(
id: id,
name: expectString(),
);
FormalParameterDeclarationImpl _expectFormalParameterDeclaration(int id) =>
FormalParameterDeclarationImpl.fromBitMask(
id: id,
identifier: expectRemoteInstance(),
library: RemoteInstance.deserialize(this),
metadata: (this..moveNext())._expectRemoteInstanceList(),
bitMask: BitMask((this..moveNext()).expectInt()),
style: ParameterStyle.values[(this..moveNext()).expectInt()],
type: RemoteInstance.deserialize(this),
);
RecordFieldImpl _expectRecordField(int id) => RecordFieldImpl(
id: id,
name: expectNullableString(),
type: (this..moveNext()).expectRemoteInstance());
RecordTypeAnnotationImpl _expectRecordTypeAnnotation(int id) =>
RecordTypeAnnotationImpl(
id: id,
isNullable: expectBool(),
namedFields: (this..moveNext())._expectRemoteInstanceList(),
positionalFields: (this..moveNext())._expectRemoteInstanceList(),
);
TypeParameterImpl _expectTypeParameter(int id) => TypeParameterImpl(
id: id,
bound: checkNull() ? null : expectRemoteInstance(),
metadata: (this..moveNext())._expectRemoteInstanceList(),
name: (this..moveNext()).expectString(),
);
TypeParameterDeclarationImpl _expectTypeParameterDeclaration(int id) =>
TypeParameterDeclarationImpl(
id: id,
identifier: expectRemoteInstance(),
library: RemoteInstance.deserialize(this),
metadata: (this..moveNext())._expectRemoteInstanceList(),
bound: (this..moveNext()).checkNull() ? null : expectRemoteInstance(),
);
FunctionDeclarationImpl _expectFunctionDeclaration(int id) =>
FunctionDeclarationImpl.fromBitMask(
id: id,
identifier: expectRemoteInstance(),
library: RemoteInstance.deserialize(this),
metadata: (this..moveNext())._expectRemoteInstanceList(),
bitMask: BitMask((this..moveNext()).expectInt()),
namedParameters: (this..moveNext())._expectRemoteInstanceList(),
positionalParameters: (this..moveNext())._expectRemoteInstanceList(),
returnType: RemoteInstance.deserialize(this),
typeParameters: (this..moveNext())._expectRemoteInstanceList(),
);
MethodDeclarationImpl _expectMethodDeclaration(int id) =>
MethodDeclarationImpl.fromBitMask(
id: id,
identifier: expectRemoteInstance(),
library: RemoteInstance.deserialize(this),
metadata: (this..moveNext())._expectRemoteInstanceList(),
bitMask: BitMask((this..moveNext()).expectInt()),
namedParameters: (this..moveNext())._expectRemoteInstanceList(),
positionalParameters: (this..moveNext())._expectRemoteInstanceList(),
returnType: RemoteInstance.deserialize(this),
typeParameters: (this..moveNext())._expectRemoteInstanceList(),
definingType: RemoteInstance.deserialize(this),
);
ConstructorDeclarationImpl _expectConstructorDeclaration(int id) =>
ConstructorDeclarationImpl.fromBitMask(
id: id,
identifier: expectRemoteInstance(),
library: RemoteInstance.deserialize(this),
metadata: (this..moveNext())._expectRemoteInstanceList(),
bitMask: BitMask((this..moveNext()).expectInt()),
namedParameters: (this..moveNext())._expectRemoteInstanceList(),
positionalParameters: (this..moveNext())._expectRemoteInstanceList(),
returnType: RemoteInstance.deserialize(this),
typeParameters: (this..moveNext())._expectRemoteInstanceList(),
definingType: RemoteInstance.deserialize(this),
);
VariableDeclarationImpl _expectVariableDeclaration(int id) =>
VariableDeclarationImpl.fromBitMask(
id: id,
identifier: expectRemoteInstance(),
library: RemoteInstance.deserialize(this),
metadata: (this..moveNext())._expectRemoteInstanceList(),
bitMask: BitMask((this..moveNext()).expectInt()),
type: RemoteInstance.deserialize(this),
);
FieldDeclarationImpl _expectFieldDeclaration(int id) =>
FieldDeclarationImpl.fromBitMask(
id: id,
// Declaration fields.
identifier: expectRemoteInstance(),
library: RemoteInstance.deserialize(this),
metadata: (this..moveNext())._expectRemoteInstanceList(),
bitMask: BitMask((this..moveNext()).expectInt()),
type: RemoteInstance.deserialize(this),
// FieldDeclaration fields
definingType: RemoteInstance.deserialize(this),
);
ClassDeclarationImpl _expectClassDeclaration(int id) =>
ClassDeclarationImpl.fromBitMask(
id: id,
identifier: expectRemoteInstance(),
library: RemoteInstance.deserialize(this),
metadata: (this..moveNext())._expectRemoteInstanceList(),
typeParameters: (this..moveNext())._expectRemoteInstanceList(),
bitMask: BitMask((this..moveNext()).expectInt()),
interfaces: (this..moveNext())._expectRemoteInstanceList(),
mixins: (this..moveNext())._expectRemoteInstanceList(),
superclass:
(this..moveNext()).checkNull() ? null : expectRemoteInstance(),
);
ConstructorMetadataAnnotationImpl _expectConstructorMetadataAnnotation(
int id) =>
ConstructorMetadataAnnotationImpl(
id: id,
constructor: expectRemoteInstance(),
type: RemoteInstance.deserialize(this),
positionalArguments: (this..moveNext())._expectCodeList(),
namedArguments: (this..moveNext())._expectStringCodeMap());
IdentifierMetadataAnnotationImpl _expectIdentifierMetadataAnnotation(
int id) =>
IdentifierMetadataAnnotationImpl(
id: id,
identifier: expectRemoteInstance(),
);
EnumDeclarationImpl _expectEnumDeclaration(int id) => EnumDeclarationImpl(
id: id,
identifier: expectRemoteInstance(),
library: RemoteInstance.deserialize(this),
metadata: (this..moveNext())._expectRemoteInstanceList(),
typeParameters: (this..moveNext())._expectRemoteInstanceList(),
interfaces: (this..moveNext())._expectRemoteInstanceList(),
mixins: (this..moveNext())._expectRemoteInstanceList(),
);
MixinDeclarationImpl _expectMixinDeclaration(int id) => MixinDeclarationImpl(
id: id,
identifier: expectRemoteInstance(),
library: RemoteInstance.deserialize(this),
metadata: (this..moveNext())._expectRemoteInstanceList(),
typeParameters: (this..moveNext())._expectRemoteInstanceList(),
hasBase: (this..moveNext()).expectBool(),
interfaces: (this..moveNext())._expectRemoteInstanceList(),
superclassConstraints: (this..moveNext())._expectRemoteInstanceList(),
);
EnumValueDeclarationImpl _expectEnumValueDeclaration(int id) =>
EnumValueDeclarationImpl(
id: id,
identifier: expectRemoteInstance(),
library: RemoteInstance.deserialize(this),
metadata: (this..moveNext())._expectRemoteInstanceList(),
definingEnum: RemoteInstance.deserialize(this),
);
MacroExceptionImpl _expectException(RemoteInstanceKind kind, int id) =>
MacroExceptionImpl(
id: id,
kind: kind,
message: expectString(),
stackTrace: (this..moveNext()).expectNullableString(),
);
ExtensionDeclarationImpl _expectExtensionDeclaration(int id) =>
ExtensionDeclarationImpl(
id: id,
identifier: expectRemoteInstance(),
library: RemoteInstance.deserialize(this),
metadata: (this..moveNext())._expectRemoteInstanceList(),
typeParameters: (this..moveNext())._expectRemoteInstanceList(),
onType: RemoteInstance.deserialize(this),
);
ExtensionTypeDeclarationImpl _expectExtensionTypeDeclaration(int id) =>
ExtensionTypeDeclarationImpl(
id: id,
identifier: expectRemoteInstance(),
library: RemoteInstance.deserialize(this),
metadata: (this..moveNext())._expectRemoteInstanceList(),
typeParameters: (this..moveNext())._expectRemoteInstanceList(),
representationType: RemoteInstance.deserialize(this),
);
TypeAliasDeclarationImpl _expectTypeAliasDeclaration(int id) =>
TypeAliasDeclarationImpl(
id: id,
identifier: expectRemoteInstance(),
library: RemoteInstance.deserialize(this),
metadata: (this..moveNext())._expectRemoteInstanceList(),
typeParameters: (this..moveNext())._expectRemoteInstanceList(),
aliasedType: RemoteInstance.deserialize(this),
);
LibraryImpl _expectLibrary(int id) => LibraryImpl(
id: id,
languageVersion:
LanguageVersionImpl(expectInt(), (this..moveNext()).expectInt()),
metadata: (this..moveNext())._expectRemoteInstanceList(),
uri: (this..moveNext()).expectUri(),
);
List<String> _readStringList() => [
for (bool hasNext = (this
..moveNext()
..expectList())
.moveNext();
hasNext;
hasNext = moveNext())
expectString(),
];
List<T> _readCodeList<T extends Code>() => [
for (bool hasNext = (this
..moveNext()
..expectList())
.moveNext();
hasNext;
hasNext = moveNext())
expectCode(),
];
List<Object> _readParts() {
moveNext();
expectList();
List<Object> parts = [];
while (moveNext()) {
_CodePartKind partKind = _CodePartKind.values[expectInt()];
moveNext();
switch (partKind) {
case _CodePartKind.code:
parts.add(expectCode());
break;
case _CodePartKind.string:
parts.add(expectString());
break;
case _CodePartKind.identifier:
parts.add(expectRemoteInstance());
break;
}
}
return parts;
}
T expectCode<T extends Code>() {
CodeKind kind = CodeKind.values[expectInt()];
return switch (kind) {
CodeKind.raw => RawCode.fromParts(_readParts()) as T,
CodeKind.rawTypeAnnotation =>
RawTypeAnnotationCode.fromParts(_readParts()) as T,
CodeKind.comment => CommentCode.fromParts(_readParts()) as T,
CodeKind.declaration => DeclarationCode.fromParts(_readParts()) as T,
CodeKind.expression => ExpressionCode.fromParts(_readParts()) as T,
CodeKind.functionBody => FunctionBodyCode.fromParts(_readParts()) as T,
CodeKind.functionTypeAnnotation => FunctionTypeAnnotationCode(
namedParameters: _readCodeList(),
positionalParameters: _readCodeList(),
returnType: (this..moveNext()).expectNullableCode(),
typeParameters: _readCodeList()) as T,
CodeKind.namedTypeAnnotation => NamedTypeAnnotationCode(
name: RemoteInstance.deserialize(this) as Identifier,
typeArguments: _readCodeList()) as T,
CodeKind.nullableTypeAnnotation =>
NullableTypeAnnotationCode((this..moveNext()).expectCode()) as T,
CodeKind.omittedTypeAnnotation =>
OmittedTypeAnnotationCode(RemoteInstance.deserialize(this)) as T,
CodeKind.parameter => ParameterCode(
defaultValue: (this..moveNext()).expectNullableCode(),
keywords: _readStringList(),
name: (this..moveNext()).expectNullableString(),
style: ParameterStyle.values[(this..moveNext()).expectInt()],
type: (this..moveNext()).expectNullableCode()) as T,
CodeKind.recordField => RecordFieldCode(
name: (this..moveNext()).expectNullableString(),
type: (this..moveNext()).expectCode()) as T,
CodeKind.recordTypeAnnotation => RecordTypeAnnotationCode(
namedFields: _readCodeList(), positionalFields: _readCodeList()) as T,
CodeKind.typeParameter => TypeParameterCode(
bound: (this..moveNext()).expectNullableCode(),
name: (this..moveNext()).expectString()) as T,
};
}
T? expectNullableCode<T extends Code>() {
if (checkNull()) return null;
return expectCode();
}
Diagnostic expectDiagnostic() {
expectList();
List<DiagnosticMessage> context = [
for (; moveNext();) expectDiagnosticMessage(),
];
String? correctionMessage = (this..moveNext()).expectNullableString();
DiagnosticMessage message = (this..moveNext()).expectDiagnosticMessage();
Severity severity = Severity.values[(this..moveNext()).expectInt()];
return Diagnostic(message, severity,
contextMessages: context, correctionMessage: correctionMessage);
}
DiagnosticMessage expectDiagnosticMessage() {
String message = expectString();
moveNext();
RemoteInstance? target = checkNull() ? null : expectRemoteInstance();
return switch (target) {
null => DiagnosticMessage(message),
DeclarationImpl() =>
DiagnosticMessage(message, target: target.asDiagnosticTarget),
TypeAnnotationImpl() =>
DiagnosticMessage(message, target: target.asDiagnosticTarget),
MetadataAnnotationImpl() =>
DiagnosticMessage(message, target: target.asDiagnosticTarget),
_ => throw UnsupportedError(
'Unsupported target type ${target.runtimeType}, only Declarations, '
'TypeAnnotations, and Metadata are allowed.'),
};
}
}
extension SerializeNullable on Serializable? {
/// Either serializes a `null` literal or the object.
void serializeNullable(Serializer serializer) {
Serializable? self = this;
if (self == null) {
serializer.addNull();
} else {
self.serialize(serializer);
}
}
}
extension SerializeNullableCode on Code? {
/// Either serializes a `null` literal or the code object.
void serializeNullable(Serializer serializer) {
Code? self = this;
if (self == null) {
serializer.addNull();
} else {
self.serialize(serializer);
}
}
}
extension SerializeCode on Code {
void serialize(Serializer serializer) {
serializer.addInt(kind.index);
switch (kind) {
case CodeKind.namedTypeAnnotation:
NamedTypeAnnotationCode self = this as NamedTypeAnnotationCode;
(self.name as IdentifierImpl).serialize(serializer);
serializer.startList();
for (TypeAnnotationCode typeArg in self.typeArguments) {
typeArg.serialize(serializer);
}
serializer.endList();
return;
case CodeKind.functionTypeAnnotation:
FunctionTypeAnnotationCode self = this as FunctionTypeAnnotationCode;
serializer.startList();
for (ParameterCode named in self.namedParameters) {
named.serialize(serializer);
}
serializer
..endList()
..startList();
for (ParameterCode positional in self.positionalParameters) {
positional.serialize(serializer);
}
serializer.endList();
self.returnType.serializeNullable(serializer);
serializer.startList();
for (TypeParameterCode typeParam in self.typeParameters) {
typeParam.serialize(serializer);
}
serializer.endList();
return;
case CodeKind.nullableTypeAnnotation:
NullableTypeAnnotationCode self = this as NullableTypeAnnotationCode;
self.underlyingType.serialize(serializer);
return;
case CodeKind.omittedTypeAnnotation:
OmittedTypeAnnotationCode self = this as OmittedTypeAnnotationCode;
(self.typeAnnotation as OmittedTypeAnnotationImpl)
.serialize(serializer);
return;
case CodeKind.recordField:
RecordFieldCode self = this as RecordFieldCode;
serializer.addNullableString(self.name);
self.type.serialize(serializer);
return;
case CodeKind.recordTypeAnnotation:
RecordTypeAnnotationCode self = this as RecordTypeAnnotationCode;
serializer.startList();
for (RecordFieldCode field in self.namedFields) {
field.serialize(serializer);
}
serializer
..endList()
..startList();
for (RecordFieldCode field in self.positionalFields) {
field.serialize(serializer);
}
serializer.endList();
return;
case CodeKind.parameter:
ParameterCode self = this as ParameterCode;
self.defaultValue.serializeNullable(serializer);
serializer.startList();
for (String keyword in self.keywords) {
serializer.addString(keyword);
}
serializer
..endList()
..addNullableString(self.name);
serializer.addInt(self.style.index);
self.type.serializeNullable(serializer);
return;
case CodeKind.typeParameter:
TypeParameterCode self = this as TypeParameterCode;
self.bound.serializeNullable(serializer);
serializer.addString(self.name);
return;
case CodeKind.comment:
case CodeKind.declaration:
case CodeKind.expression:
case CodeKind.raw:
case CodeKind.rawTypeAnnotation:
case CodeKind.functionBody:
serializer.startList();
for (Object part in parts) {
if (part is String) {
serializer
..addInt(_CodePartKind.string.index)
..addString(part);
} else if (part is Code) {
serializer.addInt(_CodePartKind.code.index);
part.serialize(serializer);
} else if (part is IdentifierImpl) {
serializer.addInt(_CodePartKind.identifier.index);
part.serialize(serializer);
} else {
throw StateError('Unrecognized code part $part');
}
}
serializer.endList();
return;
}
}
}
extension SerializeDiagnostic on Diagnostic {
void serialize(Serializer serializer) {
serializer.startList();
for (DiagnosticMessage message in contextMessages) {
message.serialize(serializer);
}
serializer.endList();
serializer.addNullableString(correctionMessage);
message.serialize(serializer);
serializer.addInt(severity.index);
}
}
extension SerializeDiagnosticMessage on DiagnosticMessage {
void serialize(Serializer serializer) {
serializer.addString(message);
switch (target) {
case null:
serializer.addNull();
case DeclarationDiagnosticTarget target:
(target.declaration as DeclarationImpl).serialize(serializer);
case TypeAnnotationDiagnosticTarget target:
(target.typeAnnotation as TypeAnnotationImpl).serialize(serializer);
case MetadataAnnotationDiagnosticTarget target:
(target.metadataAnnotation as MetadataAnnotationImpl)
.serialize(serializer);
}
}
}
extension Helpers on Serializer {
void addUri(Uri uri) => addString('$uri');
void addSerializable(Serializable serializable) =>
serializable.serialize(this);
}
enum _CodePartKind {
string,
code,
identifier,
}
-488
View File
@@ -1,488 +0,0 @@
// Copyright (c) 2024, 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 '../api.dart';
import '../executor.dart';
/// Meta information for a span of text in a generated augmentation library.
///
/// These are collected during generation of augmentation libraries and are used
/// to compute relation between offsets in the intermediate augmentation
/// libraries and the merged augmented library.
class Span {
/// Key that defines the semantics of the content of this span.
///
/// This must be unique within the spans generated from a single
/// augmentation library.
final Key key;
/// The offset in the generated augmentation library source code where this
/// span occurs.
final int offset;
/// The source code of this span.
final String text;
Span(this.key, this.offset, this.text);
}
/// Object that defines the semantics of a [Span] in a generated augmentation
/// library.
///
/// This is used to identify corresponding parts of generated augmentation
/// libraries when converting offsets from the intermediate augmentation
/// libraries to the merged augmentation library.
///
/// For instance we might have two intermediate both containing an import
/// of the same library with potentially different prefixes:
///
/// // intermediate augmentation library #0
/// ...
/// import 'dart:core' as prefix1;
/// ...
/// prefix1.String method1() => '42';
/// ...
///
/// // intermediate augmentation library #1
/// ...
/// import 'dart:core' as prefix2;
/// ...
/// prefix2.String method2() => '87';
/// ...
///
/// and the merged augmentation library:
///
/// ...
/// import 'dart:core' as prefix4;
/// ...
/// prefix4.String method1() => '42';
/// ...
/// prefix4.String method2() => '87';
/// ...
///
/// Here the same key is used for the 'prefix1', 'prefix2' and 'prefix4' in the
/// import directives. The same key is used for 'prefix1' in
/// 'prefix1.String' in intermediate augmentation library #0 and the 'prefix4'
/// in the first occurrence of 'prefix4.String' in the merged augmentation
/// library. Similarly for 'prefix2' and 'prefix4' for 'method2'.
sealed class Key {
Key? get parent;
}
enum _ContentKind {
code,
string,
implicitThis,
prefixDot,
staticScope,
identifierName,
libraryAugmentation,
libraryAugmentationSeparator,
}
/// Content defined by its [_kind] and [index] within the [parent] key.
class ContentKey implements Key {
@override
final Key parent;
final int index;
final _ContentKind _kind;
ContentKey._(this.parent, this.index, this._kind);
/// Create the key for a [Code] object occurring as the [index]th part of
/// [parent].
ContentKey.code(Key parent, int index)
: this._(parent, index, _ContentKind.code);
/// Create the key for a [String] occurring as the [index]th part of [parent].
ContentKey.string(Key parent, int index)
: this._(parent, index, _ContentKind.string);
/// Create the key for a `this.` occurring as the [index]th part of [parent].
ContentKey.implicitThis(Key parent, int index)
: this._(parent, index, _ContentKind.implicitThis);
/// Create the key for a `.` after a prefix occurring as the [index]th part
/// of [parent].
ContentKey.prefixDot(Key parent, int index)
: this._(parent, index, _ContentKind.prefixDot);
/// Create the key for a static qualifier `Foo.` of a static member access in
/// `Foo` occurring as the [index]th part of [parent].
ContentKey.staticScope(Key parent, int index)
: this._(parent, index, _ContentKind.staticScope);
/// Create the key for an [Identifier] after a prefix occurring as the
/// [index]th part of [parent].
ContentKey.identifierName(Key parent, int index)
: this._(parent, index, _ContentKind.identifierName);
/// Create the key the [index]th library augmentation in [parent].
ContentKey.libraryAugmentation(Key parent, int index)
: this._(parent, index, _ContentKind.libraryAugmentation);
/// Create the key the separator text after the [index]th library augmentation
/// in [parent].
ContentKey.libraryAugmentationSeparator(Key parent, int index)
: this._(parent, index, _ContentKind.libraryAugmentationSeparator);
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is ContentKey &&
runtimeType == other.runtimeType &&
parent == other.parent &&
index == other.index &&
_kind == other._kind;
@override
int get hashCode => Object.hash(parent, index, _kind);
}
enum _UriKind {
prefix,
importPrefix,
importSuffix,
}
/// Use of a [Uri] defined by the [uri] and the [_kind] of use.
class UriKey implements Key {
final Uri uri;
final _UriKind _kind;
UriKey._(this.uri, this._kind);
/// Creates a key for the definition of the prefix for [uri], that is,
/// "prefix" in `import 'foo.dart' as prefix;`.
UriKey.prefixDefinition(Uri uri) : this._(uri, _UriKind.prefix);
/// Creates a key for the prefix of the import of [uri], that is,
/// "import 'foo.dart' as" in `import 'foo.dart' as prefix;`.
UriKey.importPrefix(Uri uri) : this._(uri, _UriKind.importPrefix);
/// Creates a key for the suffix of the import of [uri], that is,
/// ";\n" in
///
/// import 'foo.dart' as prefix;
///
UriKey.importSuffix(Uri uri) : this._(uri, _UriKind.importSuffix);
@override
Key? get parent => null;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is UriKey &&
runtimeType == other.runtimeType &&
uri == other.uri &&
_kind == other._kind;
@override
int get hashCode => Object.hash(uri, _kind);
}
/// A reference to the prefix of [uri] occurring as the [index]th part of
/// [parent].
class PrefixUseKey implements Key {
@override
final Key parent;
final int index;
final Uri uri;
PrefixUseKey(this.parent, this.index, this.uri);
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is PrefixUseKey &&
runtimeType == other.runtimeType &&
parent == other.parent &&
uri == other.uri &&
index == other.index;
@override
int get hashCode => Object.hash(parent, uri, index);
}
/// The use of [omittedTypeAnnotation] occurring as the [index]th part of
/// [parent].
class OmittedTypeAnnotationKey implements Key {
@override
final Key parent;
final int index;
final OmittedTypeAnnotation omittedTypeAnnotation;
OmittedTypeAnnotationKey(this.parent, this.index, this.omittedTypeAnnotation);
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is OmittedTypeAnnotationKey &&
runtimeType == other.runtimeType &&
parent == other.parent &&
index == other.index &&
omittedTypeAnnotation == other.omittedTypeAnnotation;
@override
int get hashCode => Object.hash(parent, index, omittedTypeAnnotation);
}
/// The content defined by [result].
///
/// This is used as the root key for content specific to [result].
class MacroExecutionResultKey implements Key {
final MacroExecutionResult result;
MacroExecutionResultKey(this.result);
@override
Key? get parent => null;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is MacroExecutionResultKey &&
runtimeType == other.runtimeType &&
result == other.result;
@override
int get hashCode => result.hashCode;
}
/// The root key for content of [typeDeclaration].
///
/// This is used as the root key for the parts of the declaration of
/// [typeDeclaration] that can be shared amongst members.
///
/// For instance when to intermediate augmentation libraries generate members
/// for the same class we have
///
/// // intermediate augmentation library #0
/// ...
/// augment class Foo {
/// method1() {}
/// }
/// ...
///
/// // intermediate augmentation library #1
/// ...
/// augment class Foo {
/// method2() {}
/// }
/// ...
///
/// and the merged augmentation library merges these to same the class
/// declaration:
///
/// ...
/// augment class Foo {
/// method1() {}
/// method2() {}
/// }
/// ...
///
/// In this case the declaration "augment class Foo ", the body start "{\n" and
/// the body end "}\n" use keys with the same [TypeDeclarationKey] as parent.
class TypeDeclarationKey implements Key {
final TypeDeclaration typeDeclaration;
TypeDeclarationKey(this.typeDeclaration);
@override
Key? get parent => null;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is TypeDeclarationKey &&
runtimeType == other.runtimeType &&
typeDeclaration == other.typeDeclaration;
@override
int get hashCode => typeDeclaration.hashCode;
}
enum _TypeDeclarationContentKind {
declaration,
superclass,
mixins,
interfaces,
bodyStart,
enumValueEnd,
declarationSeparator,
bodyEnd,
typeParameterStart,
typeParameterEnd,
}
/// Content of a [TypeDeclaration].
class TypeDeclarationContentKey implements Key {
@override
final Key parent;
final _TypeDeclarationContentKind _kind;
TypeDeclarationContentKey._(this.parent, this._kind);
/// The declaration of the type declaration, that is, "augment class Foo " in
/// `augment class Foo { }`.
TypeDeclarationContentKey.declaration(Key parent)
: this._(parent, _TypeDeclarationContentKind.declaration);
/// The fixed parts of a with-clause, that is, "with " and ", " in
/// `augment class Foo with Bar, Baz { }`.
TypeDeclarationContentKey.mixins(Key parent)
: this._(parent, _TypeDeclarationContentKind.mixins);
/// The fixed parts of an implements-clause, that is, "implements " and ", "
/// in `augment class Foo implements Bar, Baz { }`.
TypeDeclarationContentKey.interfaces(Key parent)
: this._(parent, _TypeDeclarationContentKind.interfaces);
/// The fixed parts of an extends-clause, that is, "extends " in
/// `augment class Foo extends Bar { }`.
TypeDeclarationContentKey.superclass(Key parent)
: this._(parent, _TypeDeclarationContentKind.superclass);
/// The start of the declaration body, that is, "{\n" in
///
/// augment class Foo implements Bar, Baz {
/// }
///
TypeDeclarationContentKey.bodyStart(Key parent)
: this._(parent, _TypeDeclarationContentKind.bodyStart);
/// The end of element values, that is, ";\n"
///
/// augment enum Foo {
/// a,
/// b,
/// ;
/// method() {}
/// }
///
TypeDeclarationContentKey.enumValueEnd(Key parent)
: this._(parent, _TypeDeclarationContentKind.enumValueEnd);
/// The space between member declarations.
TypeDeclarationContentKey.declarationSeparator(Key parent)
: this._(parent, _TypeDeclarationContentKind.declarationSeparator);
/// The end of the declaration body, that is, "}\n" in
///
/// augment class Foo implements Bar, Baz {
/// }
///
TypeDeclarationContentKey.bodyEnd(Key parent)
: this._(parent, _TypeDeclarationContentKind.bodyEnd);
/// The start of the type parameters, that is, "<" in
///
/// augment class Foo<T> {
/// }
///
TypeDeclarationContentKey.typeParametersStart(Key parent)
: this._(parent, _TypeDeclarationContentKind.typeParameterStart);
/// The end of the type parameters, that is, ">" in
///
/// augment class Foo<T> {
/// }
///
TypeDeclarationContentKey.typeParametersEnd(Key parent)
: this._(parent, _TypeDeclarationContentKind.typeParameterEnd);
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is TypeDeclarationContentKey &&
runtimeType == other.runtimeType &&
parent == other.parent &&
_kind == other._kind;
@override
int get hashCode => Object.hash(parent, _kind);
}
enum _IdentifierKind {
enuum, // `uu` because `enum` is reserved
mixin,
interface,
type,
}
/// Key defined be the [identifier] its use [_kind] occurring as the [index]th
/// part of [parent].
class IdentifierKey implements Key {
@override
final Key parent;
final Identifier identifier;
final int index;
final _IdentifierKind _kind;
IdentifierKey._(this.parent, this.index, this.identifier, this._kind);
/// Identifier for an enum value.
IdentifierKey.enum_(Key parent, int index, Identifier identifier)
: this._(parent, index, identifier, _IdentifierKind.enuum);
/// Identifier for an extended type.
IdentifierKey.superclass(Key parent, Identifier identifier)
: this._(parent, 0, identifier, _IdentifierKind.interface);
/// Identifier for a mixed in type.
IdentifierKey.mixin(Key parent, int index, Identifier identifier)
: this._(parent, index, identifier, _IdentifierKind.mixin);
/// Identifier for an implemented type.
IdentifierKey.interface(Key parent, int index, Identifier identifier)
: this._(parent, index, identifier, _IdentifierKind.interface);
/// Identifier for an augmented member.
IdentifierKey.member(Key parent, int index, Identifier identifier)
: this._(parent, index, identifier, _IdentifierKind.type);
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is IdentifierKey &&
runtimeType == other.runtimeType &&
parent == other.parent &&
index == other.index &&
identifier == other.identifier &&
_kind == other._kind;
@override
int get hashCode => Object.hash(parent, index, identifier, _kind);
}
/// Key for the separation between imports and declarations.
class ImportDeclarationSeparatorKey implements Key {
const ImportDeclarationSeparatorKey();
@override
Key? get parent => null;
}
/// Key for the end-of-file.
class EndOfFileKey implements Key {
const EndOfFileKey();
@override
Key? get parent => null;
}
/// Key for the `library augment` directive
class LibraryAugmentKey implements Key {
const LibraryAugmentKey();
@override
Key? get parent => null;
}
-18
View File
@@ -1,18 +0,0 @@
name: _macros
version: 0.3.3
description: >-
This is a private SDK vendored package, which is re-exported by the public
`macros` package, which is a pub package. Every change to this package is
treated as a release, see CONTRIBUTING.md for full instructions.
publish_to: none
repository: https://github.com/dart-lang/sdk/tree/main/pkg/_macros
environment:
sdk: ^3.5.0
resolution: workspace
# Note that as an SDK vendored package, pub package dependencies are only
# allowed in the dev_dependencies section.
dev_dependencies:
test: any
@@ -1,703 +0,0 @@
// 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:test/fake.dart';
import 'package:test/test.dart';
import 'package:_macros/src/api.dart';
import 'package:_macros/src/executor.dart';
import 'package:_macros/src/executor/augmentation_library.dart';
import 'package:_macros/src/executor/introspection_impls.dart';
import 'package:_macros/src/executor/remote_instance.dart';
import 'package:_macros/src/executor/response_impls.dart';
import '../util.dart';
void main() {
group('AugmentationLibraryBuilder', () {
final intIdentifier = TestIdentifier(
id: RemoteInstance.uniqueId,
name: 'int',
kind: IdentifierKind.topLevelMember,
staticScope: null,
uri: Uri.parse('dart:core'));
final objectIdentifier = TestIdentifier(
id: RemoteInstance.uniqueId,
name: 'Object',
kind: IdentifierKind.topLevelMember,
staticScope: null,
uri: Uri.parse('dart:core'));
final superclassIdentifier = TestIdentifier(
id: RemoteInstance.uniqueId,
name: 'SomeSuperclass',
kind: IdentifierKind.topLevelMember,
uri: null,
staticScope: null);
final interfaceIdentifiers = [
for (var i = 0; i < 2; i++)
TestIdentifier(
id: RemoteInstance.uniqueId,
name: 'I$i',
kind: IdentifierKind.topLevelMember,
uri: null,
staticScope: null),
];
final mixinIdentifiers = [
for (var i = 0; i < 2; i++)
TestIdentifier(
id: RemoteInstance.uniqueId,
name: 'M$i',
kind: IdentifierKind.topLevelMember,
uri: null,
staticScope: null),
];
test('can combine multiple execution results', () {
final classes = <IdentifierImpl, ClassDeclaration>{};
for (var i = 0; i < 2; i++) {
for (var j = 0; j < 3; j++) {
final identifier =
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Foo$i$j');
classes[identifier] = ClassDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier: identifier,
library: Fixtures.library,
metadata: [],
typeParameters: [],
interfaces: [],
hasAbstract: false,
hasBase: false,
hasExternal: false,
hasFinal: false,
hasInterface: false,
hasMixin: false,
hasSealed: false,
mixins: [],
superclass: null);
}
}
var results = [
for (var i = 0; i < 2; i++)
MacroExecutionResultImpl(
diagnostics: [],
enumValueAugmentations: {},
extendsTypeAugmentations: {
for (var j in [0, 2])
classes.keys.firstWhere(
(identifier) => identifier.name == 'Foo$i$j'):
NamedTypeAnnotationCode(name: superclassIdentifier),
},
interfaceAugmentations: {
for (var j = 0; j < 3; j++)
classes.keys
.firstWhere((identifier) => identifier.name == 'Foo$i$j'): [
for (var k = 0; k < j; k++)
NamedTypeAnnotationCode(name: interfaceIdentifiers[k]),
]
},
libraryAugmentations: [
for (var j = 0; j < 3; j++)
DeclarationCode.fromParts(
[intIdentifier, ' get i${i}j$j => ${i + j};\n']),
],
mixinAugmentations: {
for (var j = 0; j < 3; j++)
classes.keys
.firstWhere((identifier) => identifier.name == 'Foo$i$j'): [
for (var k = 0; k < i; k++)
NamedTypeAnnotationCode(name: mixinIdentifiers[k]),
]
},
newTypeNames: [
'Foo${i}0',
'Foo${i}1',
'Foo${i}2',
],
typeAugmentations: {
for (var j = 0; j < 3; j++)
classes.keys
.firstWhere((identifier) => identifier.name == 'Foo$i$j'): [
DeclarationCode.fromParts([intIdentifier, ' get i => $i;\n']),
DeclarationCode.fromParts([intIdentifier, ' get j => $j;\n']),
]
},
),
];
var library = _TestExecutor().buildAugmentationLibrary(
Fixtures.library.uri,
results,
(Identifier i) => classes[i]!,
(Identifier i) => (i as TestIdentifier).resolved,
(OmittedTypeAnnotation i) =>
(i as TestOmittedTypeAnnotation).inferredType);
expect(library, equalsIgnoringWhitespace('''
augment library 'package:foo/bar.dart';
import 'dart:core' as prefix0;
prefix0.int get i0j0 => 0;
prefix0.int get i0j1 => 1;
prefix0.int get i0j2 => 2;
prefix0.int get i1j0 => 1;
prefix0.int get i1j1 => 2;
prefix0.int get i1j2 => 3;
augment class Foo00 extends SomeSuperclass {
prefix0.int get i => 0;
prefix0.int get j => 0;
}
augment class Foo02 extends SomeSuperclass implements I0, I1 {
prefix0.int get i => 0;
prefix0.int get j => 2;
}
augment class Foo10 extends SomeSuperclass with M0 {
prefix0.int get i => 1;
prefix0.int get j => 0;
}
augment class Foo12 extends SomeSuperclass with M0 implements I0, I1 {
prefix0.int get i => 1;
prefix0.int get j => 2;
}
augment class Foo01 implements I0 {
prefix0.int get i => 0;
prefix0.int get j => 1;
}
augment class Foo11 with M0 implements I0 {
prefix0.int get i => 1;
prefix0.int get j => 1;
}
'''));
});
test('can add imports for identifiers', () {
var fooIdentifier = TestIdentifier(
id: RemoteInstance.uniqueId,
name: 'Foo',
kind: IdentifierKind.topLevelMember,
staticScope: null,
uri: Uri.parse('package:foo/foo.dart'));
var barIdentifier = TestIdentifier(
id: RemoteInstance.uniqueId,
name: 'Bar',
kind: IdentifierKind.topLevelMember,
staticScope: null,
uri: Uri.parse('package:bar/bar.dart'));
var builderIdentifier = TestIdentifier(
id: RemoteInstance.uniqueId,
name: 'Builder',
kind: IdentifierKind.topLevelMember,
staticScope: null,
uri: Uri.parse('package:builder/builder.dart'));
var barInstanceMember = TestIdentifier(
id: RemoteInstance.uniqueId,
name: 'baz',
kind: IdentifierKind.instanceMember,
staticScope: null,
uri: Uri.parse('package:bar/bar.dart'));
var barStaticMember = TestIdentifier(
id: RemoteInstance.uniqueId,
name: 'zap',
kind: IdentifierKind.staticInstanceMember,
staticScope: 'Bar',
uri: Uri.parse('package:bar/bar.dart'));
var results = [
MacroExecutionResultImpl(
diagnostics: [],
enumValueAugmentations: {},
extendsTypeAugmentations: {},
interfaceAugmentations: {},
mixinAugmentations: {},
typeAugmentations: {},
libraryAugmentations: [
DeclarationCode.fromParts([
'class FooBuilder<T extends ',
fooIdentifier,
'> implements ',
builderIdentifier,
'<',
barIdentifier,
'<T>> {\n',
'late ',
intIdentifier,
' ${barInstanceMember.name};\n',
barIdentifier,
'<T> build() => new ',
barIdentifier,
'()..',
barInstanceMember,
' = ',
barStaticMember,
';',
'\n}',
]),
],
newTypeNames: [
'FooBuilder',
],
)
];
var library = _TestExecutor().buildAugmentationLibrary(
Fixtures.library.uri,
results,
(_) => throw UnimplementedError(),
(Identifier i) => (i as TestIdentifier).resolved,
(OmittedTypeAnnotation i) =>
(i as TestOmittedTypeAnnotation).inferredType);
expect(library, equalsIgnoringWhitespace('''
augment library 'package:foo/bar.dart';
import 'package:foo/foo.dart' as prefix0;
import 'package:builder/builder.dart' as prefix1;
import 'package:bar/bar.dart' as prefix2;
import 'dart:core' as prefix3;
class FooBuilder<T extends prefix0.Foo> implements prefix1.Builder<prefix2.Bar<T>> {
late prefix3.int baz;
prefix2.Bar<T> build() => new prefix2.Bar()..baz = prefix2.Bar.zap;
}
'''));
});
test('can handle omitted type annotations', () {
var results = [
MacroExecutionResultImpl(
diagnostics: [],
enumValueAugmentations: {},
extendsTypeAugmentations: {},
interfaceAugmentations: {},
mixinAugmentations: {},
typeAugmentations: {},
libraryAugmentations: [
DeclarationCode.fromParts([
OmittedTypeAnnotationCode(
TestOmittedTypeAnnotation(NamedTypeAnnotationImpl(
id: RemoteInstance.uniqueId,
identifier: intIdentifier,
isNullable: false,
typeArguments: [],
))),
' x = 1;',
]),
],
newTypeNames: []),
];
var library = _TestExecutor().buildAugmentationLibrary(
Fixtures.library.uri,
results,
(_) => throw UnimplementedError(),
(Identifier i) => (i as TestIdentifier).resolved,
(OmittedTypeAnnotation i) =>
(i as TestOmittedTypeAnnotation).inferredType);
expect(library, equalsIgnoringWhitespace('''
augment library 'package:foo/bar.dart';
import 'dart:core' as prefix0;
prefix0.int x = 1;
'''));
});
test('can handle name conflicts', () {
var omittedType0 = TestOmittedTypeAnnotation();
var omittedType1 = TestOmittedTypeAnnotation();
var omittedTypeIdentifier = TestIdentifier(
id: RemoteInstance.uniqueId,
name: 'OmittedType',
kind: IdentifierKind.topLevelMember,
staticScope: null,
uri: Uri.parse('package:foo/foo.dart'));
var omittedTypeIdentifier0 = TestIdentifier(
id: RemoteInstance.uniqueId,
name: 'OmittedType0',
kind: IdentifierKind.topLevelMember,
staticScope: null,
uri: Uri.parse('package:bar/bar.dart'));
var prefixInstanceMember = TestIdentifier(
id: RemoteInstance.uniqueId,
name: 'prefix',
kind: IdentifierKind.instanceMember,
staticScope: null,
uri: Uri.parse('package:bar/bar.dart'));
var prefix0InstanceMember = TestIdentifier(
id: RemoteInstance.uniqueId,
name: 'prefix0',
kind: IdentifierKind.instanceMember,
staticScope: null,
uri: Uri.parse('package:bar/bar.dart'));
var prefix1StaticMember = TestIdentifier(
id: RemoteInstance.uniqueId,
name: 'prefix1',
kind: IdentifierKind.staticInstanceMember,
staticScope: 'OmittedType1',
uri: Uri.parse('package:bar/bar.dart'));
var results = [
MacroExecutionResultImpl(
diagnostics: [],
enumValueAugmentations: {},
extendsTypeAugmentations: {},
interfaceAugmentations: {},
mixinAugmentations: {},
typeAugmentations: {},
libraryAugmentations: [
DeclarationCode.fromParts([
'class OmittedType {\n ',
omittedType0.code,
' method(',
omittedType1.code,
' o) {\n ',
intIdentifier,
' ${prefixInstanceMember.name} = 0;\n ',
omittedTypeIdentifier,
' ${prefix0InstanceMember.name} = ',
'new ',
omittedTypeIdentifier,
'();\n ',
'new ',
omittedTypeIdentifier0,
'()..',
prefixInstanceMember,
' = ',
prefix1StaticMember,
';',
'\n }',
'\n}',
]),
],
newTypeNames: [
'OmittedType',
],
)
];
var omittedTypes = <OmittedTypeAnnotation, String>{};
var library = _TestExecutor().buildAugmentationLibrary(
Fixtures.library.uri,
results,
(_) => throw UnimplementedError(),
(Identifier i) => (i as TestIdentifier).resolved,
(OmittedTypeAnnotation i) =>
(i as TestOmittedTypeAnnotation).inferredType,
omittedTypes: omittedTypes);
expect(library, equalsIgnoringWhitespace('''
augment library 'package:foo/bar.dart';
import 'dart:core' as prefix2_0;
import 'package:foo/foo.dart' as prefix2_1;
import 'package:bar/bar.dart' as prefix2_2;
class OmittedType {
OmittedType2_0 method(OmittedType2_1 o) {
prefix2_0.int prefix = 0;
prefix2_1.OmittedType prefix0 = new prefix2_1.OmittedType();
new prefix2_2.OmittedType0()..prefix = prefix2_2.OmittedType1.prefix1;
}
}
'''));
expect(omittedTypes[omittedType0], 'OmittedType2_0');
expect(omittedTypes[omittedType1], 'OmittedType2_1');
});
test('can augment enums and enum values', () async {
final myEnum = EnumDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier: TestIdentifier(
id: RemoteInstance.uniqueId,
name: 'MyEnum',
kind: IdentifierKind.topLevelMember,
uri: Uri.parse('a.dart'),
staticScope: null),
library: Fixtures.library,
metadata: [],
typeParameters: [],
interfaces: [],
mixins: [],
);
final myField = FieldDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier: TestIdentifier(
id: RemoteInstance.uniqueId,
name: 'value',
kind: IdentifierKind.instanceMember,
uri: Uri.parse('a.dart'),
staticScope: null),
library: Fixtures.library,
metadata: [],
definingType: myEnum.identifier,
hasAbstract: false,
hasConst: false,
hasExternal: false,
hasFinal: true,
hasInitializer: false,
hasLate: false,
hasStatic: false,
type: NamedTypeAnnotationImpl(
id: RemoteInstance.uniqueId,
isNullable: false,
identifier: intIdentifier,
typeArguments: []));
var results = [
MacroExecutionResultImpl(diagnostics: [], enumValueAugmentations: {
myEnum.identifier: [
DeclarationCode.fromParts(['a(1),\n']),
],
}, extendsTypeAugmentations: {}, typeAugmentations: {
myEnum.identifier: [
DeclarationCode.fromParts(['MyEnum(', myField.identifier, ');\n']),
DeclarationCode.fromParts(
['final ', intIdentifier, ' ', myField.identifier.name, ';\n']),
],
}, interfaceAugmentations: {
myEnum.identifier: [
NamedTypeAnnotationCode(name: interfaceIdentifiers.first),
],
}, mixinAugmentations: {
myEnum.identifier: [
NamedTypeAnnotationCode(name: mixinIdentifiers.first),
],
}, newTypeNames: [], libraryAugmentations: []),
];
var library = _TestExecutor().buildAugmentationLibrary(
Fixtures.library.uri,
results,
(Identifier i) =>
i == myEnum.identifier ? myEnum : throw UnimplementedError(),
(Identifier i) => (i as TestIdentifier).resolved,
(OmittedTypeAnnotation i) =>
(i as TestOmittedTypeAnnotation).inferredType);
expect(library, equalsIgnoringWhitespace('''
augment library 'package:foo/bar.dart';
import 'a.dart' as prefix0;
import 'dart:core' as prefix1;
augment enum MyEnum with M0 implements I0 {
a(1),
;
MyEnum(this.value);
final prefix1.int value;
}
'''));
});
test('can augment extensions', () async {
final myExtension = ExtensionDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier: TestIdentifier(
id: RemoteInstance.uniqueId,
name: 'MyExtension',
kind: IdentifierKind.topLevelMember,
uri: Uri.parse('a.dart'),
staticScope: null),
library: Fixtures.library,
metadata: [],
typeParameters: [],
onType: Fixtures.myClassType,
);
final myGetter = MethodDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier: TestIdentifier(
id: RemoteInstance.uniqueId,
name: 'x',
kind: IdentifierKind.instanceMember,
uri: Uri.parse('a.dart'),
staticScope: null),
library: Fixtures.library,
metadata: [],
definingType: myExtension.identifier,
hasExternal: false,
hasStatic: false,
returnType: NamedTypeAnnotationImpl(
id: RemoteInstance.uniqueId,
isNullable: false,
identifier: intIdentifier,
typeArguments: []),
hasBody: true,
isGetter: true,
isOperator: false,
isSetter: false,
namedParameters: [],
positionalParameters: [],
typeParameters: []);
var results = [
MacroExecutionResultImpl(
diagnostics: [],
enumValueAugmentations: {},
extendsTypeAugmentations: {},
typeAugmentations: {
myExtension.identifier: [
DeclarationCode.fromParts([
intIdentifier,
' get ',
myGetter.identifier.name,
' => 1;\n'
]),
],
},
interfaceAugmentations: {},
mixinAugmentations: {},
newTypeNames: [],
libraryAugmentations: []),
];
var library = _TestExecutor().buildAugmentationLibrary(
Fixtures.library.uri,
results,
(Identifier i) => i == myExtension.identifier
? myExtension
: throw UnimplementedError(),
(Identifier i) => (i as TestIdentifier).resolved,
(OmittedTypeAnnotation i) =>
(i as TestOmittedTypeAnnotation).inferredType);
expect(library, equalsIgnoringWhitespace('''
augment library 'package:foo/bar.dart';
import 'dart:core' as prefix0;
augment extension MyExtension {
prefix0.int get x => 1;
}
'''));
});
test('copies keywords for classes', () async {
for (final hasKeywords in [true, false]) {
final clazz = ClassDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'MyClass'),
library: Fixtures.library,
metadata: [],
typeParameters: [],
interfaces: [],
hasAbstract: hasKeywords,
hasBase: hasKeywords,
hasExternal: hasKeywords,
hasFinal: hasKeywords,
hasInterface: hasKeywords,
hasMixin: hasKeywords,
hasSealed: hasKeywords,
mixins: [],
superclass: null);
var results = [
MacroExecutionResultImpl(
diagnostics: [],
enumValueAugmentations: {},
extendsTypeAugmentations: {},
typeAugmentations: {
clazz.identifier: [
DeclarationCode.fromParts(['']),
]
},
interfaceAugmentations: {},
mixinAugmentations: {},
newTypeNames: [],
libraryAugmentations: []),
];
var library = _TestExecutor().buildAugmentationLibrary(
Fixtures.library.uri,
results,
(Identifier i) =>
i == clazz.identifier ? clazz : throw UnimplementedError(),
(Identifier i) => (i as TestIdentifier).resolved,
(OmittedTypeAnnotation i) =>
(i as TestOmittedTypeAnnotation).inferredType);
final expectedKeywords = [
if (hasKeywords) ...[
'abstract',
'base',
'external',
'final',
'interface',
'mixin',
'sealed'
]
];
// Add extra space after, if we have keywords
if (expectedKeywords.isNotEmpty) expectedKeywords.add('');
expect(library, equalsIgnoringWhitespace('''
augment library 'package:foo/bar.dart';
augment ${expectedKeywords.join(' ')}class MyClass {
}
'''));
}
});
test('copies generic types and bounds', () async {
final clazz = ClassDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'MyClass'),
library: Fixtures.library,
metadata: [],
typeParameters: [
TypeParameterDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'T'),
library: Fixtures.library,
metadata: [],
bound: NamedTypeAnnotationImpl(
id: RemoteInstance.uniqueId,
isNullable: false,
identifier: objectIdentifier,
typeArguments: [])),
TypeParameterDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'S'),
library: Fixtures.library,
metadata: [],
bound: null),
],
interfaces: [],
hasAbstract: false,
hasBase: false,
hasExternal: false,
hasFinal: false,
hasInterface: false,
hasMixin: false,
hasSealed: false,
mixins: [],
superclass: null);
var results = [
MacroExecutionResultImpl(
diagnostics: [],
enumValueAugmentations: {},
extendsTypeAugmentations: {},
typeAugmentations: {
clazz.identifier: [
DeclarationCode.fromParts(['']),
]
},
interfaceAugmentations: {},
mixinAugmentations: {},
newTypeNames: [],
libraryAugmentations: []),
];
var library = _TestExecutor().buildAugmentationLibrary(
Fixtures.library.uri,
results,
(Identifier i) =>
i == clazz.identifier ? clazz : throw UnimplementedError(),
(Identifier i) => (i as TestIdentifier).resolved,
(OmittedTypeAnnotation i) =>
(i as TestOmittedTypeAnnotation).inferredType);
expect(library, equalsIgnoringWhitespace('''
augment library 'package:foo/bar.dart';
import 'dart:core' as prefix0;
augment class MyClass<T extends prefix0.Object, S> {
}
'''));
});
});
}
class _TestExecutor extends MacroExecutor
with AugmentationLibraryBuilder, Fake {}
-242
View File
@@ -1,242 +0,0 @@
// Copyright (c) 2023, 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:test/test.dart';
import 'package:_macros/src/executor/cast.dart';
void main() {
test("dynamic casts", () {
expect(Cast<dynamic>().cast(3), 3);
expect(Cast<dynamic>().cast("hello"), "hello");
});
test("int casts", () {
expect(Cast<int>().cast(3), 3);
expect(
() => Cast<int>().cast("hello"),
throwsA(isA<FailedCast>().having((e) => e.toString(), 'toString()',
'Failed cast: expected type int but got type String for: hello')));
});
test("double casts", () {
expect(Cast<double>().cast(3.1), 3.1);
expect(
() => Cast<double>().cast("hello"),
throwsA(isA<FailedCast>().having(
(e) => e.toString(),
'toString()',
'Failed cast: '
'expected type double but got type String for: hello')));
});
test("String casts", () {
expect(Cast<String>().cast("hello"), "hello");
expect(
() => Cast<String>().cast(3),
throwsA(isA<FailedCast>().having((e) => e.toString(), 'toString()',
'Failed cast: expected type String but got type int for: 3')));
});
test("bool casts", () {
expect(Cast<bool>().cast(true), true);
expect(
() => Cast<bool>().cast(3),
throwsA(isA<FailedCast>().having((e) => e.toString(), 'toString()',
'Failed cast: expected type bool but got type int for: 3')));
});
test("Casting empty lists", () {
var listOfInt = ListCast.from(Cast<int>());
var listOfString = ListCast.from(Cast<String>());
expect(listOfInt.cast(<dynamic>[]) is List<int>, isTrue);
expect(listOfInt.cast(<dynamic>[]), <int>[]);
expect(
() => listOfString.cast({}),
throwsA(isA<FailedCast>().having(
(e) => e.toString(),
'toString()',
'Failed cast: expected type List<String> but got '
'type _Map<dynamic, dynamic> for: {}')));
});
test("Casting non-empty lists", () {
var listOfInt = ListCast.from(Cast<int>());
var listOfString = ListCast.from(Cast<String>());
expect(listOfInt.cast(<num>[3]) is List<int>, isTrue);
expect(listOfInt.cast(<num>[3]), <int>[3]);
expect(
() => listOfString.cast(<num>[3]),
throwsA(isA<FailedCast>().having((e) => e.toString(), 'toString()',
'Failed cast: expected type String but got type int for: 3')));
});
test("Casting nested lists", () {
var listOfInt = ListCast.from(Cast<int>());
var listOfString = ListCast.from(Cast<String>());
var listOfListOfInt = ListCast.from(listOfInt);
var listOfListOfString = ListCast.from(listOfString);
expect(
listOfListOfInt.cast(<dynamic>[
<dynamic>[3]
]) is List<List<int>>,
isTrue);
expect(
listOfListOfInt.cast(<dynamic>[
<dynamic>[3]
]),
<List<int>>[
<int>[3]
]);
expect(
() => listOfListOfString.cast(<dynamic>[
<dynamic>[3]
]),
throwsA(isA<FailedCast>().having((e) => e.toString(), 'toString()',
'Failed cast: expected type String but got type int for: 3')));
});
test("Casting non-empty sets", () {
var setOfInt = SetCast.from(Cast<int>());
var setOfString = SetCast.from(Cast<String>());
expect(setOfInt.cast(<num>{3}) is Set<int>, isTrue);
expect(setOfInt.cast(<num>{3}), <int>{3});
expect(
() => setOfString.cast(<num>{3}),
throwsA(isA<FailedCast>().having((e) => e.toString(), 'toString()',
'Failed cast: expected type String but got type int for: 3')));
});
test("Casting nested sets", () {
var setOfInt = SetCast.from(Cast<int>());
var setOfString = SetCast.from(Cast<String>());
var setOfSetOfInt = SetCast.from(setOfInt);
var setOfSetOfString = SetCast.from(setOfString);
expect(
setOfSetOfInt.cast(<dynamic>{
<dynamic>{3}
}) is Set<Set<int>>,
isTrue);
expect(
setOfSetOfInt.cast(<dynamic>{
<dynamic>{3}
}),
<Set<int>>{
<int>{3}
});
expect(
() => setOfSetOfString.cast(<dynamic>{
<dynamic>{3}
}),
throwsA(isA<FailedCast>().having((e) => e.toString(), 'toString()',
'Failed cast: expected type String but got type int for: 3')));
});
test("Casting empty maps", () {
var mapOfStringToInt = MapCast.from(Cast<String>(), Cast<int>());
var mapOfStringToString = MapCast.from(Cast<String>(), Cast<String>());
expect(mapOfStringToInt.cast(<dynamic, dynamic>{}) is Map<String, int>,
isTrue);
expect(mapOfStringToInt.cast(<dynamic, dynamic>{}), <String, int>{});
expect(
() => mapOfStringToString.cast(<dynamic>[]),
throwsA(isA<FailedCast>().having(
(e) => e.toString(),
'toString()',
'Failed cast: expected type Map<String, String> but got type '
'List<dynamic> for: []')));
});
test("Casting non-empty maps", () {
var mapOfStringToInt = MapCast.from(Cast<String>(), Cast<int>());
var mapOfStringToString = MapCast.from(Cast<String>(), Cast<String>());
expect(
mapOfStringToInt.cast(<dynamic, dynamic>{"hello": 3})
is Map<String, int>,
isTrue);
expect(mapOfStringToInt.cast(<dynamic, dynamic>{"hello": 3}),
<String, int>{"hello": 3});
expect(() => mapOfStringToString.cast(<dynamic, dynamic>{"hello": 3}),
throwsA(isA<FailedCast>()));
expect(
() => mapOfStringToString.cast(<dynamic, dynamic>{3: "world"}),
throwsA(isA<FailedCast>().having((e) => e.toString(), 'toString()',
'Failed cast: expected type String but got type int for: 3')));
});
test("Casting nested maps", () {
var schema =
MapCast.from(Cast<String>(), MapCast.from(Cast<String>(), Cast<int>()));
expect(
schema.cast(<dynamic, dynamic>{
"hello": <dynamic, dynamic>{"hello": 3}
}) is Map<String, Map<String, int>>,
isTrue);
expect(
schema.cast(<dynamic, dynamic>{
"hello": <dynamic, dynamic>{"hello": 3}
}),
<String, Map<String, int>>{
"hello": <String, int>{"hello": 3}
});
expect(
() => schema.cast(<dynamic, dynamic>{
"hello": <dynamic, dynamic>{3: "hello"}
}),
throwsA(isA<FailedCast>().having((e) => e.toString(), 'toString()',
'Failed cast: expected type String but got type int for: 3')));
});
test("Casting nested list/maps/sets", () {
var schema =
MapCast.from(Cast<String>(), ListCast.from(SetCast.from(Cast<int>())));
expect(
schema.cast(<dynamic, dynamic>{
"hello": <dynamic>[
{3}
]
}) is Map<String, List<Set<int>>>,
isTrue);
expect(
schema.cast(<dynamic, dynamic>{
"hello": <dynamic>[
{3}
]
}),
<String, List<Set<int>>>{
"hello": <Set<int>>[
{3}
]
});
});
test("nullable cast", () {
expect(Cast<int>().nullable.cast(null), null);
expect(Cast<int>().nullable.cast(3), 3);
expect(ListCast.from(Cast<int>()).nullable.cast(<num>[3]), <int>[3]);
expect(ListCast.from(Cast<int>().nullable).cast(<num?>[3, null]),
<int?>[3, null]);
expect(
() => Cast<int>().nullable.cast(2.0),
throwsA(isA<FailedCast>().having((e) => e.toString(), 'toString()',
'Failed cast: expected type int but got type double for: 2.0')));
expect(
() => ListCast.from(Cast<int>()).nullable.cast([2.0]),
throwsA(isA<FailedCast>().having((e) => e.toString(), 'toString()',
'Failed cast: expected type int but got type double for: 2.0')));
});
}
File diff suppressed because it is too large Load Diff
@@ -1,269 +0,0 @@
// 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:_macros/src/api.dart';
import 'package:_macros/src/executor.dart';
import 'package:_macros/src/executor/remote_instance.dart';
import 'package:_macros/src/executor/response_impls.dart';
import 'package:test/fake.dart';
import 'package:test/test.dart';
void main() {
group('MacroInstanceIdentifierImpl', () {
test('shouldExecute', () {
for (var kind in DeclarationKind.values) {
for (var phase in Phase.values) {
var instance = instancesByKindAndPhase[kind]?[phase];
if (instance == null) continue;
for (var otherKind in DeclarationKind.values) {
for (var otherPhase in Phase.values) {
var expected = false;
if (otherPhase == phase) {
if (kind == otherKind) {
expected = true;
} else if (kind == DeclarationKind.function &&
otherKind == DeclarationKind.method) {
expected = true;
} else if (kind == DeclarationKind.variable &&
otherKind == DeclarationKind.field) {
expected = true;
}
}
expect(instance.shouldExecute(otherKind, otherPhase), expected,
reason: 'Expected a $kind macro in $phase to '
'${expected ? '' : 'not '}be applied to a $otherKind '
'in $otherPhase');
}
}
}
}
});
test('supportsDeclarationKind', () {
for (var kind in DeclarationKind.values) {
for (var phase in Phase.values) {
var instance = instancesByKindAndPhase[kind]?[phase];
if (instance == null) continue;
for (var otherKind in DeclarationKind.values) {
var expected = false;
if (kind == otherKind) {
expected = true;
} else if (kind == DeclarationKind.function &&
otherKind == DeclarationKind.method) {
expected = true;
} else if (kind == DeclarationKind.variable &&
otherKind == DeclarationKind.field) {
expected = true;
}
expect(instance.supportsDeclarationKind(otherKind), expected,
reason: 'Expected a $kind macro to ${expected ? '' : 'not '}'
'support a $otherKind');
}
}
}
});
});
}
final Map<DeclarationKind, Map<Phase, MacroInstanceIdentifierImpl>>
instancesByKindAndPhase = {
DeclarationKind.classType: {
Phase.types: MacroInstanceIdentifierImpl(
FakeClassTypesMacro(), RemoteInstance.uniqueId),
Phase.declarations: MacroInstanceIdentifierImpl(
FakeClassDeclarationsMacro(), RemoteInstance.uniqueId),
Phase.definitions: MacroInstanceIdentifierImpl(
FakeClassDefinitionMacro(), RemoteInstance.uniqueId),
},
DeclarationKind.constructor: {
Phase.types: MacroInstanceIdentifierImpl(
FakeConstructorTypesMacro(), RemoteInstance.uniqueId),
Phase.declarations: MacroInstanceIdentifierImpl(
FakeConstructorDeclarationsMacro(), RemoteInstance.uniqueId),
Phase.definitions: MacroInstanceIdentifierImpl(
FakeConstructorDefinitionMacro(), RemoteInstance.uniqueId),
},
DeclarationKind.enumType: {
Phase.types: MacroInstanceIdentifierImpl(
FakeEnumTypesMacro(), RemoteInstance.uniqueId),
Phase.declarations: MacroInstanceIdentifierImpl(
FakeEnumDeclarationsMacro(), RemoteInstance.uniqueId),
Phase.definitions: MacroInstanceIdentifierImpl(
FakeEnumDefinitionMacro(), RemoteInstance.uniqueId),
},
DeclarationKind.enumValue: {
Phase.types: MacroInstanceIdentifierImpl(
FakeEnumValueTypesMacro(), RemoteInstance.uniqueId),
Phase.declarations: MacroInstanceIdentifierImpl(
FakeEnumValueDeclarationsMacro(), RemoteInstance.uniqueId),
Phase.definitions: MacroInstanceIdentifierImpl(
FakeEnumValueDefinitionMacro(), RemoteInstance.uniqueId),
},
DeclarationKind.extension: {
Phase.types: MacroInstanceIdentifierImpl(
FakeExtensionTypesMacro(), RemoteInstance.uniqueId),
Phase.declarations: MacroInstanceIdentifierImpl(
FakeExtensionDeclarationsMacro(), RemoteInstance.uniqueId),
Phase.definitions: MacroInstanceIdentifierImpl(
FakeExtensionDefinitionMacro(), RemoteInstance.uniqueId),
},
DeclarationKind.extensionType: {
Phase.types: MacroInstanceIdentifierImpl(
FakeExtensionTypeTypesMacro(), RemoteInstance.uniqueId),
Phase.declarations: MacroInstanceIdentifierImpl(
FakeExtensionTypeDeclarationsMacro(), RemoteInstance.uniqueId),
Phase.definitions: MacroInstanceIdentifierImpl(
FakeExtensionTypeDefinitionMacro(), RemoteInstance.uniqueId),
},
DeclarationKind.field: {
Phase.types: MacroInstanceIdentifierImpl(
FakeFieldTypesMacro(), RemoteInstance.uniqueId),
Phase.declarations: MacroInstanceIdentifierImpl(
FakeFieldDeclarationsMacro(), RemoteInstance.uniqueId),
Phase.definitions: MacroInstanceIdentifierImpl(
FakeFieldDefinitionMacro(), RemoteInstance.uniqueId),
},
DeclarationKind.function: {
Phase.types: MacroInstanceIdentifierImpl(
FakeFunctionTypesMacro(), RemoteInstance.uniqueId),
Phase.declarations: MacroInstanceIdentifierImpl(
FakeFunctionDeclarationsMacro(), RemoteInstance.uniqueId),
Phase.definitions: MacroInstanceIdentifierImpl(
FakeFunctionDefinitionMacro(), RemoteInstance.uniqueId),
},
DeclarationKind.library: {
Phase.types: MacroInstanceIdentifierImpl(
FakeLibraryTypesMacro(), RemoteInstance.uniqueId),
Phase.declarations: MacroInstanceIdentifierImpl(
FakeLibraryDeclarationsMacro(), RemoteInstance.uniqueId),
Phase.definitions: MacroInstanceIdentifierImpl(
FakeLibraryDefinitionMacro(), RemoteInstance.uniqueId),
},
DeclarationKind.method: {
Phase.types: MacroInstanceIdentifierImpl(
FakeMethodTypesMacro(), RemoteInstance.uniqueId),
Phase.declarations: MacroInstanceIdentifierImpl(
FakeMethodDeclarationsMacro(), RemoteInstance.uniqueId),
Phase.definitions: MacroInstanceIdentifierImpl(
FakeMethodDefinitionMacro(), RemoteInstance.uniqueId),
},
DeclarationKind.mixinType: {
Phase.types: MacroInstanceIdentifierImpl(
FakeMixinTypesMacro(), RemoteInstance.uniqueId),
Phase.declarations: MacroInstanceIdentifierImpl(
FakeMixinDeclarationsMacro(), RemoteInstance.uniqueId),
Phase.definitions: MacroInstanceIdentifierImpl(
FakeMixinDefinitionMacro(), RemoteInstance.uniqueId),
},
DeclarationKind.typeAlias: {
Phase.types: MacroInstanceIdentifierImpl(
FakeTypeAliasTypesMacro(), RemoteInstance.uniqueId),
Phase.declarations: MacroInstanceIdentifierImpl(
FakeTypeAliasDeclarationsMacro(), RemoteInstance.uniqueId),
},
DeclarationKind.variable: {
Phase.types: MacroInstanceIdentifierImpl(
FakeVariableTypesMacro(), RemoteInstance.uniqueId),
Phase.declarations: MacroInstanceIdentifierImpl(
FakeVariableDeclarationsMacro(), RemoteInstance.uniqueId),
Phase.definitions: MacroInstanceIdentifierImpl(
FakeVariableDefinitionMacro(), RemoteInstance.uniqueId),
},
};
class FakeClassTypesMacro extends Fake implements ClassTypesMacro {}
class FakeClassDeclarationsMacro extends Fake
implements ClassDeclarationsMacro {}
class FakeClassDefinitionMacro extends Fake implements ClassDefinitionMacro {}
class FakeConstructorTypesMacro extends Fake implements ConstructorTypesMacro {}
class FakeConstructorDeclarationsMacro extends Fake
implements ConstructorDeclarationsMacro {}
class FakeConstructorDefinitionMacro extends Fake
implements ConstructorDefinitionMacro {}
class FakeFieldTypesMacro extends Fake implements FieldTypesMacro {}
class FakeFieldDeclarationsMacro extends Fake
implements FieldDeclarationsMacro {}
class FakeFieldDefinitionMacro extends Fake implements FieldDefinitionMacro {}
class FakeFunctionTypesMacro extends Fake implements FunctionTypesMacro {}
class FakeFunctionDeclarationsMacro extends Fake
implements FunctionDeclarationsMacro {}
class FakeFunctionDefinitionMacro extends Fake
implements FunctionDefinitionMacro {}
class FakeMethodTypesMacro extends Fake implements MethodTypesMacro {}
class FakeMethodDeclarationsMacro extends Fake
implements MethodDeclarationsMacro {}
class FakeMethodDefinitionMacro extends Fake implements MethodDefinitionMacro {}
class FakeVariableTypesMacro extends Fake implements VariableTypesMacro {}
class FakeVariableDeclarationsMacro extends Fake
implements VariableDeclarationsMacro {}
class FakeVariableDefinitionMacro extends Fake
implements VariableDefinitionMacro {}
class FakeMixinTypesMacro extends Fake implements MixinTypesMacro {}
class FakeMixinDeclarationsMacro extends Fake
implements MixinDeclarationsMacro {}
class FakeMixinDefinitionMacro extends Fake implements MixinDefinitionMacro {}
class FakeEnumTypesMacro extends Fake implements EnumTypesMacro {}
class FakeEnumDeclarationsMacro extends Fake implements EnumDeclarationsMacro {}
class FakeEnumDefinitionMacro extends Fake implements EnumDefinitionMacro {}
class FakeEnumValueTypesMacro extends Fake implements EnumValueTypesMacro {}
class FakeEnumValueDeclarationsMacro extends Fake
implements EnumValueDeclarationsMacro {}
class FakeEnumValueDefinitionMacro extends Fake
implements EnumValueDefinitionMacro {}
class FakeExtensionTypesMacro extends Fake implements ExtensionTypesMacro {}
class FakeExtensionDeclarationsMacro extends Fake
implements ExtensionDeclarationsMacro {}
class FakeExtensionDefinitionMacro extends Fake
implements ExtensionDefinitionMacro {}
class FakeExtensionTypeTypesMacro extends Fake
implements ExtensionTypeTypesMacro {}
class FakeExtensionTypeDeclarationsMacro extends Fake
implements ExtensionTypeDeclarationsMacro {}
class FakeExtensionTypeDefinitionMacro extends Fake
implements ExtensionTypeDefinitionMacro {}
class FakeLibraryTypesMacro extends Fake implements LibraryTypesMacro {}
class FakeLibraryDeclarationsMacro extends Fake
implements LibraryDeclarationsMacro {}
class FakeLibraryDefinitionMacro extends Fake
implements LibraryDefinitionMacro {}
class FakeTypeAliasTypesMacro extends Fake implements TypeAliasTypesMacro {}
class FakeTypeAliasDeclarationsMacro extends Fake
implements TypeAliasDeclarationsMacro {}
@@ -1,779 +0,0 @@
// 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 'dart:math';
import 'package:_macros/src/api.dart';
import 'package:_macros/src/executor.dart';
import 'package:_macros/src/executor/exception_impls.dart';
import 'package:_macros/src/executor/introspection_impls.dart';
import 'package:_macros/src/executor/remote_instance.dart';
import 'package:_macros/src/executor/serialization.dart';
import 'package:test/test.dart';
import '../util.dart';
void main() {
// We randomize fields which should make the tests more likely to catch issues
// related to serialization ordering.
final seed = Random().nextInt(1000);
print('Nondeterministic test ran with seed: $seed, change to this seed to '
'repro.');
final rand = Random(seed);
for (var mode in [SerializationMode.json, SerializationMode.byteData]) {
test('$mode can serialize and deserialize basic data', () {
withSerializationMode(mode, () {
var serializer = serializerFactory();
serializer
..addInt(0)
..addInt(1)
..addInt(0xff)
..addInt(0xffff)
..addInt(0xffffffff)
..addInt(0xffffffffffffffff)
..addInt(-1)
..addInt(-0x80)
..addInt(-0x8000)
..addInt(-0x80000000)
..addInt(-0x8000000000000000)
..addNullableInt(null)
..addString('hello')
..addString('') // Requires a two byte string
..addString('𐐷') // Requires two, 16 bit code units
..addNullableString(null)
..startList()
..addBool(true)
..startList()
..addNull()
..endList()
..addNullableBool(null)
..endList()
..addDouble(1.0)
..startList()
..endList();
var deserializer = deserializerFactory(serializer.result);
expect(deserializer.moveNext(), true);
expect(deserializer.expectInt(), 0);
expect(deserializer.moveNext(), true);
expect(deserializer.expectInt(), 1);
expect(deserializer.moveNext(), true);
expect(deserializer.expectInt(), 0xff);
expect(deserializer.moveNext(), true);
expect(deserializer.expectInt(), 0xffff);
expect(deserializer.moveNext(), true);
expect(deserializer.expectInt(), 0xffffffff);
expect(deserializer.moveNext(), true);
expect(deserializer.expectInt(), 0xffffffffffffffff);
expect(deserializer.moveNext(), true);
expect(deserializer.expectInt(), -1);
expect(deserializer.moveNext(), true);
expect(deserializer.expectInt(), -0x80);
expect(deserializer.moveNext(), true);
expect(deserializer.expectInt(), -0x8000);
expect(deserializer.moveNext(), true);
expect(deserializer.expectInt(), -0x80000000);
expect(deserializer.moveNext(), true);
expect(deserializer.expectInt(), -0x8000000000000000);
expect(deserializer.moveNext(), true);
expect(deserializer.expectNullableInt(), null);
expect(deserializer.moveNext(), true);
expect(deserializer.expectString(), 'hello');
expect(deserializer.moveNext(), true);
expect(deserializer.expectString(), '');
expect(deserializer.moveNext(), true);
expect(deserializer.expectString(), '𐐷');
expect(deserializer.moveNext(), true);
expect(deserializer.expectNullableString(), null);
expect(deserializer.moveNext(), true);
deserializer.expectList();
expect(deserializer.moveNext(), true);
expect(deserializer.expectBool(), true);
expect(deserializer.moveNext(), true);
deserializer.expectList();
expect(deserializer.moveNext(), true);
expect(deserializer.checkNull(), true);
expect(deserializer.moveNext(), false);
expect(deserializer.moveNext(), true);
expect(deserializer.expectNullableBool(), null);
expect(deserializer.moveNext(), false);
// Have to move the parent again to advance it past the list entry.
expect(deserializer.moveNext(), true);
expect(deserializer.expectDouble(), 1.0);
expect(deserializer.moveNext(), true);
deserializer.expectList();
expect(deserializer.moveNext(), false);
expect(deserializer.moveNext(), false);
});
});
}
for (var mode in [SerializationMode.byteData, SerializationMode.json]) {
test('remote instances in $mode', () async {
var string = NamedTypeAnnotationImpl(
id: RemoteInstance.uniqueId,
isNullable: false,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'String'),
typeArguments: const []);
var foo = NamedTypeAnnotationImpl(
id: RemoteInstance.uniqueId,
isNullable: false,
identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Foo'),
typeArguments: [string]);
withSerializationMode(mode, () {
final int zoneId = newRemoteInstanceZone();
withRemoteInstanceZone(zoneId, () {
var serializer = serializerFactory();
foo.serialize(serializer);
// This is a fake client, we don't want to actually share the cache,
// so we negate the zone id and use that.
var response = roundTrip(serializer.result, -zoneId);
var deserializer = deserializerFactory(response);
var instance = RemoteInstance.deserialize(deserializer);
expect(instance, foo);
});
});
});
}
group('declarations', () {
final barType = NamedTypeAnnotationImpl(
id: RemoteInstance.uniqueId,
isNullable: rand.nextBool(),
identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Bar'),
typeArguments: []);
final fooType = NamedTypeAnnotationImpl(
id: RemoteInstance.uniqueId,
isNullable: rand.nextBool(),
identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Foo'),
typeArguments: [barType]);
for (var mode in [SerializationMode.byteData, SerializationMode.json]) {
group('with mode $mode', () {
test('NamedTypeAnnotation', () {
expectSerializationEquality<TypeAnnotationImpl>(
fooType, mode, RemoteInstance.deserialize);
});
final fooNamedParam = FormalParameterDeclarationImpl(
id: RemoteInstance.uniqueId,
isNamed: rand.nextBool(),
isRequired: rand.nextBool(),
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'foo'),
library: Fixtures.library,
metadata: [],
style: ParameterStyle.values[rand.nextInt(3)],
type: fooType);
final fooNamedFunctionTypeParam = FormalParameterImpl(
id: RemoteInstance.uniqueId,
isNamed: rand.nextBool(),
isRequired: rand.nextBool(),
metadata: [],
name: 'foo',
type: fooType);
final barPositionalParam = FormalParameterDeclarationImpl(
id: RemoteInstance.uniqueId,
isNamed: rand.nextBool(),
isRequired: rand.nextBool(),
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'bar'),
library: Fixtures.library,
metadata: [],
style: ParameterStyle.values[rand.nextInt(3)],
type: barType);
final barPositionalFunctionTypeParam = FormalParameterImpl(
id: RemoteInstance.uniqueId,
isNamed: rand.nextBool(),
isRequired: rand.nextBool(),
metadata: [],
name: 'bar',
type: fooType);
final unnamedFunctionTypeParam = FormalParameterImpl(
id: RemoteInstance.uniqueId,
isNamed: rand.nextBool(),
isRequired: rand.nextBool(),
metadata: [],
name: rand.nextBool() ? null : 'zip',
type: fooType);
final zapTypeParam = TypeParameterDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Zap'),
library: Fixtures.library,
metadata: [],
bound: barType);
// Transitively tests `TypeParameterDeclaration` and
// `ParameterDeclaration`.
test('FunctionTypeAnnotation', () {
var functionType = FunctionTypeAnnotationImpl(
id: RemoteInstance.uniqueId,
isNullable: rand.nextBool(),
namedParameters: [
fooNamedFunctionTypeParam,
unnamedFunctionTypeParam
],
positionalParameters: [barPositionalFunctionTypeParam],
returnType: fooType,
typeParameters: [
TypeParameterImpl(
id: RemoteInstance.uniqueId,
metadata: [],
name: 'Zip',
bound: barType)
],
);
expectSerializationEquality<TypeAnnotationImpl>(
functionType, mode, RemoteInstance.deserialize);
});
test('FunctionDeclaration', () {
var function = FunctionDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'name'),
library: Fixtures.library,
metadata: [],
hasBody: rand.nextBool(),
hasExternal: rand.nextBool(),
isGetter: rand.nextBool(),
isOperator: rand.nextBool(),
isSetter: rand.nextBool(),
namedParameters: [],
positionalParameters: [],
returnType: fooType,
typeParameters: []);
expectSerializationEquality<DeclarationImpl>(
function, mode, RemoteInstance.deserialize);
});
test('MethodDeclaration', () {
var method = MethodDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'zorp'),
library: Fixtures.library,
metadata: [],
hasBody: rand.nextBool(),
hasExternal: rand.nextBool(),
isGetter: rand.nextBool(),
isOperator: rand.nextBool(),
isSetter: rand.nextBool(),
namedParameters: [fooNamedParam],
positionalParameters: [barPositionalParam],
returnType: fooType,
typeParameters: [zapTypeParam],
definingType: fooType.identifier,
hasStatic: rand.nextBool());
expectSerializationEquality<DeclarationImpl>(
method, mode, RemoteInstance.deserialize);
});
test('ConstructorDeclaration', () {
var constructor = ConstructorDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'new'),
library: Fixtures.library,
metadata: [],
hasBody: rand.nextBool(),
hasExternal: rand.nextBool(),
namedParameters: [fooNamedParam],
positionalParameters: [barPositionalParam],
returnType: fooType,
typeParameters: [zapTypeParam],
definingType: fooType.identifier,
isConst: rand.nextBool(),
isFactory: rand.nextBool(),
);
expectSerializationEquality<DeclarationImpl>(
constructor, mode, RemoteInstance.deserialize);
});
test('VariableDeclaration', () {
var bar = VariableDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'bar'),
library: Fixtures.library,
metadata: [],
hasConst: rand.nextBool(),
hasExternal: rand.nextBool(),
hasFinal: rand.nextBool(),
hasInitializer: rand.nextBool(),
hasLate: rand.nextBool(),
type: barType,
);
expectSerializationEquality<DeclarationImpl>(
bar, mode, RemoteInstance.deserialize);
});
test('FieldDeclaration', () {
var bar = FieldDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'bar'),
library: Fixtures.library,
metadata: [],
hasAbstract: rand.nextBool(),
hasConst: rand.nextBool(),
hasExternal: rand.nextBool(),
hasFinal: rand.nextBool(),
hasInitializer: rand.nextBool(),
hasLate: rand.nextBool(),
type: barType,
definingType: fooType.identifier,
hasStatic: rand.nextBool(),
);
expectSerializationEquality<DeclarationImpl>(
bar, mode, RemoteInstance.deserialize);
});
var objectType = NamedTypeAnnotationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Object'),
isNullable: false,
typeArguments: [],
);
var serializableType = NamedTypeAnnotationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Serializable'),
isNullable: rand.nextBool(),
typeArguments: [],
);
test('ClassDeclaration', () {
var fooClass = ClassDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Foo'),
library: Fixtures.library,
metadata: [],
interfaces: [barType],
hasAbstract: rand.nextBool(),
hasBase: rand.nextBool(),
hasExternal: rand.nextBool(),
hasFinal: rand.nextBool(),
hasInterface: rand.nextBool(),
hasMixin: rand.nextBool(),
hasSealed: rand.nextBool(),
mixins: [serializableType],
superclass: objectType,
typeParameters: [zapTypeParam],
);
expectSerializationEquality<DeclarationImpl>(
fooClass, mode, RemoteInstance.deserialize);
});
test('EnumDeclaration', () {
var fooEnum = EnumDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'MyEnum'),
library: Fixtures.library,
metadata: [],
interfaces: [barType],
mixins: [serializableType],
typeParameters: [zapTypeParam],
);
expectSerializationEquality<DeclarationImpl>(
fooEnum, mode, RemoteInstance.deserialize);
});
test('EnumValueDeclaration', () {
var entry = EnumValueDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'a'),
library: Fixtures.library,
metadata: [],
definingEnum:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'MyEnum'),
);
expectSerializationEquality<DeclarationImpl>(
entry, mode, RemoteInstance.deserialize);
});
test('ExtensionDeclaration', () {
var extension = ExtensionDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier: IdentifierImpl(
id: RemoteInstance.uniqueId, name: 'MyExtension'),
library: Fixtures.library,
metadata: [],
typeParameters: [],
onType: Fixtures.myClassType);
expectSerializationEquality<DeclarationImpl>(
extension, mode, RemoteInstance.deserialize);
});
test('ExtensionTypeDeclaration', () {
var extensionType = ExtensionTypeDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier: IdentifierImpl(
id: RemoteInstance.uniqueId, name: 'MyExtensionType'),
library: Fixtures.library,
metadata: [],
typeParameters: [],
representationType: Fixtures.myClassType);
expectSerializationEquality<DeclarationImpl>(
extensionType, mode, RemoteInstance.deserialize);
});
test('MixinDeclaration', () {
var mixin = MixinDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'MyMixin'),
library: Fixtures.library,
metadata: [],
hasBase: rand.nextBool(),
interfaces: [barType],
superclassConstraints: [serializableType],
typeParameters: [zapTypeParam],
);
expectSerializationEquality<DeclarationImpl>(
mixin, mode, RemoteInstance.deserialize);
});
test('TypeAliasDeclaration', () {
var typeAlias = TypeAliasDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'FooOfBar'),
library: Fixtures.library,
metadata: [],
typeParameters: [zapTypeParam],
aliasedType: NamedTypeAnnotationImpl(
id: RemoteInstance.uniqueId,
isNullable: rand.nextBool(),
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Foo'),
typeArguments: [barType]),
);
expectSerializationEquality<DeclarationImpl>(
typeAlias, mode, RemoteInstance.deserialize);
});
/// Transitively tests [RecordField]
test('RecordTypeAnnotation', () {
var recordType = RecordTypeAnnotationImpl(
id: RemoteInstance.uniqueId,
isNullable: rand.nextBool(),
namedFields: [
RecordFieldImpl(
id: RemoteInstance.uniqueId,
name: 'hello',
type: barType,
),
],
positionalFields: [
RecordFieldImpl(
id: RemoteInstance.uniqueId,
name: rand.nextBool() ? null : 'zoiks',
type: fooType,
),
],
);
expectSerializationEquality<TypeAnnotationImpl>(
recordType, mode, RemoteInstance.deserialize);
});
});
}
});
group('Arguments', () {
test('can create properly typed collections', () {
withSerializationMode(SerializationMode.json, () {
final parsed = Arguments.deserialize(deserializerFactory([
// positional args
[
// int
ArgumentKind.int.index,
1,
// List<int>
ArgumentKind.list.index,
[ArgumentKind.int.index],
[
ArgumentKind.int.index,
1,
ArgumentKind.int.index,
2,
ArgumentKind.int.index,
3,
],
// List<Set<String>>
ArgumentKind.list.index,
[ArgumentKind.set.index, ArgumentKind.string.index],
[
// Set<String>
ArgumentKind.set.index,
[ArgumentKind.string.index],
[
ArgumentKind.string.index,
'hello',
ArgumentKind.string.index,
'world',
]
],
// Map<int, List<String>>
ArgumentKind.map.index,
[
ArgumentKind.int.index,
ArgumentKind.nullable.index,
ArgumentKind.list.index,
ArgumentKind.string.index
],
[
// key: int
ArgumentKind.int.index,
4,
// value: List<String>
ArgumentKind.list.index,
[ArgumentKind.string.index],
[
ArgumentKind.string.index,
'zip',
],
ArgumentKind.int.index,
5,
ArgumentKind.nil.index,
]
],
// named args
[],
]));
expect(parsed.positional.length, 4);
expect(parsed.positional.first.value, 1);
expect(parsed.positional[1].value, [1, 2, 3]);
expect(parsed.positional[1].value, isA<List<int>>());
expect(parsed.positional[2].value, [
{'hello', 'world'}
]);
expect(parsed.positional[2].value, isA<List<Set<String>>>());
expect(
parsed.positional[3].value,
{
4: ['zip'],
5: null,
},
);
expect(parsed.positional[3].value, isA<Map<int, List<String>?>>());
});
});
group('can be serialized and deserialized', () {
for (var mode in [SerializationMode.byteData, SerializationMode.json]) {
test('with mode $mode', () {
final arguments = Arguments([
MapArgument({
StringArgument('hello'): ListArgument(
[BoolArgument(rand.nextBool()), NullArgument()],
[ArgumentKind.nullable, ArgumentKind.bool]),
}, [
ArgumentKind.string,
ArgumentKind.list,
ArgumentKind.nullable,
ArgumentKind.bool
]),
CodeArgument(ExpressionCode.fromParts([
'1 + ',
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'a')
])),
ListArgument([
TypeAnnotationArgument(Fixtures.myClassType),
TypeAnnotationArgument(Fixtures.myEnumType),
TypeAnnotationArgument(NamedTypeAnnotationImpl(
id: RemoteInstance.uniqueId,
isNullable: false,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'List'),
typeArguments: [Fixtures.stringType])),
], [
ArgumentKind.typeAnnotation
])
], {
'a': SetArgument([
MapArgument({
IntArgument(1): StringArgument('1'),
}, [
ArgumentKind.int,
ArgumentKind.string
])
], [
ArgumentKind.map,
ArgumentKind.int,
ArgumentKind.string
])
});
expectSerializationEquality(arguments, mode, Arguments.deserialize);
});
}
});
});
group('Exceptions', () {
group('can be serialized and deserialized', () {
for (var mode in [SerializationMode.byteData, SerializationMode.json]) {
test('with mode $mode', () {
final exception = UnexpectedMacroExceptionImpl('something happened',
stackTrace: 'here');
expectSerializationEquality<UnexpectedMacroExceptionImpl>(
exception, mode, RemoteInstance.deserialize);
});
}
});
});
group('metadata annotations can be serialized and deserialized', () {
for (var mode in [SerializationMode.byteData, SerializationMode.json]) {
group('with mode $mode', () {
test('identifiers', () {
final identifierMetadata = IdentifierMetadataAnnotationImpl(
id: RemoteInstance.uniqueId,
identifier: IdentifierImpl(
id: RemoteInstance.uniqueId, name: 'singleton'));
expectSerializationEquality<IdentifierMetadataAnnotationImpl>(
identifierMetadata, mode, RemoteInstance.deserialize);
});
test('constructor invocations', () {
final constructorMetadata = ConstructorMetadataAnnotationImpl(
id: RemoteInstance.uniqueId,
type: NamedTypeAnnotationImpl(
id: RemoteInstance.uniqueId,
identifier: IdentifierImpl(
id: RemoteInstance.uniqueId, name: 'Singleton'),
isNullable: false,
typeArguments: []),
constructor:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'someName'),
positionalArguments: [
ExpressionCode.fromString("'foo'"),
ExpressionCode.fromString('12'),
],
namedArguments: {
'bar': ExpressionCode.fromString("'bar'"),
'foobar': ExpressionCode.fromString('13'),
});
expectSerializationEquality<ConstructorMetadataAnnotationImpl>(
constructorMetadata, mode, RemoteInstance.deserialize);
});
});
}
});
group('static types can be serialized and deserialized', () {
for (var mode in [SerializationMode.byteData, SerializationMode.json]) {
group('with mode $mode', () {
test('named static type', () async {
final staticType = NamedStaticTypeImpl(
RemoteInstance.uniqueId,
declaration: ClassDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Foo'),
library: Fixtures.library,
metadata: [],
interfaces: [],
hasAbstract: false,
hasBase: false,
hasExternal: false,
hasFinal: false,
hasInterface: false,
hasMixin: false,
hasSealed: false,
mixins: [],
superclass: null,
typeParameters: [
TypeParameterDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'T'),
library: Fixtures.library,
metadata: const [],
bound: null,
),
],
),
typeArguments: [
NamedStaticTypeImpl(
RemoteInstance.uniqueId,
declaration: Fixtures.stringClass,
typeArguments: const [],
),
],
);
expectSerializationEquality<NamedStaticTypeImpl>(
staticType, mode, RemoteInstance.deserialize);
});
});
}
});
}
/// Serializes [serializable] in server mode, then deserializes it in client
/// mode, and checks that all the fields are the same.
void expectSerializationEquality<T extends Serializable>(T serializable,
SerializationMode mode, T Function(Deserializer deserializer) deserialize) {
withSerializationMode(mode, () {
late Object? serialized;
final int zoneId = newRemoteInstanceZone();
withRemoteInstanceZone(zoneId, () {
var serializer = serializerFactory();
serializable.serialize(serializer);
serialized = serializer.result;
});
// This is a fake client, we don't want to actually share the cache,
// so we negate the zone id and use that.
withRemoteInstanceZone(-zoneId, () {
var deserializer = deserializerFactory(serialized);
var deserialized = deserialize(deserializer);
expect(
serializable,
switch (deserialized) {
Declaration() => deepEqualsDeclaration(deserialized as Declaration),
TypeAnnotation() =>
deepEqualsTypeAnnotation(deserialized as TypeAnnotation),
Arguments() => deepEqualsArguments(deserialized),
MacroExceptionImpl() => deepEqualsMacroException(deserialized),
MetadataAnnotation() =>
deepEqualsMetadataAnnotation(deserialized as MetadataAnnotation),
NamedStaticTypeImpl() =>
deepEqualsStaticType(deserialized as NamedStaticTypeImpl),
_ =>
throw UnsupportedError('Unsupported object type $deserialized'),
});
}, createIfMissing: true);
});
}
/// Deserializes [serialized] in its own remote instance cache and sends it
/// back.
Object? roundTrip(Object? serialized, int zoneId) {
return withRemoteInstanceZone(zoneId, () {
var deserializer = deserializerFactory(serialized);
var instance = RemoteInstance.deserialize(deserializer) as Serializable;
var serializer = serializerFactory();
instance.serialize(serializer);
return serializer.result;
}, createIfMissing: true);
}
-878
View File
@@ -1,878 +0,0 @@
// 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 'dart:async';
import 'package:_macros/src/api.dart';
/// A macro for testing diagnostics reporting, including error handling.
class DiagnosticMacro implements ClassTypesMacro {
@override
FutureOr<void> buildTypesForClass(
ClassDeclaration clazz, ClassTypeBuilder builder) {
builder.report(Diagnostic(
DiagnosticMessage('superclass',
target: clazz.superclass!.asDiagnosticTarget),
Severity.info,
contextMessages: [
DiagnosticMessage(
'interface',
target: clazz.interfaces.single.asDiagnosticTarget,
),
],
correctionMessage: 'correct me!'));
// Test general error handling also
throw 'I threw an error!';
}
}
/// A very simple macro that augments any declaration it is given, usually
/// adding print statements and inlining values from the declaration object
/// for comparison with expected values in tests.
///
/// When applied to [MethodDeclaration]s there is some extra work that happens
/// to validate the introspection APIs work as expected.
class SimpleMacro
implements
ClassTypesMacro,
ClassDeclarationsMacro,
ClassDefinitionMacro,
ConstructorTypesMacro,
ConstructorDeclarationsMacro,
ConstructorDefinitionMacro,
EnumTypesMacro,
EnumDeclarationsMacro,
EnumDefinitionMacro,
EnumValueTypesMacro,
EnumValueDeclarationsMacro,
EnumValueDefinitionMacro,
ExtensionTypesMacro,
ExtensionDeclarationsMacro,
ExtensionDefinitionMacro,
ExtensionTypeTypesMacro,
ExtensionTypeDeclarationsMacro,
ExtensionTypeDefinitionMacro,
FieldTypesMacro,
FieldDeclarationsMacro,
FieldDefinitionMacro,
FunctionTypesMacro,
FunctionDeclarationsMacro,
FunctionDefinitionMacro,
LibraryTypesMacro,
LibraryDeclarationsMacro,
LibraryDefinitionMacro,
MethodTypesMacro,
MethodDeclarationsMacro,
MethodDefinitionMacro,
MixinTypesMacro,
MixinDeclarationsMacro,
MixinDefinitionMacro,
TypeAliasTypesMacro,
TypeAliasDeclarationsMacro,
VariableTypesMacro,
VariableDeclarationsMacro,
VariableDefinitionMacro {
final bool? myBool;
final int? myInt;
final double? myDouble;
final Set? mySet;
final List? myList;
final Map? myMap;
final String? myString;
SimpleMacro([this.myInt])
: myBool = null,
myDouble = null,
mySet = null,
myList = null,
myMap = null,
myString = null;
SimpleMacro.named(
{required this.myBool,
required this.myDouble,
required this.myInt,
required this.mySet,
required this.myList,
required this.myMap,
required this.myString});
@override
FutureOr<void> buildDeclarationsForClass(
ClassDeclaration clazz, MemberDeclarationBuilder builder) async {
var fields = await builder.fieldsOf(clazz);
builder.declareInType(DeclarationCode.fromParts([
'static const List<String> fieldNames = [',
for (var field in fields) "'${field.identifier.name}',",
'];',
]));
}
@override
FutureOr<void> buildDeclarationsForConstructor(
ConstructorDeclaration constructor, MemberDeclarationBuilder builder) {
var className = constructor.definingType.name;
var constructorName = constructor.identifier.name;
builder.declareInType(DeclarationCode.fromString(
'factory $className.${constructorName}Delegate() => '
'$className.$constructorName();'));
}
@override
FutureOr<void> buildDeclarationsForEnum(
EnumDeclaration enuum, EnumDeclarationBuilder builder) async {
var values = await builder.valuesOf(enuum);
builder.declareInType(DeclarationCode.fromParts([
'static const List<String> valuesByName = {',
for (var value in values) ...[
"'${value.identifier.name}': ",
value.identifier
],
'};',
]));
}
@override
FutureOr<void> buildDeclarationsForEnumValue(
EnumValueDeclaration value, EnumDeclarationBuilder builder) async {
final parent = await builder.typeDeclarationOf(value.definingEnum);
builder.declareInType(DeclarationCode.fromParts([
parent.identifier,
' ${value.identifier.name}ToString() => ',
value.identifier,
'.toString();',
]));
}
@override
FutureOr<void> buildDeclarationsForFunction(
FunctionDeclaration function, DeclarationBuilder builder) {
var functionName = function.identifier.name;
builder.declareInLibrary(DeclarationCode.fromParts([
function.returnType.code,
if (function.isGetter) ' get' else if (function.isSetter) ' set ',
' delegate${functionName.capitalize()}',
if (!function.isGetter) ...[
'(',
if (function.isSetter) ...[
function.positionalParameters.first.type.code,
' value',
],
')',
],
' => $functionName',
function.isGetter
? ''
: function.isSetter
? ' = value'
: '()',
';',
]));
}
@override
FutureOr<void> buildDeclarationsForMethod(
MethodDeclaration method, MemberDeclarationBuilder builder) {
if (method.positionalParameters.isNotEmpty ||
method.namedParameters.isNotEmpty) {
throw UnsupportedError('Can only run on method with no parameters!');
}
var methodName = method.identifier.name;
builder.declareInLibrary(DeclarationCode.fromParts([
method.returnType.code,
' delegateMember${methodName.capitalize()}() => $methodName();',
]));
}
@override
FutureOr<void> buildDeclarationsForMixin(
MixinDeclaration mixin, MemberDeclarationBuilder builder) async {
var methods = await builder.methodsOf(mixin);
builder.declareInType(DeclarationCode.fromParts([
'static const List<String> methodNames = [',
for (var method in methods) "'${method.identifier.name}',",
'];',
]));
}
@override
FutureOr<void> buildDeclarationsForVariable(
VariableDeclaration variable, DeclarationBuilder builder) {
var variableName = variable.identifier.name;
builder.declareInLibrary(DeclarationCode.fromParts([
variable.type.code,
' get delegate${variableName.capitalize()} => $variableName;',
]));
}
@override
FutureOr<void> buildDeclarationsForField(
FieldDeclaration field, MemberDeclarationBuilder builder) {
var fieldName = field.identifier.name;
builder.declareInType(DeclarationCode.fromParts([
field.type.code,
' get delegate${fieldName.capitalize()} => $fieldName;',
]));
}
@override
Future<void> buildDefinitionForClass(
ClassDeclaration clazz, TypeDefinitionBuilder builder) async {
// Apply ourself to all our members
var fields = (await builder.fieldsOf(clazz));
for (var field in fields) {
await buildDefinitionForField(
field, await builder.buildField(field.identifier));
}
var methods = (await builder.methodsOf(clazz));
for (var method in methods) {
await buildDefinitionForMethod(
method, await builder.buildMethod(method.identifier));
}
var constructors = (await builder.constructorsOf(clazz));
for (var constructor in constructors) {
await buildDefinitionForConstructor(
constructor, await builder.buildConstructor(constructor.identifier));
}
}
@override
Future<void> buildDefinitionForConstructor(ConstructorDeclaration constructor,
ConstructorDefinitionBuilder builder) async {
var clazz = await builder.declarationOf(constructor.definingType)
as TypeDeclaration;
var fields = (await builder.fieldsOf(clazz));
builder.augment(
body: await _buildFunctionAugmentation(constructor, builder),
initializers: [
for (var field in fields)
// TODO: Compare against actual `int` type.
if (field.hasFinal &&
(field.type as NamedTypeAnnotation).identifier.name == 'int')
RawCode.fromParts([field.identifier, ' = ${myInt!}']),
],
);
}
@override
Future<void> buildDefinitionForEnum(
EnumDeclaration enuum, EnumDefinitionBuilder builder) async {
// Apply ourself to all our members
var values = (await builder.valuesOf(enuum));
for (var value in values) {
await buildDefinitionForEnumValue(
value, await builder.buildEnumValue(value.identifier));
}
var fields = (await builder.fieldsOf(enuum));
for (var field in fields) {
await buildDefinitionForField(
field, await builder.buildField(field.identifier));
}
var methods = (await builder.methodsOf(enuum));
for (var method in methods) {
await buildDefinitionForMethod(
method, await builder.buildMethod(method.identifier));
}
var constructors = (await builder.constructorsOf(enuum));
for (var constructor in constructors) {
await buildDefinitionForConstructor(
constructor, await builder.buildConstructor(constructor.identifier));
}
}
@override
FutureOr<void> buildDefinitionForEnumValue(
EnumValueDeclaration value, EnumValueDefinitionBuilder builder) async {
final parent =
await builder.typeDeclarationOf(value.definingEnum) as EnumDeclaration;
final constructor = (await builder.constructorsOf(parent)).first;
final parts = [
value.identifier,
'(',
];
final stringType = await builder.resolve(NamedTypeAnnotationCode(
name:
// ignore: deprecated_member_use_from_same_package
await builder.resolveIdentifier(Uri.parse('dart:core'), 'String')));
for (var positional in constructor.positionalParameters) {
final resolvedType = await builder.resolve(positional.type.code);
if (!(await resolvedType.isExactly(stringType))) {
throw StateError('Expected only string parameters');
}
parts.add("'${positional.identifier.name}', ");
}
for (var named in constructor.namedParameters) {
final resolvedType = await builder.resolve(named.type.code);
if (!(await resolvedType.isExactly(stringType))) {
throw StateError('Expected only string parameters');
}
parts.add("${named.identifier.name}: '${named.identifier.name}', ");
}
parts.add('),');
builder.augment(DeclarationCode.fromParts(parts));
}
@override
Future<void> buildDefinitionForField(
FieldDeclaration field, VariableDefinitionBuilder builder) async =>
buildDefinitionForVariable(field, builder);
@override
Future<void> buildDefinitionForFunction(
FunctionDeclaration function, FunctionDefinitionBuilder builder) async {
builder.augment(await _buildFunctionAugmentation(function, builder),
docComments: CommentCode.fromString('// A comment!'));
}
@override
Future<void> buildDefinitionForMethod(
MethodDeclaration method, FunctionDefinitionBuilder builder) async {
await buildDefinitionForFunction(method, builder);
// Test the type declaration resolver
var parentClass = await builder.typeDeclarationOf(method.definingType);
// Should be able to find ourself in the methods of the parent class.
(await builder.methodsOf(parentClass))
.singleWhere((m) => m.identifier == method.identifier);
TypeDeclaration? superClass;
final interfaces = <TypeDeclaration>[];
final mixins = <TypeDeclaration>[];
final superclassConstraints = <TypeDeclaration>[];
// Test the class introspector
if (parentClass is ClassDeclaration) {
superClass =
(await builder.typeDeclarationOf(parentClass.superclass!.identifier));
interfaces.addAll(await Future.wait(parentClass.interfaces.map(
(interface) => builder.typeDeclarationOf(interface.identifier))));
mixins.addAll(await Future.wait(parentClass.mixins
.map((mixins) => builder.typeDeclarationOf(mixins.identifier))));
} else if (parentClass is MixinDeclaration) {
superclassConstraints.addAll(await Future.wait(
parentClass.superclassConstraints.map(
(interface) => builder.typeDeclarationOf(interface.identifier))));
interfaces.addAll(await Future.wait(parentClass.interfaces.map(
(interface) => builder.typeDeclarationOf(interface.identifier))));
} else if (parentClass is EnumDeclaration) {
interfaces.addAll(await Future.wait(parentClass.interfaces.map(
(interface) => builder.typeDeclarationOf(interface.identifier))));
mixins.addAll(await Future.wait(parentClass.mixins
.map((mixins) => builder.typeDeclarationOf(mixins.identifier))));
}
var fields = (await builder.fieldsOf(parentClass));
var methods = (await builder.methodsOf(parentClass));
var constructors = (await builder.constructorsOf(parentClass));
// Test the type resolver and static type interfaces
var methodReturnType = method.returnType as RecordTypeAnnotation;
var staticReturnType = await builder
.resolve(methodReturnType.positionalFields.first.type.code);
if (!(await staticReturnType.isExactly(staticReturnType))) {
throw StateError('The return type should be exactly equal to itself!');
}
if (!(await staticReturnType.isSubtypeOf(staticReturnType))) {
throw StateError('The return type should be a subtype of itself!');
}
// TODO: Use `builder.instantiateCode` instead once implemented.
if (constructors.isNotEmpty) {
var classType = await builder.resolve(constructors.first.returnType.code);
if (await staticReturnType.isExactly(classType)) {
throw StateError(
'The return type should not be exactly equal to the class type');
}
if (await staticReturnType.isSubtypeOf(classType)) {
throw StateError(
'The return type should not be a subtype of the class type!');
}
}
builder.augment(FunctionBodyCode.fromParts([
'''{
print('myBool: $myBool');
print('myDouble: $myDouble');
print('myInt: $myInt');
print('myList: $myList');
print('mySet: $mySet');
print('myMap: $myMap');
print('myString: $myString');
print('parentClass: ${parentClass.identifier.name}');
print('superClass: ${superClass?.identifier.name}');''',
for (var interface in interfaces)
"\n print('interface: ${interface.identifier.name}');",
for (var mixin in mixins)
"\n print('mixin: ${mixin.identifier.name}');",
for (var field in fields)
"\n print('field: ${field.identifier.name}');",
for (var method in methods)
"\n print('method: ${method.identifier.name}');",
for (var constructor in constructors)
"\n print('constructor: ${constructor.identifier.name}');",
'''
\n return augmented();
}''',
]));
}
@override
Future<void> buildDefinitionForMixin(
MixinDeclaration mixin, TypeDefinitionBuilder builder) async {
// Apply ourself to all our members
var fields = (await builder.fieldsOf(mixin));
for (var field in fields) {
await buildDefinitionForField(
field, await builder.buildField(field.identifier));
}
var methods = (await builder.methodsOf(mixin));
for (var method in methods) {
await buildDefinitionForMethod(
method, await builder.buildMethod(method.identifier));
}
}
@override
Future<void> buildDefinitionForVariable(
VariableDeclaration variable, VariableDefinitionBuilder builder) async {
var definingClass =
variable is FieldDeclaration ? variable.definingType.name : '';
builder.augment(
getter: DeclarationCode.fromParts([
variable.type.code,
' get ',
variable.identifier.name,
''' {
print('parentClass: $definingClass');
''',
if (variable is FieldDeclaration)
"print('isAbstract: ${variable.hasAbstract}');\n",
'''print('isExternal: ${variable.hasExternal}');
print('isFinal: ${variable.hasFinal}');
print('isLate: ${variable.hasLate}');
return augmented;
}''',
]),
setter: DeclarationCode.fromParts([
'set ',
variable.identifier.name,
'(',
variable.type.code,
' value) { augmented = value; }'
]),
initializer: ExpressionCode.fromString("'new initial value' + augmented"),
);
}
@override
FutureOr<void> buildTypesForClass(
ClassDeclaration clazz, ClassTypeBuilder builder) async {
List<Object> buildTypeParam(
TypeParameterDeclaration typeParam, bool isFirst) {
return [
if (!isFirst) ', ',
typeParam.identifier.name,
if (typeParam.bound != null) ...[
' extends ',
typeParam.bound!.code,
]
];
}
var name = '${clazz.identifier.name}Builder';
builder.declareType(
name,
DeclarationCode.fromParts([
'class $name',
if (clazz.typeParameters.isNotEmpty) ...[
'<',
...buildTypeParam(clazz.typeParameters.first, true),
for (var typeParam in clazz.typeParameters.skip(1))
...buildTypeParam(typeParam, false),
'>',
],
' implements Builder<',
clazz.identifier,
if (clazz.typeParameters.isNotEmpty) ...[
'<',
clazz.typeParameters.first.identifier.name,
for (var typeParam in clazz.typeParameters)
', ${typeParam.identifier.name}',
'>',
],
'> {}'
]));
final interfaceName = 'HasX';
builder.declareType(interfaceName, DeclarationCode.fromString('''
abstract interface class $interfaceName {
int get x;
}'''));
final mixinName = 'GetX';
builder.declareType(mixinName, DeclarationCode.fromString('''
mixin $mixinName implements $interfaceName {
int get x => 1;
}'''));
// ignore: deprecated_member_use_from_same_package
final mySuperClass = await builder.resolveIdentifier(
Uri.parse('package:foo/bar.dart'), 'MySuperclass');
builder.extendsType(NamedTypeAnnotationCode(name: mySuperClass));
builder.appendInterfaces([RawTypeAnnotationCode.fromString(interfaceName)]);
builder.appendMixins([RawTypeAnnotationCode.fromString(mixinName)]);
}
@override
FutureOr<void> buildTypesForConstructor(
ConstructorDeclaration constructor, TypeBuilder builder) {
var name = 'GeneratedBy${constructor.identifier.name.capitalize()}';
builder.declareType(name, DeclarationCode.fromString('class $name {}'));
}
@override
FutureOr<void> buildTypesForEnum(EnumDeclaration enuum, TypeBuilder builder) {
final name = 'GeneratedBy${enuum.identifier.name.capitalize()}';
builder.declareType(name, DeclarationCode.fromString('class $name {}'));
}
@override
FutureOr<void> buildTypesForEnumValue(
EnumValueDeclaration value, TypeBuilder builder) {
final name = 'GeneratedBy${value.definingEnum.name}_'
'${value.identifier.name.capitalize()}';
builder.declareType(name, DeclarationCode.fromString('class $name {}'));
}
@override
FutureOr<void> buildTypesForField(
FieldDeclaration field, TypeBuilder builder) {
var name = 'GeneratedBy${field.identifier.name.capitalize()}';
builder.declareType(name, DeclarationCode.fromString('class $name {}'));
}
@override
FutureOr<void> buildTypesForFunction(
FunctionDeclaration function, TypeBuilder builder) {
var suffix = function.isGetter
? 'Getter'
: function.isSetter
? 'Setter'
: '';
var name = 'GeneratedBy${function.identifier.name.capitalize()}$suffix';
builder.declareType(name, DeclarationCode.fromString('class $name {}'));
}
@override
FutureOr<void> buildTypesForMethod(
MethodDeclaration method, TypeBuilder builder) {
var name = 'GeneratedBy${method.identifier.name.capitalize()}';
builder.declareType(name, DeclarationCode.fromString('class $name {}'));
}
@override
FutureOr<void> buildTypesForMixin(
MixinDeclaration mixin, TypeBuilder builder) {
final onNames = mixin.superclassConstraints
.map((type) => type.identifier.name.capitalize())
.join('');
final name = 'GeneratedBy${mixin.identifier.name.capitalize()}On$onNames';
builder.declareType(name, DeclarationCode.fromString('class $name {}'));
}
@override
FutureOr<void> buildTypesForVariable(
VariableDeclaration variable, TypeBuilder builder) {
var name = 'GeneratedBy${variable.identifier.name.capitalize()}';
builder.declareType(name, DeclarationCode.fromString('class $name {}'));
}
@override
void buildDeclarationsForLibrary(
Library library, DeclarationBuilder builder) {
builder.declareInLibrary(
DeclarationCode.fromString("final LibraryInfo library;"));
}
@override
Future<void> buildDefinitionForLibrary(
Library library, LibraryDefinitionBuilder builder) async {
var languageVersion = library.languageVersion;
var allDeclarations = await builder.topLevelDeclarationsOf(library);
var variableDeclaration =
allDeclarations.singleWhere((d) => d.identifier.name == 'library');
var variableBuilder =
await builder.buildVariable(variableDeclaration.identifier);
variableBuilder.augment(
initializer: ExpressionCode.fromParts([
'LibraryInfo(',
"Uri.parse('${library.uri}'), ",
"'${languageVersion.major}.${languageVersion.minor}', ",
"[",
for (var type in allDeclarations)
if (type is TypeDeclaration) ...[type.identifier, ', '],
"])",
]));
}
@override
void buildTypesForLibrary(Library library, TypeBuilder builder) {
builder.declareType('LibraryInfo', DeclarationCode.fromString('''
class LibraryInfo {
final Uri uri;
final String languageVersion;
final List<Type> definedTypes;
const LibraryInfo(this.uri, this.languageVersion, this.definedTypes);
}'''));
}
@override
FutureOr<void> buildTypesForExtension(
ExtensionDeclaration extension, TypeBuilder builder) {
final onType = extension.onType as NamedTypeAnnotation;
final name = '${extension.identifier.name}On${onType.identifier.name}';
builder.declareType(name, DeclarationCode.fromString('class $name {}'));
}
@override
FutureOr<void> buildDeclarationsForExtension(
ExtensionDeclaration extension, MemberDeclarationBuilder builder) async {
final dartCoreList =
// ignore: deprecated_member_use_from_same_package
await builder.resolveIdentifier(Uri.parse('dart:core'), 'List');
final dartCoreString =
// ignore: deprecated_member_use_from_same_package
await builder.resolveIdentifier(Uri.parse('dart:core'), 'String');
builder.declareInType(DeclarationCode.fromParts([
NamedTypeAnnotationCode(name: dartCoreList, typeArguments: [
NamedTypeAnnotationCode(name: dartCoreString),
]),
' get onTypeFieldNames;',
]));
}
@override
FutureOr<void> buildDefinitionForExtension(
ExtensionDeclaration extension, TypeDefinitionBuilder builder) async {
// Get a builder for the getter we added earlier.
final extensionMethods = await builder.methodsOf(extension);
final getterBuilder = await builder.buildMethod(extensionMethods
.singleWhere((m) => m.identifier.name == 'onTypeFieldNames')
.identifier);
// Introspect on our `on` type.
final onType = (await builder.typeDeclarationOf(
(extension.onType as NamedTypeAnnotation).identifier));
final onTypeFields = await builder.fieldsOf(onType);
getterBuilder.augment(FunctionBodyCode.fromParts([
'=> [',
for (var field in onTypeFields) "'${field.identifier.name}',",
'];',
]));
}
@override
FutureOr<void> buildTypesForExtensionType(
ExtensionTypeDeclaration extensionType, TypeBuilder builder) {
final representationType =
extensionType.representationType as NamedTypeAnnotation;
final name = '${extensionType.identifier.name}On'
'${representationType.identifier.name}';
builder.declareType(name, DeclarationCode.fromString('class $name {}'));
}
@override
FutureOr<void> buildDeclarationsForExtensionType(
ExtensionTypeDeclaration extensionType,
MemberDeclarationBuilder builder) async {
final dartCoreList =
// ignore: deprecated_member_use_from_same_package
await builder.resolveIdentifier(Uri.parse('dart:core'), 'List');
final dartCoreString =
// ignore: deprecated_member_use_from_same_package
await builder.resolveIdentifier(Uri.parse('dart:core'), 'String');
builder.declareInType(DeclarationCode.fromParts([
NamedTypeAnnotationCode(name: dartCoreList, typeArguments: [
NamedTypeAnnotationCode(name: dartCoreString),
]),
' get onTypeFieldNames;',
]));
}
@override
FutureOr<void> buildDefinitionForExtensionType(
ExtensionTypeDeclaration extensionType,
TypeDefinitionBuilder builder) async {
// Get a builder for the getter we added earlier.
final extensionTypeMethods = await builder.methodsOf(extensionType);
final getterBuilder = await builder.buildMethod(extensionTypeMethods
.singleWhere((m) => m.identifier.name == 'onTypeFieldNames')
.identifier);
// Introspect on our "representation" type.
final onType = (await builder.typeDeclarationOf(
(extensionType.representationType as NamedTypeAnnotation).identifier));
final onTypeFields = await builder.fieldsOf(onType);
getterBuilder.augment(FunctionBodyCode.fromParts([
'=> [',
for (var field in onTypeFields) "'${field.identifier.name}',",
'];',
]));
}
@override
FutureOr<void> buildTypesForTypeAlias(
TypeAliasDeclaration extensionType, TypeBuilder builder) {
final representationType = extensionType.aliasedType as NamedTypeAnnotation;
final name = '${extensionType.identifier.name}AliasedType'
'${representationType.identifier.name}';
builder.declareType(name, DeclarationCode.fromString('class $name {}'));
}
@override
FutureOr<void> buildDeclarationsForTypeAlias(
TypeAliasDeclaration extensionType, DeclarationBuilder builder) async {
final dartCoreList =
// ignore: deprecated_member_use_from_same_package
await builder.resolveIdentifier(Uri.parse('dart:core'), 'List');
final dartCoreString =
// ignore: deprecated_member_use_from_same_package
await builder.resolveIdentifier(Uri.parse('dart:core'), 'String');
builder.declareInLibrary(DeclarationCode.fromParts([
NamedTypeAnnotationCode(name: dartCoreList, typeArguments: [
NamedTypeAnnotationCode(name: dartCoreString),
]),
' get aliasedTypeFieldNames;',
]));
}
}
Future<FunctionBodyCode> _buildFunctionAugmentation(
FunctionDeclaration function,
DefinitionPhaseIntrospector introspector) async {
Future<List<Object>> typeParts(TypeAnnotation annotation) async {
if (annotation is OmittedTypeAnnotation) {
var inferred = await introspector.inferType(annotation);
return [inferred.code, ' (inferred)'];
}
return [annotation.code];
}
return FunctionBodyCode.fromParts([
'{\n',
if (function is MethodDeclaration)
"print('definingClass: ${function.definingType.name}');\n",
if (function is ConstructorDeclaration)
'''
print('isConst: ${function.isConst}');
print('isFactory: ${function.isFactory}');''',
'''
print('isExternal: ${function.hasExternal}');
print('isGetter: ${function.isGetter}');
print('isSetter: ${function.isSetter}');
print('returnType: ''',
function.returnType.code,
"');\n",
for (var param in function.positionalParameters) ...[
"print('positionalParam: ",
...await typeParts(param.type),
' ${param.identifier.name}',
"');\n",
],
for (var param in function.namedParameters) ...[
"print('namedParam: ",
...await typeParts(param.type),
' ${param.identifier.name}',
"');\n",
],
for (var param in function.typeParameters) ...[
"print('typeParam: ${param.identifier.name} ",
if (param.bound != null) param.bound!.code,
"');\n",
],
'return augmented',
if (function.isSetter) ...[
' = ',
function.positionalParameters.first.identifier,
],
if (!function.isGetter && !function.isSetter) '()',
''';
}''',
]);
}
class DanglingTaskMacro
implements
LibraryTypesMacro,
LibraryDeclarationsMacro,
LibraryDefinitionMacro {
@override
void buildTypesForLibrary(Library library, TypeBuilder builder) {
Future.value('hello').then((_) => Future.value('world'));
}
@override
void buildDeclarationsForLibrary(
Library library, DeclarationBuilder builder) {
Future.value('hello').then((_) => Future.value('world'));
}
@override
FutureOr<void> buildDefinitionForLibrary(
Library library, LibraryDefinitionBuilder builder) {
Future.value('hello').then((_) => Future.value('world'));
}
}
class DanglingTimerMacro
implements
LibraryTypesMacro,
LibraryDeclarationsMacro,
LibraryDefinitionMacro {
@override
void buildTypesForLibrary(Library library, TypeBuilder builder) {
Timer.run(() {});
}
@override
void buildDeclarationsForLibrary(
Library library, DeclarationBuilder builder) {
Timer.run(() {});
}
@override
FutureOr<void> buildDefinitionForLibrary(
Library library, LibraryDefinitionBuilder builder) {
Timer.run(() {});
}
}
class DanglingPeriodicTimerMacro
implements
LibraryTypesMacro,
LibraryDeclarationsMacro,
LibraryDefinitionMacro {
@override
void buildTypesForLibrary(Library library, TypeBuilder builder) {
Timer.periodic(Duration(seconds: 1), (_) {});
}
@override
void buildDeclarationsForLibrary(
Library library, DeclarationBuilder builder) {
Timer.periodic(Duration(seconds: 1), (_) {});
}
@override
FutureOr<void> buildDefinitionForLibrary(
Library library, LibraryDefinitionBuilder builder) {
Timer.periodic(Duration(seconds: 1), (_) {});
}
}
extension on String {
String capitalize() => '${this[0].toUpperCase()}${substring(1)}';
}
-926
View File
@@ -1,926 +0,0 @@
// 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:mirrors';
import 'package:_macros/src/api.dart';
import 'package:_macros/src/executor.dart';
import 'package:_macros/src/executor/introspection_impls.dart';
import 'package:_macros/src/executor/remote_instance.dart';
import 'package:test/test.dart';
class TestTypePhaseIntrospector implements TypePhaseIntrospector {
@override
Future<Identifier> resolveIdentifier(Uri library, String name) async {
if (library == Uri.parse('dart:core') && name == 'String') {
return Fixtures.stringType.identifier;
}
if (library == Uri.parse('dart:core') && name == 'List') {
return Fixtures.listIdentifier;
}
if (library == Fixtures.library.uri &&
name == Fixtures.mySuperclass.identifier.name) {
return Fixtures.mySuperclass.identifier;
}
throw UnimplementedError('Cannot resolve the identifier $library:$name');
}
}
class TestDeclarationPhaseIntrospector extends TestTypePhaseIntrospector
implements DeclarationPhaseIntrospector {
final Map<TypeDeclaration, List<ConstructorDeclaration>> constructors;
final Map<EnumDeclaration, List<EnumValueDeclaration>> enumValues;
final Map<TypeDeclaration, List<FieldDeclaration>> fields;
final Map<TypeDeclaration, List<MethodDeclaration>> methods;
final Map<Library, List<TypeDeclaration>> libraryTypes;
final Map<Identifier, StaticType> staticTypes;
final Map<Identifier, Declaration> identifierDeclarations;
TestDeclarationPhaseIntrospector(
{required this.constructors,
required this.enumValues,
required this.fields,
required this.methods,
required this.libraryTypes,
required this.staticTypes,
required this.identifierDeclarations});
@override
Future<TypeDeclaration> typeDeclarationOf(
covariant Identifier identifier) async {
var declaration = identifierDeclarations[identifier];
if (declaration != null) return declaration as TypeDeclaration;
throw 'No declaration found for ${identifier.name}';
}
@override
Future<StaticType> resolve(covariant TypeAnnotationCode type) async {
assert(type.parts.length == 1);
return staticTypes[type.parts.first]!;
}
@override
Future<List<ConstructorDeclaration>> constructorsOf(
covariant TypeDeclaration type) async =>
constructors[type]!;
@override
Future<List<EnumValueDeclaration>> valuesOf(
covariant EnumDeclaration enuum) async =>
enumValues[enuum]!;
@override
Future<List<FieldDeclaration>> fieldsOf(
covariant TypeDeclaration clazz) async =>
fields[clazz]!;
@override
Future<List<MethodDeclaration>> methodsOf(
covariant TypeDeclaration clazz) async =>
methods[clazz]!;
@override
Future<List<TypeDeclaration>> typesOf(covariant Library library) async =>
libraryTypes[library]!;
}
/// Doesn't handle generics etc but thats ok for now
class TestNamedStaticType extends NamedStaticTypeImpl {
final List<TestNamedStaticType> superTypes;
TestNamedStaticType(
super.id, {
required this.superTypes,
required super.declaration,
required super.typeArguments,
});
@override
Future<bool> isExactly(TestNamedStaticType other) async => _isExactly(other);
@override
Future<bool> isSubtypeOf(TestNamedStaticType other) async =>
_isExactly(other) ||
superTypes.any((superType) => superType._isExactly(other));
bool _isExactly(TestNamedStaticType other) =>
identical(other, this) ||
(declaration.library == other.declaration.library &&
declaration.identifier == other.declaration.identifier);
@override
Future<NamedStaticType?> asInstanceOf(TypeDeclaration declaration) async {
for (TestNamedStaticType superType in superTypes) {
if (superType.declaration.identifier == declaration.identifier) {
return superType;
}
}
return null;
}
}
/// Assumes all omitted types are [TestOmittedTypeAnnotation]s and just returns
/// the inferred type directly.
class TestDefinitionsPhaseIntrospector extends TestDeclarationPhaseIntrospector
implements DefinitionPhaseIntrospector {
final Map<Library, List<Declaration>> libraryDeclarations;
TestDefinitionsPhaseIntrospector(
{required this.libraryDeclarations,
required super.constructors,
required super.enumValues,
required super.fields,
required super.methods,
required super.libraryTypes,
required super.staticTypes,
required super.identifierDeclarations});
@override
Future<Declaration> declarationOf(Identifier identifier) async =>
identifierDeclarations[identifier]!;
@override
Future<TypeAnnotation> inferType(
TestOmittedTypeAnnotation omittedType) async =>
omittedType.inferredType!;
@override
Future<List<Declaration>> topLevelDeclarationsOf(Library library) async =>
libraryDeclarations[library]!;
@override
Future<TypeDeclaration> typeDeclarationOf(Identifier identifier) async =>
await super.typeDeclarationOf(identifier);
}
/// Knows its inferred type ahead of time.
class TestOmittedTypeAnnotation extends OmittedTypeAnnotationImpl {
final TypeAnnotation? inferredType;
TestOmittedTypeAnnotation([this.inferredType])
: super(id: RemoteInstance.uniqueId);
}
/// An identifier that knows the resolved version of itself.
class TestIdentifier extends IdentifierImpl {
final ResolvedIdentifier resolved;
TestIdentifier({
required super.id,
required super.name,
required IdentifierKind kind,
required Uri? uri,
required String? staticScope,
}) : resolved = ResolvedIdentifier(
kind: kind, name: name, staticScope: staticScope, uri: uri);
}
extension DebugCodeString on Code {
StringBuffer debugString([StringBuffer? buffer]) {
buffer ??= StringBuffer();
for (var part in parts) {
if (part is Code) {
part.debugString(buffer);
} else if (part is IdentifierImpl) {
buffer.write(part.name);
} else if (part is TestOmittedTypeAnnotation) {
if (part.inferredType != null) {
buffer.write('/*inferred*/');
part.inferredType!.code.debugString(buffer);
} else {
buffer.write('/*omitted*/');
}
} else {
buffer.write(part as String);
}
}
return buffer;
}
}
extension IterableToDebugCodeString on Iterable<Code> {
Iterable<String> mapToDebugCodeString() =>
map((a) => a.debugString().toString())
// Avoid doing this repeatedly when used in unorderedEquals etc.
.toList();
}
extension MapValuesToDebugCodeString<K> on Map<K, Iterable<Code>> {
Map<K, Iterable<String>> mapValuesToDebugCodeString() =>
map((key, values) => MapEntry(key, values.mapToDebugCodeString()));
}
/// Checks if two [Code] objects are of the same type and all their fields are
/// equal.
Matcher deepEqualsCode(Code other) => _DeepEqualityMatcher(other);
/// Checks if two [Declaration]s are of the same type and all their fields are
/// equal.
Matcher deepEqualsDeclaration(Declaration declaration) =>
_DeepEqualityMatcher(declaration);
/// Checks if two [TypeAnnotation]s are of the same type and all their fields
/// are equal.
Matcher deepEqualsTypeAnnotation(TypeAnnotation declaration) =>
_DeepEqualityMatcher(declaration);
/// Checks if two [Arguments]s are identical
Matcher deepEqualsArguments(Arguments arguments) =>
_DeepEqualityMatcher(arguments);
/// Checks if two [MacroException]s are identical
Matcher deepEqualsMacroException(MacroException macroException) =>
_DeepEqualityMatcher(macroException);
/// Checks if two [MetadataAnnotation]s are identical
Matcher deepEqualsMetadataAnnotation(MetadataAnnotation metadata) =>
_DeepEqualityMatcher(metadata);
/// Checks if two [StaticType]s are identical
Matcher deepEqualsStaticType(StaticType type) => _DeepEqualityMatcher(type);
/// Checks if two [Declaration]s, [TypeAnnotation]s, [Code]s or
/// [MacroException]s are of the same type and all their fields are equal.
class _DeepEqualityMatcher extends Matcher {
final Object? instance;
_DeepEqualityMatcher(this.instance);
@override
Description describe(Description description) => description;
@override
bool matches(item, Map matchState) {
// For type promotion.
final instance = this.instance;
if (!equals(item.runtimeType).matches(instance.runtimeType, matchState)) {
return false;
}
if (instance is Declaration ||
instance is TypeAnnotation ||
instance is MetadataAnnotation ||
instance is MacroException ||
instance is StaticType) {
var instanceReflector = reflect(instance);
var itemReflector = reflect(item);
var type = instanceReflector.type;
for (var getter
in type.instanceMembers.values.where((member) => member.isGetter)) {
// We only care about synthetic field getters
if (!getter.isSynthetic) continue;
var instanceField = instanceReflector.getField(getter.simpleName);
var itemField = itemReflector.getField(getter.simpleName);
var instanceValue = instanceField.reflectee;
var itemValue = itemField.reflectee;
if (!_DeepEqualityMatcher(instanceValue)
.matches(itemValue, matchState)) {
return false;
}
}
} else if (instance is Code) {
item as Code;
if (!_DeepEqualityMatcher(instance.parts)
.matches(item.parts, matchState)) {
return false;
}
} else if (instance is Arguments) {
item as Arguments;
if (!equals(instance.positional.length)
.matches(item.positional.length, matchState)) {
return false;
}
for (var i = 0; i < instance.positional.length; i++) {
if (!_DeepEqualityMatcher(instance.positional[i].value)
.matches(item.positional[i].value, matchState)) {
return false;
}
}
if (instance.named.length != item.named.length) return false;
if (!equals(instance.named.keys).matches(item.named.keys, matchState)) {
return false;
}
for (var key in instance.named.keys) {
if (!_DeepEqualityMatcher(instance.named[key]!.value)
.matches(item.named[key]!.value, matchState)) {
return false;
}
}
} else if (instance is List) {
item as List;
if (!equals(instance.length).matches(item.length, matchState)) {
return false;
}
for (var i = 0; i < instance.length; i++) {
if (!_DeepEqualityMatcher(instance[i]).matches(item[i], matchState)) {
return false;
}
}
} else if (instance is Map) {
item as Map;
if (!equals(instance.length).matches(item.length, matchState)) {
return false;
}
for (var key in instance.keys) {
// Key sets are same size, so they are equal if every key in `instance`
// is also a key in `item`.
if (!contains(key).matches(item, matchState)) {
return false;
}
// Maps are equal if keys are equal and every value is equal.
if (!_DeepEqualityMatcher(instance[key])
.matches(item[key], matchState)) {
return false;
}
}
} else {
// Handles basic values and identity
if (!equals(instance).matches(item, matchState)) {
return false;
}
}
return true;
}
}
class Fixtures {
static final library = LibraryImpl(
id: RemoteInstance.uniqueId,
languageVersion: LanguageVersionImpl(3, 0),
metadata: [],
uri: Uri.parse('package:foo/bar.dart'));
static final listIdentifier =
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'List');
static final nullableBoolType = NamedTypeAnnotationImpl(
id: RemoteInstance.uniqueId,
identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'bool'),
isNullable: true,
typeArguments: const []);
static final stringType = NamedTypeAnnotationImpl(
id: RemoteInstance.uniqueId,
identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'String'),
isNullable: false,
typeArguments: const []);
static final stringClass = ClassDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier: stringType.identifier,
library: Fixtures.library,
metadata: [],
typeParameters: [],
interfaces: [],
hasAbstract: false,
hasBase: false,
hasExternal: false,
hasFinal: false,
hasInterface: false,
hasMixin: false,
hasSealed: false,
mixins: [],
superclass: null);
static final inferredStringType = TestOmittedTypeAnnotation(stringType);
static final voidType = NamedTypeAnnotationImpl(
id: RemoteInstance.uniqueId,
identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'void'),
isNullable: false,
typeArguments: const []);
static final recordType = RecordTypeAnnotationImpl(
id: RemoteInstance.uniqueId,
isNullable: false,
namedFields: [
RecordFieldImpl(
id: RemoteInstance.uniqueId, name: 'world', type: stringType),
],
positionalFields: [
RecordFieldImpl(
id: RemoteInstance.uniqueId, name: null, type: stringType),
RecordFieldImpl(
id: RemoteInstance.uniqueId, name: 'hello', type: nullableBoolType),
]);
// Top level, non-class declarations.
static final myFunction = FunctionDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'myFunction'),
library: Fixtures.library,
metadata: [],
hasBody: true,
hasExternal: false,
isGetter: false,
isOperator: false,
isSetter: false,
namedParameters: [],
positionalParameters: [],
returnType: stringType,
typeParameters: []);
static final myVariable = VariableDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: '_myVariable'),
library: Fixtures.library,
metadata: [],
hasConst: false,
hasExternal: false,
hasFinal: true,
hasInitializer: false,
hasLate: false,
type: inferredStringType);
static final myVariableGetter = FunctionDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'myVariable'),
library: Fixtures.library,
metadata: [],
hasBody: true,
hasExternal: false,
isGetter: true,
isOperator: false,
isSetter: false,
namedParameters: [],
positionalParameters: [],
returnType: stringType,
typeParameters: []);
static final myVariableSetter = FunctionDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'myVariable'),
library: Fixtures.library,
metadata: [],
hasBody: true,
hasExternal: false,
isGetter: false,
isOperator: false,
isSetter: true,
namedParameters: [],
positionalParameters: [
FormalParameterDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'value'),
library: Fixtures.library,
metadata: [],
isNamed: false,
isRequired: true,
style: ParameterStyle.normal,
type: stringType)
],
returnType: voidType,
typeParameters: []);
static final libraryVariable = VariableDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'library'),
library: Fixtures.library,
metadata: [],
hasConst: false,
hasExternal: false,
hasFinal: true,
hasInitializer: false,
hasLate: false,
type: NamedTypeAnnotationImpl(
id: RemoteInstance.uniqueId,
isNullable: false,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'LibraryInfo'),
typeArguments: []));
// Class and member declarations
static final myInterfaceType = NamedTypeAnnotationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'MyInterface'),
isNullable: false,
typeArguments: const []);
static final myMixinType = NamedTypeAnnotationImpl(
id: RemoteInstance.uniqueId,
identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'MyMixin'),
isNullable: false,
typeArguments: const []);
static final mySuperclassType = NamedTypeAnnotationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'MySuperclass'),
isNullable: false,
typeArguments: const []);
static final myClassType = NamedTypeAnnotationImpl(
id: RemoteInstance.uniqueId,
identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'MyClass'),
isNullable: false,
typeArguments: const []);
static final myClass = ClassDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier: myClassType.identifier,
library: Fixtures.library,
metadata: [],
typeParameters: [],
interfaces: [myInterfaceType],
hasAbstract: false,
hasBase: false,
hasExternal: false,
hasFinal: false,
hasInterface: false,
hasMixin: false,
hasSealed: false,
mixins: [myMixinType],
superclass: mySuperclassType);
static final myConstructor = ConstructorDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'myConstructor'),
library: Fixtures.library,
metadata: [],
hasBody: false, // we will augment with one
hasExternal: false,
namedParameters: [],
positionalParameters: [
FormalParameterDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'myField'),
library: Fixtures.library,
metadata: [],
isNamed: false,
isRequired: true,
style: ParameterStyle.superFormal,
type: TestOmittedTypeAnnotation(myField.type))
],
returnType: myClassType,
typeParameters: [],
definingType: myClassType.identifier,
isConst: false,
isFactory: false);
static final myFactoryConstructor = ConstructorDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier: IdentifierImpl(
id: RemoteInstance.uniqueId, name: 'myFactoryConstructor'),
library: Fixtures.library,
metadata: [],
hasBody: false, // we will augment with one
hasExternal: false,
namedParameters: [],
positionalParameters: [
FormalParameterDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'myField'),
library: Fixtures.library,
metadata: [],
isNamed: false,
isRequired: true,
style: ParameterStyle.normal,
type: TestOmittedTypeAnnotation(myField.type))
],
returnType: myClassType,
typeParameters: [],
definingType: myClassType.identifier,
isConst: false,
isFactory: true);
static final myConstConstructor = ConstructorDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier: IdentifierImpl(
id: RemoteInstance.uniqueId, name: 'myConstConstructor'),
library: Fixtures.library,
metadata: [],
hasBody: false, // we will augment with one
hasExternal: false,
namedParameters: [],
positionalParameters: [
FormalParameterDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'myField'),
library: Fixtures.library,
metadata: [],
isNamed: false,
isRequired: true,
style: ParameterStyle.fieldFormal,
type: TestOmittedTypeAnnotation(myField.type))
],
returnType: myClassType,
typeParameters: [],
definingType: myClassType.identifier,
isConst: true,
isFactory: false);
static final myField = FieldDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'myField'),
library: Fixtures.library,
metadata: [],
hasAbstract: false,
hasConst: false,
hasExternal: false,
hasFinal: false,
hasInitializer: false,
hasLate: false,
type: stringType,
definingType: myClassType.identifier,
hasStatic: false);
static final myInterface = ClassDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier: myInterfaceType.identifier,
library: Fixtures.library,
metadata: [],
typeParameters: [],
interfaces: [],
hasAbstract: false,
hasBase: false,
hasExternal: false,
hasFinal: false,
hasInterface: true,
hasMixin: false,
hasSealed: false,
mixins: [],
superclass: null);
static final myMethod = MethodDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'myMethod'),
library: Fixtures.library,
metadata: [],
hasBody: true,
hasExternal: false,
isGetter: false,
isOperator: false,
isSetter: false,
namedParameters: [],
positionalParameters: [],
returnType: recordType,
typeParameters: [],
definingType: myClassType.identifier,
hasStatic: false);
static final mySuperclass = ClassDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier: mySuperclassType.identifier,
library: Fixtures.library,
metadata: [],
typeParameters: [
TypeParameterDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'T'),
library: library,
metadata: const [],
bound: null,
),
],
interfaces: [],
hasAbstract: false,
hasBase: false,
hasExternal: false,
hasFinal: false,
hasInterface: false,
hasMixin: false,
hasSealed: false,
mixins: [],
superclass: null);
static final mySuperTypeInstantiatedWithString = TestNamedStaticType(
RemoteInstance.uniqueId,
declaration: mySuperclass,
typeArguments: [],
superTypes: [
TestNamedStaticType(
RemoteInstance.uniqueId,
declaration: stringClass,
superTypes: [],
typeArguments: [],
)
],
);
static final myClassStaticType = TestNamedStaticType(
RemoteInstance.uniqueId,
declaration: myClass,
typeArguments: [],
superTypes: [
mySuperTypeInstantiatedWithString,
],
);
static final myEnumType = NamedTypeAnnotationImpl(
id: RemoteInstance.uniqueId,
isNullable: false,
identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'MyEnum'),
typeArguments: []);
static final myEnum = EnumDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier: myEnumType.identifier,
library: Fixtures.library,
metadata: [],
typeParameters: [],
interfaces: [],
mixins: []);
static final myEnumValues = [
EnumValueDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'a'),
library: Fixtures.library,
metadata: [],
definingEnum: myEnum.identifier,
),
];
static final myEnumConstructor = ConstructorDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier: IdentifierImpl(
id: RemoteInstance.uniqueId, name: 'myEnumConstructor'),
library: Fixtures.library,
metadata: [],
hasBody: false, // We will augment with one
hasExternal: false,
namedParameters: [],
positionalParameters: [
FormalParameterDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'myField'),
library: Fixtures.library,
metadata: [],
isNamed: false,
isRequired: true,
style: ParameterStyle.fieldFormal,
type: stringType)
],
returnType: myEnumType,
typeParameters: [],
definingType: myEnum.identifier,
isConst: false,
isFactory: false);
static final myMixin = MixinDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier: myMixinType.identifier,
library: Fixtures.library,
metadata: [],
typeParameters: [],
hasBase: false,
interfaces: [],
superclassConstraints: [myClassType],
);
static final myMixinMethod = MethodDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'myMixinMethod'),
library: Fixtures.library,
metadata: [],
hasBody: true,
hasExternal: false,
isGetter: false,
isOperator: false,
isSetter: false,
namedParameters: [],
positionalParameters: [],
returnType: recordType,
typeParameters: [],
definingType: myMixinType.identifier,
hasStatic: false);
static final myExtension = ExtensionDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'MyExtension'),
library: Fixtures.library,
metadata: [],
typeParameters: [],
onType: myClassType);
static final myExtensionType = ExtensionTypeDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'MyExtensionType'),
library: Fixtures.library,
metadata: [],
typeParameters: [],
representationType: myClassType);
static final myTypeAlias = TypeAliasDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier: IdentifierImpl(
id: RemoteInstance.uniqueId,
name: 'MyTypeAlias',
),
library: Fixtures.library,
metadata: [],
typeParameters: [],
aliasedType: myClassType,
);
static final myGeneratedExtensionMethod = MethodDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'onTypeFieldNames'),
library: library,
metadata: [],
hasBody: true,
hasExternal: false,
isGetter: true,
isOperator: false,
isSetter: false,
namedParameters: [],
positionalParameters: [],
returnType: NamedTypeAnnotationImpl(
id: RemoteInstance.uniqueId,
isNullable: false,
identifier: listIdentifier,
typeArguments: [stringType]),
typeParameters: [],
definingType: myExtension.identifier,
// TODO: This is a bit weird, the method is actually static, but doesn't
// have the keyword because it is implicit.
hasStatic: false);
static final myGeneratedExtensionTypeMethod = MethodDeclarationImpl(
id: RemoteInstance.uniqueId,
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'onTypeFieldNames'),
library: library,
metadata: [],
hasBody: true,
hasExternal: false,
isGetter: true,
isOperator: false,
isSetter: false,
namedParameters: [],
positionalParameters: [],
returnType: NamedTypeAnnotationImpl(
id: RemoteInstance.uniqueId,
isNullable: false,
identifier: listIdentifier,
typeArguments: [stringType]),
typeParameters: [],
definingType: myExtensionType.identifier,
// TODO: This is a bit weird, the method is actually static, but doesn't
// have the keyword because it is implicit.
hasStatic: false);
static final testDeclarationPhaseIntrospector =
TestDeclarationPhaseIntrospector(constructors: {
myClass: [myConstructor, myConstConstructor, myFactoryConstructor],
myEnum: [myEnumConstructor],
myMixin: [],
}, enumValues: {
myEnum: myEnumValues,
}, fields: {
myClass: [myField],
myMixin: [],
myEnum: [],
}, methods: {
myClass: [myMethod],
myMixin: [myMixinMethod],
myEnum: [],
myExtension: [myGeneratedExtensionMethod],
myExtensionType: [myGeneratedExtensionTypeMethod],
}, libraryTypes: {
Fixtures.library: [
myClass,
myEnum,
myExtension,
myMixin,
],
}, staticTypes: {
stringType.identifier: TestNamedStaticType(
RemoteInstance.uniqueId,
declaration: stringClass,
superTypes: [],
typeArguments: [],
),
myClass.identifier: myClassStaticType,
mySuperclass.identifier: TestNamedStaticType(
RemoteInstance.uniqueId,
declaration: mySuperclass,
superTypes: [],
typeArguments: [],
),
}, identifierDeclarations: {
myClass.identifier: myClass,
myEnum.identifier: myEnum,
myExtension.identifier: myExtension,
mySuperclass.identifier: mySuperclass,
myInterface.identifier: myInterface,
myMixin.identifier: myMixin,
myConstructor.identifier: myConstructor,
myEnumConstructor.identifier: myEnumConstructor,
for (EnumValueDeclaration value in myEnumValues) value.identifier: value,
myField.identifier: myField,
myMixinMethod.identifier: myMixinMethod,
myMethod.identifier: myMethod,
});
static final testDefinitionPhaseIntrospector =
TestDefinitionsPhaseIntrospector(
constructors: testDeclarationPhaseIntrospector.constructors,
enumValues: testDeclarationPhaseIntrospector.enumValues,
fields: testDeclarationPhaseIntrospector.fields,
methods: testDeclarationPhaseIntrospector.methods,
libraryDeclarations: {
Fixtures.library: [
myClass,
myEnum,
myMixin,
myFunction,
myVariable,
libraryVariable,
],
},
libraryTypes: testDeclarationPhaseIntrospector.libraryTypes,
staticTypes: testDeclarationPhaseIntrospector.staticTypes,
identifierDeclarations:
testDeclarationPhaseIntrospector.identifierDeclarations);
}
@@ -9,7 +9,6 @@ import 'package:_fe_analyzer_shared/src/messages/diagnostic_message.dart'
show DiagnosticMessageHandler;
import 'package:kernel/kernel.dart' show Component, Library, dummyComponent;
import 'package:kernel/target/targets.dart' show Target;
import 'package:macros/src/executor/serialization.dart' show SerializationMode;
import '../api_prototype/compiler_options.dart';
import '../api_prototype/experimental_flags.dart' show ExperimentalFlag;
@@ -54,9 +53,6 @@ Future<InitializedCompilerState> initializeIncrementalCompiler(
Map<String, String> environmentDefines, {
bool trackNeededDillLibraries = false,
bool verbose = false,
bool requirePrebuiltMacros = false,
List<String> precompiledMacros = const [],
SerializationMode macroSerializationMode = SerializationMode.byteData,
}) {
List<Component> outputLoadedAdditionalDills =
new List<Component>.filled(additionalDills.length, dummyComponent);
-1
View File
@@ -17,7 +17,6 @@ resolution: workspace
dependencies:
_fe_analyzer_shared: any
kernel: any
macros: any
package_config: any
yaml: any
+2 -7
View File
@@ -23,7 +23,6 @@ import 'package:front_end/src/api_unstable/bazel_worker.dart' as fe;
import 'package:front_end/src/api_unstable/frontend_server.dart';
import 'package:kernel/ast.dart' show Component, Library;
import 'package:kernel/target/targets.dart';
import 'package:macros/src/executor/serialization.dart' show SerializationMode;
import 'package:vm/kernel_front_end.dart';
import 'package:vm/modular/target/flutter.dart';
import 'package:vm/modular/target/flutter_runner.dart';
@@ -107,6 +106,7 @@ final ArgParser summaryArgsParser = new ArgParser()
'compilation.',
allowed: fe.Verbosity.allowedValues,
allowedHelp: fe.Verbosity.allowedValuesHelp)
// TODO(johnniwinther): Remove the macros-related options.
..addFlag('require-prebuilt-macros',
defaultsTo: false,
help: 'Require that prebuilt macros for all macro applications be '
@@ -313,8 +313,6 @@ Future<ComputeKernelResult> computeKernel(List<String> args,
}
}
SerializationMode macroSerializationMode =
new SerializationMode.fromOption(parsedArgs['macro-serialization-mode']);
if (usingIncrementalCompiler) {
// If digests weren't given and if not in worker mode, create fake data and
// ensure we don't have a previous state (as that wouldn't be safe with
@@ -351,10 +349,7 @@ Future<ComputeKernelResult> computeKernel(List<String> args,
summaryOnly,
nullableEnvironmentDefines!,
trackNeededDillLibraries: recordUsedInputs,
verbose: verbose,
requirePrebuiltMacros: parsedArgs['require-prebuilt-macros'],
precompiledMacros: parsedArgs['precompiled-macro'],
macroSerializationMode: macroSerializationMode);
verbose: verbose);
} else {
state = fe.initializeCompiler(
// TODO(sigmund): pass an old state once we can make use of it.
-1
View File
@@ -18,7 +18,6 @@ dependencies:
dev_compiler: any
front_end: any
kernel: any
macros: any
package_config: any
path: any
vm: any
-64
View File
@@ -1,64 +0,0 @@
## 0.1.3-main.0
- Add `isConst` to constructors.
- Bug fix: Add `const` and `factory` modifiers to constructor augmentations.
- Add `isField` and `isSuper` to parameters.
## 0.1.2-main.4
- Fix bug where augmenting classes with type parameters didn't work.
## 0.1.2-main.3
- Re-export 'package:_macros/src/executor/response_impls.dart'.
## 0.1.2-main.2
- Re-publish of `0.1.2-main.1` which was retracted due to a corrupted tar file.
## 0.1.2-main.1
- Make it an error for macros to complete with pending async work scheduled.
## 0.1.2-main.0
- Remove type parameter on internal `StaticType` implementation.
## 0.1.1-main.0
- Add identifiers to `NamedStaticType`.
- Add `StaticType.asInstanceOf`.
## 0.1.0-main.7
- Fix for generating code after extendsType
## 0.1.0-main.6
- Add extendsType API for adding an extends clause.
- Refactor builder implementations, fixes some bugs around nested builders.
## 0.1.0-main.5
- Handle ParallelWaitError with DiagnosticException errors nicely.
- Fix a bug where we weren't reporting diagnostics for nested builders.
## 0.1.0-main.4
- Improve formatting of constructor initializer augmentations.
## 0.1.0-main.3
- Validate parts in `Code.fromParts()`.
## 0.1.0-main.2
- Add caching for `typeDeclarationOf` results.
## 0.1.0-main.1
- Add caching for `TypeDeclaration` related introspection results.
## 0.1.0-main.0
Initial release, highly experimental at this time.
-1
View File
@@ -1 +0,0 @@
See `pkg/_macros/CONTRIBUTING.md`.
-27
View File
@@ -1,27 +0,0 @@
Copyright 2024, the Dart project authors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Google LLC nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-1
View File
@@ -1 +0,0 @@
file:/tools/OWNERS_FOUNDATION
-23
View File
@@ -1,23 +0,0 @@
☠☠ **Warning: This package is experimental and may not be available in a future
version of Dart.** ☠☠
This package is for macro authors, and exposes the APIs necessary to write
a macro. Specifically, it exports the private `_macros` SDK vendored package.
## Macro authors
Macro authors can use normal constraints on this package, and should only import
the `package:macros/macros.dart` file.
Note that the versions of this package are tied directly to your SDK version, so
you won't be able to get new feature releases without updating your SDK.
## Compilers and tools
This package also exposes some "private" sources (under lib/src), intended only
for use by compilers and tools, in order to bootstrap and execute macros.
When depending on these "private" sources, a more narrow constraint should be
used, which constraints to feature releases (which means patch versions until
such time as this package goes to 1.0.0). For example,
`macros: ">=0.1.1 <0.1.2"`.
-5
View File
@@ -1,5 +0,0 @@
include: package:lints/recommended.yaml
analyzer:
language:
strict-casts: true
-5
View File
@@ -1,5 +0,0 @@
// Copyright (c) 2024, 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.
export 'package:_macros/src/api.dart';
-5
View File
@@ -1,5 +0,0 @@
// Copyright (c) 2024, 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.
export 'package:_macros/src/bootstrap.dart';
-5
View File
@@ -1,5 +0,0 @@
// Copyright (c) 2024, 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.
export 'package:_macros/src/client.dart';
-5
View File
@@ -1,5 +0,0 @@
// Copyright (c) 2024, 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.
export 'package:_macros/src/executor.dart';
@@ -1,5 +0,0 @@
// Copyright (c) 2024, 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.
export 'package:_macros/src/executor/exception_impls.dart';
@@ -1,5 +0,0 @@
// Copyright (c) 2024, 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.
export 'package:_macros/src/executor/introspection_impls.dart';
@@ -1,5 +0,0 @@
// Copyright (c) 2024, 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.
export 'package:_macros/src/executor/isolated_executor.dart';
@@ -1,5 +0,0 @@
// Copyright (c) 2024, 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.
export 'package:_macros/src/executor/kernel_executor.dart';
@@ -1,5 +0,0 @@
// Copyright (c) 2024, 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.
export 'package:_macros/src/executor/multi_executor.dart';
@@ -1,5 +0,0 @@
// Copyright (c) 2024, 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.
export 'package:_macros/src/executor/process_executor.dart';
@@ -1,5 +0,0 @@
// Copyright (c) 2024, 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.
export 'package:_macros/src/executor/remote_instance.dart';
@@ -1,5 +0,0 @@
// Copyright (c) 2024, 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.
export 'package:_macros/src/executor/response_impls.dart';
@@ -1,5 +0,0 @@
// Copyright (c) 2024, 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.
export 'package:_macros/src/executor/serialization.dart';
-5
View File
@@ -1,5 +0,0 @@
// Copyright (c) 2024, 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.
export 'package:_macros/src/executor/span.dart';
-16
View File
@@ -1,16 +0,0 @@
name: macros
version: 0.1.3-main.0
description: >-
This package is for macro authors, and exposes the APIs necessary to write
a macro. It exports the APIs from the private `_macros` SDK vendored package.
repository: https://github.com/dart-lang/sdk/tree/main/pkg/macros
environment:
sdk: ^3.5.0
resolution: workspace
dependencies:
_macros:
sdk: dart
version: 0.3.3
-2
View File
@@ -53,8 +53,6 @@ workspace:
- pkg/js_shared
- pkg/kernel
- pkg/linter
- pkg/_macros
- pkg/macros
- pkg/meta
- pkg/mmap
- pkg/modular_test
-10
View File
@@ -781,15 +781,6 @@ copy("copy_sdk_packages_yaml") {
outputs = [ "$root_out_dir/$dart_sdk_output/sdk_packages.yaml" ]
}
# This rule copies pkg/_macros to pkg/_macros in the built SDK. This is a
# vendored SDK package.
copy_tree("copy_pkg_macros") {
visibility = [ ":create_common_sdk" ]
source = "../pkg/_macros"
dest = "$root_out_dir/$dart_sdk_output/pkg/_macros"
exclude = "{}"
}
# Parts common to both platform and full SDKs.
group("create_common_sdk") {
visibility = [
@@ -803,7 +794,6 @@ group("create_common_sdk") {
":copy_headers",
":copy_libraries_specification",
":copy_license",
":copy_pkg_macros",
":copy_prebuilt_devtools",
":copy_readme",
":copy_sdk_packages_yaml",
@@ -332,12 +332,6 @@ const Map<String, LibraryInfo> libraries = const {
categories: '',
documented: false,
),
"_macros": const LibraryInfo(
'_macros/macros.dart',
documented: false,
platforms: VM_PLATFORM,
maturity: Maturity.EXPERIMENTAL,
),
};
/// Information about a "dart:" library.
-3
View File
@@ -2,6 +2,3 @@
version: 1
sdk: dart
# Note that paths are relative to the root of the built SDK.
packages:
- name: _macros
path: pkg/_macros