blob: ae15038b8230eb45c6f70a2b341268a7444fb2f9 [file] [log] [blame]
// 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/multi_executor.dart';
import '../executor/executor_base.dart';
import '../executor/serialization.dart';
import '../executor.dart';
/// Returns a [MacroExecutor] which loads macros into isolates using precompiled
/// kernel files and communicates with that isolate using [serializationMode].
///
/// The [serializationMode] must be a `server` variant, and any precompiled
/// programs must use the corresponding `client` variant.
///
/// This is the only public api exposed by this library.
Future<MacroExecutor> start(SerializationMode serializationMode) async =>
new MultiMacroExecutor((Uri library, String name,
{Uri? precompiledKernelUri}) {
if (precompiledKernelUri == null) {
throw new UnsupportedError(
'This environment requires a non-null `precompiledKernelUri` to be '
'passed when loading macros.');
}
return _SingleIsolatedMacroExecutor.start(
library, name, precompiledKernelUri, serializationMode);
});
/// 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 Stream<Object> messageStream,
required this.onClose,
required this.sendPort,
required SerializationMode serializationMode})
: super(
messageStream: messageStream, serializationMode: serializationMode);
static Future<_SingleIsolatedMacroExecutor> start(Uri library, String name,
Uri precompiledKernelUri, SerializationMode serializationMode) async {
ReceivePort receivePort = new ReceivePort();
Isolate isolate =
await Isolate.spawnUri(precompiledKernelUri, [], receivePort.sendPort);
Completer<SendPort> sendPortCompleter = new Completer();
StreamController<Object> messageStreamController =
new StreamController(sync: true);
receivePort.listen((message) {
if (!sendPortCompleter.isCompleted) {
sendPortCompleter.complete(message as SendPort);
} else {
if (serializationMode == SerializationMode.byteDataServer) {
message =
(message as TransferableTypedData).materialize().asUint8List();
}
messageStreamController.add(message);
}
}).onDone(messageStreamController.close);
return new _SingleIsolatedMacroExecutor(
onClose: () {
receivePort.close();
isolate.kill();
},
messageStream: messageStreamController.stream,
sendPort: await sendPortCompleter.future,
serializationMode: serializationMode);
}
@override
void close() => onClose();
/// Sends the [Serializer.result] to [sendPort], possibly wrapping it in a
/// [TransferableTypedData] object.
void sendResult(Serializer serializer) {
if (serializationMode == SerializationMode.byteDataServer) {
sendPort.send(
new TransferableTypedData.fromList([serializer.result as Uint8List]));
} else {
sendPort.send(serializer.result);
}
}
}