add(conduit): refactoring conduit
This commit is contained in:
parent
3e91c05603
commit
57812c8638
10 changed files with 424 additions and 0 deletions
7
packages/isolate/.gitignore
vendored
Normal file
7
packages/isolate/.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# https://dart.dev/guides/libraries/private-files
|
||||||
|
# Created by `dart pub`
|
||||||
|
.dart_tool/
|
||||||
|
|
||||||
|
# Avoid committing pubspec.lock for library packages; see
|
||||||
|
# https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||||
|
pubspec.lock
|
3
packages/isolate/CHANGELOG.md
Normal file
3
packages/isolate/CHANGELOG.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
## 1.0.0
|
||||||
|
|
||||||
|
- Initial version.
|
39
packages/isolate/README.md
Normal file
39
packages/isolate/README.md
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<!--
|
||||||
|
This README describes the package. If you publish this package to pub.dev,
|
||||||
|
this README's contents appear on the landing page for your package.
|
||||||
|
|
||||||
|
For information about how to write a good package README, see the guide for
|
||||||
|
[writing package pages](https://dart.dev/guides/libraries/writing-package-pages).
|
||||||
|
|
||||||
|
For general information about developing packages, see the Dart guide for
|
||||||
|
[creating packages](https://dart.dev/guides/libraries/create-library-packages)
|
||||||
|
and the Flutter guide for
|
||||||
|
[developing packages and plugins](https://flutter.dev/developing-packages).
|
||||||
|
-->
|
||||||
|
|
||||||
|
TODO: Put a short description of the package here that helps potential users
|
||||||
|
know whether this package might be useful for them.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
TODO: List what your package can do. Maybe include images, gifs, or videos.
|
||||||
|
|
||||||
|
## Getting started
|
||||||
|
|
||||||
|
TODO: List prerequisites and provide or point to information on how to
|
||||||
|
start using the package.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
TODO: Include short and useful examples for package users. Add longer examples
|
||||||
|
to `/example` folder.
|
||||||
|
|
||||||
|
```dart
|
||||||
|
const like = 'sample';
|
||||||
|
```
|
||||||
|
|
||||||
|
## Additional information
|
||||||
|
|
||||||
|
TODO: Tell users more about the package: where to find more information, how to
|
||||||
|
contribute to the package, how to file issues, what response they can expect
|
||||||
|
from the package authors, and more.
|
30
packages/isolate/analysis_options.yaml
Normal file
30
packages/isolate/analysis_options.yaml
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# This file configures the static analysis results for your project (errors,
|
||||||
|
# warnings, and lints).
|
||||||
|
#
|
||||||
|
# This enables the 'recommended' set of lints from `package:lints`.
|
||||||
|
# This set helps identify many issues that may lead to problems when running
|
||||||
|
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
|
||||||
|
# style and format.
|
||||||
|
#
|
||||||
|
# If you want a smaller set of lints you can change this to specify
|
||||||
|
# 'package:lints/core.yaml'. These are just the most critical lints
|
||||||
|
# (the recommended set includes the core lints).
|
||||||
|
# The core lints are also what is used by pub.dev for scoring packages.
|
||||||
|
|
||||||
|
include: package:lints/recommended.yaml
|
||||||
|
|
||||||
|
# Uncomment the following section to specify additional rules.
|
||||||
|
|
||||||
|
# linter:
|
||||||
|
# rules:
|
||||||
|
# - camel_case_types
|
||||||
|
|
||||||
|
# analyzer:
|
||||||
|
# exclude:
|
||||||
|
# - path/to/excluded/files/**
|
||||||
|
|
||||||
|
# For more information about the core and recommended set of lints, see
|
||||||
|
# https://dart.dev/go/core-lints
|
||||||
|
|
||||||
|
# For additional information about configuring this file, see
|
||||||
|
# https://dart.dev/guides/language/analysis-options
|
31
packages/isolate/lib/isolate.dart
Normal file
31
packages/isolate/lib/isolate.dart
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the Protevus Platform.
|
||||||
|
*
|
||||||
|
* (C) Protevus <developers@protevus.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Protevus Platform.
|
||||||
|
*
|
||||||
|
* (C) Protevus <developers@protevus.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// This library provides functionality for working with isolates in Dart.
|
||||||
|
/// It exports three main components:
|
||||||
|
/// 1. Executable: Defines the structure for tasks that can be executed in isolates.
|
||||||
|
/// 2. Executor: Provides mechanisms for running executables in isolates.
|
||||||
|
/// 3. SourceGenerator: Offers utilities for generating source code for isolates.
|
||||||
|
///
|
||||||
|
/// These components work together to facilitate concurrent programming and
|
||||||
|
/// improve performance in Dart applications by leveraging isolates.
|
||||||
|
library isolate;
|
||||||
|
|
||||||
|
export 'package:protevus_isolate/src/executable.dart';
|
||||||
|
export 'package:protevus_isolate/src/executor.dart';
|
||||||
|
export 'package:protevus_isolate/src/source_generator.dart';
|
63
packages/isolate/lib/src/executable.dart
Normal file
63
packages/isolate/lib/src/executable.dart
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the Protevus Platform.
|
||||||
|
*
|
||||||
|
* (C) Protevus <developers@protevus.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:isolate';
|
||||||
|
import 'dart:mirrors';
|
||||||
|
|
||||||
|
abstract class Executable<T extends Object?> {
|
||||||
|
Executable(this.message) : _sendPort = message["_sendPort"];
|
||||||
|
|
||||||
|
Future<T> execute();
|
||||||
|
|
||||||
|
final Map<String, dynamic> message;
|
||||||
|
final SendPort? _sendPort;
|
||||||
|
|
||||||
|
U instanceOf<U>(
|
||||||
|
String typeName, {
|
||||||
|
List positionalArguments = const [],
|
||||||
|
Map<Symbol, dynamic> namedArguments = const {},
|
||||||
|
Symbol constructorName = Symbol.empty,
|
||||||
|
}) {
|
||||||
|
ClassMirror? typeMirror = currentMirrorSystem()
|
||||||
|
.isolate
|
||||||
|
.rootLibrary
|
||||||
|
.declarations[Symbol(typeName)] as ClassMirror?;
|
||||||
|
|
||||||
|
typeMirror ??= currentMirrorSystem()
|
||||||
|
.libraries
|
||||||
|
.values
|
||||||
|
.where((lib) => lib.uri.scheme == "package" || lib.uri.scheme == "file")
|
||||||
|
.expand((lib) => lib.declarations.values)
|
||||||
|
.firstWhere(
|
||||||
|
(decl) =>
|
||||||
|
decl is ClassMirror &&
|
||||||
|
MirrorSystem.getName(decl.simpleName) == typeName,
|
||||||
|
orElse: () => throw ArgumentError(
|
||||||
|
"Unknown type '$typeName'. Did you forget to import it?",
|
||||||
|
),
|
||||||
|
) as ClassMirror?;
|
||||||
|
|
||||||
|
return typeMirror!
|
||||||
|
.newInstance(
|
||||||
|
constructorName,
|
||||||
|
positionalArguments,
|
||||||
|
namedArguments,
|
||||||
|
)
|
||||||
|
.reflectee as U;
|
||||||
|
}
|
||||||
|
|
||||||
|
void send(dynamic message) {
|
||||||
|
_sendPort!.send(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void log(String message) {
|
||||||
|
_sendPort!.send({"_line_": message});
|
||||||
|
}
|
||||||
|
}
|
128
packages/isolate/lib/src/executor.dart
Normal file
128
packages/isolate/lib/src/executor.dart
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the Protevus Platform.
|
||||||
|
*
|
||||||
|
* (C) Protevus <developers@protevus.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:isolate';
|
||||||
|
import 'package:protevus_isolate/isolate.dart';
|
||||||
|
|
||||||
|
class IsolateExecutor<U> {
|
||||||
|
IsolateExecutor(
|
||||||
|
this.generator, {
|
||||||
|
this.packageConfigURI,
|
||||||
|
this.message = const {},
|
||||||
|
});
|
||||||
|
|
||||||
|
final SourceGenerator generator;
|
||||||
|
final Map<String, dynamic> message;
|
||||||
|
final Uri? packageConfigURI;
|
||||||
|
final Completer completer = Completer();
|
||||||
|
|
||||||
|
Stream<dynamic> get events => _eventListener.stream;
|
||||||
|
|
||||||
|
Stream<String> get console => _logListener.stream;
|
||||||
|
|
||||||
|
final StreamController<String> _logListener = StreamController<String>();
|
||||||
|
final StreamController<dynamic> _eventListener = StreamController<dynamic>();
|
||||||
|
|
||||||
|
Future<U> execute() async {
|
||||||
|
if (packageConfigURI != null &&
|
||||||
|
!File.fromUri(packageConfigURI!).existsSync()) {
|
||||||
|
throw StateError(
|
||||||
|
"Package file '$packageConfigURI' not found. Run 'pub get' and retry.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final scriptSource = Uri.encodeComponent(await generator.scriptSource);
|
||||||
|
|
||||||
|
final onErrorPort = ReceivePort()
|
||||||
|
..listen((err) async {
|
||||||
|
if (err is List) {
|
||||||
|
final stack =
|
||||||
|
StackTrace.fromString(err.last.replaceAll(scriptSource, ""));
|
||||||
|
|
||||||
|
completer.completeError(StateError(err.first), stack);
|
||||||
|
} else {
|
||||||
|
completer.completeError(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final controlPort = ReceivePort()
|
||||||
|
..listen((results) {
|
||||||
|
if (results is Map && results.length == 1) {
|
||||||
|
if (results.containsKey("_result")) {
|
||||||
|
completer.complete(results['_result']);
|
||||||
|
return;
|
||||||
|
} else if (results.containsKey("_line_")) {
|
||||||
|
_logListener.add(results["_line_"]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_eventListener.add(results);
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
message["_sendPort"] = controlPort.sendPort;
|
||||||
|
|
||||||
|
final dataUri = Uri.parse(
|
||||||
|
"data:application/dart;charset=utf-8,$scriptSource",
|
||||||
|
);
|
||||||
|
|
||||||
|
await Isolate.spawnUri(
|
||||||
|
dataUri,
|
||||||
|
[],
|
||||||
|
message,
|
||||||
|
onError: onErrorPort.sendPort,
|
||||||
|
packageConfig: packageConfigURI,
|
||||||
|
automaticPackageResolution: packageConfigURI == null,
|
||||||
|
);
|
||||||
|
return await completer.future;
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
rethrow;
|
||||||
|
} finally {
|
||||||
|
onErrorPort.close();
|
||||||
|
controlPort.close();
|
||||||
|
_eventListener.close();
|
||||||
|
_logListener.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<T> run<T>(
|
||||||
|
Executable<T> executable, {
|
||||||
|
List<String> imports = const [],
|
||||||
|
Uri? packageConfigURI,
|
||||||
|
String? additionalContents,
|
||||||
|
List<Type> additionalTypes = const [],
|
||||||
|
void Function(dynamic event)? eventHandler,
|
||||||
|
void Function(String line)? logHandler,
|
||||||
|
}) async {
|
||||||
|
final source = SourceGenerator(
|
||||||
|
executable.runtimeType,
|
||||||
|
imports: imports,
|
||||||
|
additionalContents: additionalContents,
|
||||||
|
additionalTypes: additionalTypes,
|
||||||
|
);
|
||||||
|
|
||||||
|
final executor = IsolateExecutor<T>(
|
||||||
|
source,
|
||||||
|
packageConfigURI: packageConfigURI,
|
||||||
|
message: executable.message,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (eventHandler != null) {
|
||||||
|
executor.events.listen(eventHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logHandler != null) {
|
||||||
|
executor.console.listen(logHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
return executor.execute();
|
||||||
|
}
|
||||||
|
}
|
104
packages/isolate/lib/src/source_generator.dart
Normal file
104
packages/isolate/lib/src/source_generator.dart
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the Protevus Platform.
|
||||||
|
*
|
||||||
|
* (C) Protevus <developers@protevus.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:isolate';
|
||||||
|
import 'dart:mirrors';
|
||||||
|
import 'package:analyzer/dart/analysis/analysis_context.dart';
|
||||||
|
import 'package:analyzer/dart/analysis/context_builder.dart';
|
||||||
|
import 'package:analyzer/dart/analysis/context_locator.dart';
|
||||||
|
import 'package:analyzer/dart/analysis/results.dart';
|
||||||
|
import 'package:analyzer/dart/ast/ast.dart';
|
||||||
|
import 'package:analyzer/file_system/file_system.dart';
|
||||||
|
import 'package:analyzer/file_system/physical_file_system.dart';
|
||||||
|
import 'package:path/path.dart';
|
||||||
|
import 'package:protevus_isolate/isolate.dart';
|
||||||
|
|
||||||
|
class SourceGenerator {
|
||||||
|
SourceGenerator(
|
||||||
|
this.executableType, {
|
||||||
|
this.imports = const [],
|
||||||
|
this.additionalTypes = const [],
|
||||||
|
this.additionalContents,
|
||||||
|
});
|
||||||
|
|
||||||
|
Type executableType;
|
||||||
|
|
||||||
|
String get typeName =>
|
||||||
|
MirrorSystem.getName(reflectType(executableType).simpleName);
|
||||||
|
final List<String> imports;
|
||||||
|
final String? additionalContents;
|
||||||
|
final List<Type> additionalTypes;
|
||||||
|
|
||||||
|
Future<String> get scriptSource async {
|
||||||
|
final typeSource = (await _getClass(executableType)).toSource();
|
||||||
|
final builder = StringBuffer();
|
||||||
|
|
||||||
|
builder.writeln("import 'dart:async';");
|
||||||
|
builder.writeln("import 'dart:isolate';");
|
||||||
|
builder.writeln("import 'dart:mirrors';");
|
||||||
|
for (final anImport in imports) {
|
||||||
|
builder.writeln("import '$anImport';");
|
||||||
|
}
|
||||||
|
builder.writeln(
|
||||||
|
"""
|
||||||
|
Future main (List<String> args, Map<String, dynamic> message) async {
|
||||||
|
final sendPort = message['_sendPort'];
|
||||||
|
final executable = $typeName(message);
|
||||||
|
final result = await executable.execute();
|
||||||
|
sendPort.send({"_result": result});
|
||||||
|
}
|
||||||
|
""",
|
||||||
|
);
|
||||||
|
builder.writeln(typeSource);
|
||||||
|
|
||||||
|
builder.writeln((await _getClass(Executable)).toSource());
|
||||||
|
for (final type in additionalTypes) {
|
||||||
|
final source = await _getClass(type);
|
||||||
|
builder.writeln(source.toSource());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (additionalContents != null) {
|
||||||
|
builder.writeln(additionalContents);
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<ClassDeclaration> _getClass(Type type) async {
|
||||||
|
final uri =
|
||||||
|
await Isolate.resolvePackageUri(reflectClass(type).location!.sourceUri);
|
||||||
|
final path =
|
||||||
|
absolute(normalize(uri!.toFilePath(windows: Platform.isWindows)));
|
||||||
|
|
||||||
|
final context = _createContext(path);
|
||||||
|
final session = context.currentSession;
|
||||||
|
final unit = session.getParsedUnit(path) as ParsedUnitResult;
|
||||||
|
final typeName = MirrorSystem.getName(reflectClass(type).simpleName);
|
||||||
|
|
||||||
|
return unit.unit.declarations
|
||||||
|
.whereType<ClassDeclaration>()
|
||||||
|
.firstWhere((classDecl) => classDecl.name.value() == typeName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AnalysisContext _createContext(
|
||||||
|
String path, {
|
||||||
|
ResourceProvider? resourceProvider,
|
||||||
|
}) {
|
||||||
|
resourceProvider ??= PhysicalResourceProvider.INSTANCE;
|
||||||
|
final builder = ContextBuilder(resourceProvider: resourceProvider);
|
||||||
|
final contextLocator = ContextLocator(
|
||||||
|
resourceProvider: resourceProvider,
|
||||||
|
);
|
||||||
|
final root = contextLocator.locateRoots(
|
||||||
|
includedPaths: [path],
|
||||||
|
);
|
||||||
|
return builder.createContext(contextRoot: root.first);
|
||||||
|
}
|
19
packages/isolate/pubspec.yaml
Normal file
19
packages/isolate/pubspec.yaml
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
name: protevus_isolate
|
||||||
|
description: This library contains types that allow for executing code in a spawned isolate, perhaps with additional imports.
|
||||||
|
version: 0.0.1
|
||||||
|
homepage: https://protevus.com
|
||||||
|
documentation: https://docs.protevus.com
|
||||||
|
repository: https://git.protevus.com/protevus/platform
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: ^3.4.3
|
||||||
|
|
||||||
|
# Add regular dependencies here.
|
||||||
|
dependencies:
|
||||||
|
analyzer: ^6.5.0
|
||||||
|
glob: ^2.1.2
|
||||||
|
path: ^1.9.0
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
lints: ^3.0.0
|
||||||
|
test: ^1.24.0
|
Loading…
Reference in a new issue