[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:
committed by
Commit Queue
parent
04f3c05a4b
commit
f75cb3dfb9
@@ -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`.
|
||||
@@ -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.
|
||||
@@ -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 +0,0 @@
|
||||
file:/tools/OWNERS_FOUNDATION
|
||||
@@ -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 []
|
||||
@@ -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).
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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 {}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
@@ -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
|
||||
};
|
||||
''';
|
||||
@@ -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';
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
]
|
||||
]);
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 {}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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)}';
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -17,7 +17,6 @@ resolution: workspace
|
||||
dependencies:
|
||||
_fe_analyzer_shared: any
|
||||
kernel: any
|
||||
macros: any
|
||||
package_config: any
|
||||
yaml: any
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -18,7 +18,6 @@ dependencies:
|
||||
dev_compiler: any
|
||||
front_end: any
|
||||
kernel: any
|
||||
macros: any
|
||||
package_config: any
|
||||
path: any
|
||||
vm: any
|
||||
|
||||
@@ -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 +0,0 @@
|
||||
See `pkg/_macros/CONTRIBUTING.md`.
|
||||
@@ -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 +0,0 @@
|
||||
file:/tools/OWNERS_FOUNDATION
|
||||
@@ -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"`.
|
||||
@@ -1,5 +0,0 @@
|
||||
include: package:lints/recommended.yaml
|
||||
|
||||
analyzer:
|
||||
language:
|
||||
strict-casts: true
|
||||
@@ -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';
|
||||
@@ -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';
|
||||
@@ -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';
|
||||
@@ -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';
|
||||
@@ -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';
|
||||
@@ -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
|
||||
@@ -53,8 +53,6 @@ workspace:
|
||||
- pkg/js_shared
|
||||
- pkg/kernel
|
||||
- pkg/linter
|
||||
- pkg/_macros
|
||||
- pkg/macros
|
||||
- pkg/meta
|
||||
- pkg/mmap
|
||||
- pkg/modular_test
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user