在Javascript中有内置eval,eval函数允许动态执行JScript源代码, 可以执行一段非固定的字符串代码
例子1:
// 传入字符串形式 JS 表达式
console.log(eval('1 + 1')); // 2
console.log(eval('(() => 3)()')); // 3
例子2:
/**
* 使用 var 声明的变量和 function 声明的函数修改了当前词法作用域。
* 在 inner 内部创建的变量 a 和函数 fnA 覆盖了外部的变量 a 和函数 fnA。
* 变量 a 和函数 fnA 在 inner 内没有被提升。
*/
(function () {
const a = 0;
function fnA() {
return 10;
}
(function inner() {
console.log(a); // 0
console.log(fnA()); // 10
eval('var a = 1; function fnA () { return 11; };');
console.log(a); // 1
console.log(fnA()); // 11
}());
}());
本文将研究如何在dart中使用eval函数
先来看几个例子
-
使用Isolate.spawnUri调用字符串文件
示例代码:
import 'dart:isolate';
void main() async {
final uri = Uri.dataFromString(
'''
import 'dart:isolate';
void main(List<String >args, SendPort port) {
print("args:$args");
// return
dynamic result = args.first;
port.send(result);
}
''',
mimeType: 'application/dart',
);
final port = ReceivePort();
await Isolate.spawnUri(uri, ["1", "2"], port.sendPort);
final dynamic response = await port.first;
print(response);
}
Log:
/opt/flutter/bin/cache/dart-sdk/bin/dart --enable-asserts /Users/law/Desktop/Test/test_animations/lib/eval_test/isolate.dart
args:[1, 2]
1
Process finished with exit code 0
-
使用spanwUri调用dart文件
code_task.dart 文件
import 'dart:isolate';
void main(List<String> args, SendPort port) {
print("args:$args");
// return
dynamic result = args.first;
port.send(result);
}
同级下的main文件
import 'dart:isolate';
void main() async {
final port = ReceivePort();
await Isolate.spawnUri(Uri(path: "./code_task.dart"), ["1", "2"], port.sendPort);
final dynamic response = await port.first;
print(response);
}
Log:
/opt/flutter/bin/cache/dart-sdk/bin/dart --enable-asserts /Users/law/Desktop/Test/test_animations/lib/eval_test/isolate_dart.dart
args:$args
1
Process finished with exit code 0
-
在dart.ui中无法使用Isolate.spawnUri
示例代码
import 'dart:isolate';
import 'package:flutter/material.dart';
void main() async {
final uri = Uri.dataFromString(
'''
import 'dart:isolate';
void main(List<String >args, SendPort port) {
print("args:$args");
// return
dynamic result = args.first;
port.send(result);
}
''',
mimeType: 'application/dart',
);
final port = ReceivePort();
await Isolate.spawnUri(uri, ["1", "2"], port.sendPort);
final dynamic response = await port.first;
print(response);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp();
}
}
Log
Launching lib/eval_test/isolate_test.dart on macOS in debug mode...
Building macOS application...
Debug service listening on ws://127.0.0.1:65269/3I97oHekKyE=/ws
Syncing files to device macOS...
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: IsolateSpawnException: Unable to spawn isolate: Unsupported isolate URI: package:test_animations/eval_test/data:application/dart, import 'dart:isolate';
void main(List<String >args, SendPort port) {
print("args:$args");
/ return
dynamic result = args.first;
port.send(result);
}
dart_spawner:使用Isolate.spawnUri来实现,只能在Dart VM虚拟器内部执行
dart_eval:自己定义字符串解释器,将字符串代码转换成dart代码,很多标准库还没有实现
使用dart_eval
在项目中我需要在dart.ui项目中使用eval,并且在eval中需要调用外部接口。由于官方提供的例子较少,eval又比较冷门,所以我这里根据项目的需求对它进行了探索和试验。以下是测试过程。
-
dart_eval的example文件,dart_eval_example.dart
该示例介绍了如何在eval中调用外部类,以及如何扩展外部类,其中定义了属性
import 'package:dart_eval/dart_eval.dart';
import 'package:dart_eval/dart_eval_bridge.dart';
import 'package:dart_eval/stdlib/core.dart';
// *** Class definitions. *** //
//
// NOTE: In order to use these within dart_eval, scroll to the end of the file //
// to see an example of the necessary boilerplate (will be auto-generated in the future) //
class TimestampedTime {
const TimestampedTime(this.utcTime, {this.timezoneOffset = 0});
final int utcTime;
final int timezoneOffset;
}
abstract class WorldTimeTracker {
WorldTimeTracker();
TimestampedTime getTimeFor(String country);
}
// *** Main code *** //
void main(List<String> args) {
final source = '''
import 'package:example/bridge.dart';
class MyWorldTimeTracker extends WorldTimeTracker {
MyWorldTimeTracker();
static TimestampedTime _currentTimeWithOffset(int offset) {
return TimestampedTime(DateTime.now().millisecondsSinceEpoch,
timezoneOffset: offset);
}
@override
TimestampedTime getTimeFor(String country) {
final countries = <String, TimestampedTime> {
'USA': _currentTimeWithOffset(4),
'UK': _currentTimeWithOffset(6),
};
return countries[country];
}
}
MyWorldTimeTracker fn(String country) {
final timeTracker = MyWorldTimeTracker();
final myTime = timeTracker.getTimeFor(country);
print(country + ' timezone offset: ' + myTime.timezoneOffset.toString() + ' (from Eval!)');
return timeTracker;
}
''';
// Create a compiler and define the classes' bridge declarations so it knows their structure
final compiler = Compiler();
compiler.defineBridgeClasses([$TimestampedTime.$declaration, $WorldTimeTracker$bridge.$declaration]);
// Compile the source code into a Program containing metadata and bytecode. In a real app, you would likely
// compile the Eval code separately and output it to a file using program.write(), sharing only bridge classes
// with a local shared library
final program = compiler.compile({
'example': {'main.dart': source}
});
// Create a runtime from the compiled program, and register bridge functions for all static methods and constructors.
// Default constructors use "ClassName." syntax.
final runtime = Runtime.ofProgram(program)
..registerBridgeFunc('package:example/bridge.dart', 'TimestampedTime.', $TimestampedTime.$new)
..registerBridgeFunc('package:example/bridge.dart', 'WorldTimeTracker.', $WorldTimeTracker$bridge.$new,
isBridge: true);
// Call runtime.setup() after registering all bridge functions
runtime.setup();
// Specify some args for the function we're about to call. Except for [int]s, [double]s, [bool]s, and [List]s, use
// [$Value] wrappers. For named args, specify them in order using null to represent an unspecified arg.
runtime.args = [$String('USA')];
// Call the function and cast the result to the desired type
final timeTracker = runtime.executeLib('package:example/main.dart', 'fn') as WorldTimeTracker;
// We can now utilize the returned bridge class
print('UK timezone offset: ${timeTracker.getTimeFor('UK').timezoneOffset} (from outside Eval!)');
}
///////////////////////////////////////////////////////////////////////////////////////////
// *** Start of required boilerplate code. This can be auto-generated in the future. *** //
///////////////////////////////////////////////////////////////////////////////////////////
/// Create a wrapper for [TimestampedTime]. A wrapper is a performant interop solution
/// when you *don't* need the ability to override the class within the dart_eval VM.
///
/// This is a bimodal wrapper as it implements both [TimestampedTime] and [$Instance].
/// Bimodal wrappers can be used as a type argument for eg. a Future, but if you don't need
/// this it may be simpler to implement only [$Instance].
class $TimestampedTime implements TimestampedTime, $Instance {
/// Create a wrap constructor. We're not implementing the default constructor here, but if you
/// were to it'd typically be a runtimeOverride() constructor. You can read more details
/// about runtime overrides on dart_eval's GitHub wiki page for wrappers. The wrap constructor
/// wraps an underlying instance and inherits from [$Object].
$TimestampedTime.wrap(this.$value) : _superclass = $Object($value);
/// Define the compile-time type descriptor as an unresolved type
static const $type = BridgeTypeRef(BridgeTypeSpec('package:example/bridge.dart', 'TimestampedTime'));
/// Define the compile-time class declaration and map out all the fields and methods for the compiler.
static const $declaration = BridgeClassDef(BridgeClassType($type),
// Specify class constrctors
constructors: {
// Define the default constructor with an empty string
'': BridgeConstructorDef(BridgeFunctionDef(returns: BridgeTypeAnnotation($type), params: [
// Parameters using built-in types can use [RuntimeTypes] for the most common types. Others, like
// Future, may need to use a type spec for 'dart:core'.
BridgeParameter('utcTime', BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.intType)), false)
], namedParams: [
BridgeParameter('timezoneOffset', BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.intType)), true)
]))
},
// Specify class fields
fields: {
'utcTime': BridgeFieldDef(BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.intType))),
'timezoneOffset': BridgeFieldDef(BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.intType)))
},
// You can also specify methods, getters, and setters here if present.
// Inform the compiler that this is a wrapper, not a bridge.
wrap: true);
/// Define static [EvalCallableFunc] functions for all static methods and constructors. This is for the
/// default constructor and is what the runtime will use to create an instance of this class.
static $Value? $new(Runtime runtime, $Value? target, List<$Value?> args) {
return $TimestampedTime.wrap(TimestampedTime(args[0]!.$value, timezoneOffset: args[1]?.$value ?? 0));
}
/// The underlying Dart instance that this wrapper wraps
@override
final TimestampedTime $value;
/// In most cases [$reified] should just return [$value]. However, classes with generics may use
/// it to fully reify any properties they contain. For example, a dart_eval List will typically be
/// filled with [$Value] objects, but using [$reified] will convert it to a List of Dart values.
@override
TimestampedTime get $reified => $value;
/// Although not required, creating a superclass field allows you to inherit basic properties from
/// [$Object], such as == and hashCode.
final $Instance _superclass;
/// [$getProperty] is how dart_eval accesses a wrapper's properties and methods, so map them out here. In
/// the default case, fall back to our [_superclass] implementation. For methods, you would return
/// a [$Function] with a closure (for simplicity) or a custom [EvalFunction] subclass (for maximum performance).
@override
$Value? $getProperty(Runtime runtime, String identifier) {
switch (identifier) {
case 'utcTime':
return $int($value.utcTime);
case 'timezoneOffset':
return $int($value.timezoneOffset);
default:
return _superclass.$getProperty(runtime, identifier);
}
}
/// Lookup the runtime type ID
@override
int $getRuntimeType(Runtime runtime) => runtime.lookupType($type.spec!);
/// Map out non-final fields with [$setProperty]. We don't have any here, so just fallback to the Object
/// implementation. (Although there are no settable fields on Object, in the future it will invoke
/// noSuchMethod() where appropriate).
@override
void $setProperty(Runtime runtime, String identifier, $Value value) {
return _superclass.$setProperty(runtime, identifier, value);
}
/// Finally, our standard [TimestampedTime] implementations! Redirect to the wrapped [$value]'s implementation
/// for all properties and methods. This is only necessary if using a bimodal wrapper.
@override
int get timezoneOffset => $value.timezoneOffset;
@override
int get utcTime => $value.utcTime;
}
/// Unlike [TimestampedTime], we need to subclass [WorldTimeTracker]. For that, we can use a bridge class!
/// Bridge classes are flexible and in some ways simpler than wrappers, but they have a lot of overhead. Avoid
/// them if possible in performance-sensitive situations.
///
/// Because [WorldTimeTracker] is abstract, we can implement it here. If it were a concrete class you would instead
/// extend it.
class $WorldTimeTracker$bridge with $Bridge<WorldTimeTracker> implements WorldTimeTracker {
static const _$type = BridgeTypeRef(BridgeTypeSpec('package:example/bridge.dart', 'WorldTimeTracker'));
/// Define the compile-time class declaration and map out all the fields and methods for the compiler.
static const $declaration = BridgeClassDef(BridgeClassType(_$type, isAbstract: true),
constructors: {
// Even though this class is abstract, we currently need to define the default constructor anyway. This
// may change in the future.
'': BridgeConstructorDef(BridgeFunctionDef(returns: BridgeTypeAnnotation(_$type), params: [], namedParams: []))
},
methods: {
'getTimeFor': BridgeMethodDef(BridgeFunctionDef(returns: BridgeTypeAnnotation($TimestampedTime.$type), params: [
BridgeParameter('country', BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.stringType)), false)
], namedParams: []))
},
// Inform the compiler that this is a bridge, not a wrapper.
bridge: true);
/// Define static [EvalCallableFunc] functions for all static methods and constructors. This is for the
/// default constructor and is what the runtime will use to create an instance of this class.
static $Value? $new(Runtime runtime, $Value? target, List<$Value?> args) {
return $WorldTimeTracker$bridge();
}
/// [$bridgeGet] works differently than [$getProperty] - it's only called if the Eval subclass hasn't provided
/// an override implementation.
@override
$Value? $bridgeGet(String identifier) {
// [WorldTimeTracker] is abstract, so if we haven't overridden all of it's methods that's an error.
// If it were concrete, this implementation would look like [$getProperty] except you'd access fields
// and invoke methods on 'super'.
throw UnimplementedError('Cannot get property "$identifier" on abstract class WorldTimeTracker');
}
@override
void $bridgeSet(String identifier, $Value value) {
/// Same idea here.
throw UnimplementedError('Cannot set property "$identifier" on abstract class WorldTimeTracker');
}
/// In a bridge class, override all fields and methods with [$_invoke], [$_get], and [$_set]. This
/// is necessary since we may use the overridden VM implementation outside the VM.
@override
TimestampedTime getTimeFor(String country) => $_invoke('getTimeFor', [$String(country)]);
}
Log:
/opt/flutter/bin/cache/dart-sdk/bin/dart --enable-asserts /Users/law/Downloads/dart_eval-master/example/dart_eval_example.dart
USA timezone offset: 4 (from Eval!)
UK timezone offset: 6 (from outside Eval!)
Process finished with exit code 0
-
给dart_eval_example的类添加自定义方法
import 'package:dart_eval/dart_eval.dart';
import 'package:dart_eval/dart_eval_bridge.dart';
import 'package:dart_eval/stdlib/core.dart';
class TimestampedTime {
const TimestampedTime(this.utcTime, {this.timezoneOffset = 0});
final int utcTime;
final int timezoneOffset;
}
abstract class WorldTimeTracker {
WorldTimeTracker();
TimestampedTime getTimeFor(String country);
}
void main(List<String> args) {
const source = '''
import 'package:example/bridge.dart';
class MyWorldTimeTracker extends WorldTimeTracker {
MyWorldTimeTracker();
static TimestampedTime _currentTimeWithOffset(int offset) {
return TimestampedTime(DateTime.now().millisecondsSinceEpoch,
timezoneOffset: offset);
}
@override
TimestampedTime getTimeFor(String country) {
final countries = <String, TimestampedTime> {
'USA': _currentTimeWithOffset(4),
'UK': _currentTimeWithOffset(6),
};
return countries[country];
}
}
MyWorldTimeTracker fn(String country) {
final timeTracker = MyWorldTimeTracker();
final myTime = timeTracker.getTimeFor(country);
print(myTime.utcTime);
myTime.controlDevice({"a":"123"});
print(country + ' timezone offset: ' + myTime.timezoneOffset.toString() + ' (from Eval!)');
return timeTracker;
}
''';
// Create a compiler and define the classes' bridge declarations so it knows their structure
final compiler = Compiler();
compiler.defineBridgeClasses([$TimestampedTime.$declaration, $WorldTimeTracker$bridge.$declaration]);
// Compile the source code into a Program containing metadata and bytecode. In a real app, you would likely
// compile the Eval code separately and output it to a file using program.write(), sharing only bridge classes
// with a local shared library
final program = compiler.compile({
'example': {'main.dart': source}
});
// Create a runtime from the compiled program, and register bridge functions for all static methods and constructors.
// Default constructors use "ClassName." syntax.
final runtime = Runtime.ofProgram(program)
..registerBridgeFunc('package:example/bridge.dart', 'TimestampedTime.', $TimestampedTime.$new)
..registerBridgeFunc('package:example/bridge.dart', 'WorldTimeTracker.', $WorldTimeTracker$bridge.$new, isBridge: true);
// Call runtime.setup() after registering all bridge functions
runtime.setup();
// Specify some args for the function we're about to call. Except for [int]s, [double]s, [bool]s, and [List]s, use
// [$Value] wrappers. For named args, specify them in order using null to represent an unspecified arg.
runtime.args = [$String('USA')];
// Call the function and cast the result to the desired type
final timeTracker = runtime.executeLib('package:example/main.dart', 'fn') as WorldTimeTracker;
// We can now utilize the returned bridge class
print('UK timezone offset: ${timeTracker.getTimeFor('UK').timezoneOffset} (from outside Eval!)');
}
class $TimestampedTime implements TimestampedTime, $Instance {
$TimestampedTime.wrap(this.$value) : _superclass = $Object($value);
static const $type = BridgeTypeRef(BridgeTypeSpec('package:example/bridge.dart', 'TimestampedTime'));
static const $declaration = BridgeClassDef(BridgeClassType($type),
constructors: {
'': BridgeConstructorDef(BridgeFunctionDef(returns: BridgeTypeAnnotation($type), params: [
BridgeParameter('utcTime', BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.intType)), false)
], namedParams: [
BridgeParameter('timezoneOffset', BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.intType)), true)
]))
},
fields: {
'utcTime': BridgeFieldDef(BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.intType))),
'timezoneOffset': BridgeFieldDef(BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.intType)))
},
methods: {
'controlDevice': BridgeMethodDef(BridgeFunctionDef(returns: BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.nullType)), params: [
BridgeParameter('args', BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.dynamicType), nullable: true), false)
], namedParams: []))
},
wrap: true);
static $Value? $new(Runtime runtime, $Value? target, List<$Value?> args) {
return $TimestampedTime.wrap(TimestampedTime(args[0]!.$value, timezoneOffset: args[1]?.$value ?? 0));
}
@override
final TimestampedTime $value;
@override
TimestampedTime get $reified => $value;
final $Instance _superclass;
@override
$Value? $getProperty(Runtime runtime, String identifier) {
switch (identifier) {
case 'utcTime':
return $int($value.utcTime);
case 'timezoneOffset':
return $int($value.timezoneOffset);
case 'controlDevice':
return $Function((runtime, target, args) => controlDevice(runtime, target, args));
default:
return _superclass.$getProperty(runtime, identifier);
}
}
@override
int $getRuntimeType(Runtime runtime) => runtime.lookupType($type.spec!);
@override
void $setProperty(Runtime runtime, String identifier, $Value value) {
return _superclass.$setProperty(runtime, identifier, value);
}
@override
int get timezoneOffset => $value.timezoneOffset;
@override
int get utcTime => $value.utcTime;
$Value? controlDevice(Runtime runtime, $Value? target, List<$Value?> args) {
Map<$Value,$Value>? data = args[0]?.$value;
Map mapData = {};
data?.forEach((key, value) {
mapData[key.$value] = value.$value;
});
print("controlDevice $mapData");
return null;
}
}
class $WorldTimeTracker$bridge with $Bridge<WorldTimeTracker> implements WorldTimeTracker {
static const _$type = BridgeTypeRef(BridgeTypeSpec('package:example/bridge.dart', 'WorldTimeTracker'));
static const $declaration = BridgeClassDef(BridgeClassType(_$type, isAbstract: true),
constructors: {
'': BridgeConstructorDef(BridgeFunctionDef(returns: BridgeTypeAnnotation(_$type), params: [], namedParams: []))
},
methods: {
'getTimeFor': BridgeMethodDef(BridgeFunctionDef(
returns: BridgeTypeAnnotation($TimestampedTime.$type),
params: [BridgeParameter('country', BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.stringType)), false)],
namedParams: []))
},
// Inform the compiler that this is a bridge, not a wrapper.
bridge: true);
static $Value? $new(Runtime runtime, $Value? target, List<$Value?> args) {
return $WorldTimeTracker$bridge();
}
@override
$Value? $bridgeGet(String identifier) {
throw UnimplementedError('Cannot get property "$identifier" on abstract class WorldTimeTracker');
}
@override
void $bridgeSet(String identifier, $Value value) {
throw UnimplementedError('Cannot set property "$identifier" on abstract class WorldTimeTracker');
}
@override
TimestampedTime getTimeFor(String country) => $_invoke('getTimeFor', [$String(country)]);
}
Log:
/opt/flutter/bin/cache/dart-sdk/bin/dart --enable-asserts /Users/law/Downloads/dart_eval-master/tryEval/example_add_function.dart
1683533203985
controlDevice {a: 123}
USA timezone offset: 4 (from Eval!)
UK timezone offset: 6 (from outside Eval!)
Process finished with exit code 0
-
模仿example文件自定义class
import 'package:dart_eval/dart_eval.dart';
import 'package:dart_eval/dart_eval_bridge.dart';
import 'package:dart_eval/stdlib/core.dart';
const appName = 'evalApp';
const libraryName = 'package:evalApp/evalFunctions.dart';
const fileName = 'evalMain.dart';
void main() {
const source = '''
import 'package:evalApp/evalFunctions.dart';
class Cat {
Cat(this.name);
final String name;
String speak() {
return name;
}
}
String main() {
final cat = Cat('Fluffy');
print("main");
Fns myF = Fns();
myF.controlDevice({"a":"123"});
return cat.speak();
}
''';
//
final compiler = Compiler();
compiler.defineBridgeClass($Fns.$declaration);
//
final program = compiler.compile({
appName: {fileName: source}
});
final runtime = Runtime.ofProgram(program);
runtime.registerBridgeFunc(libraryName, 'Fns.', $Fns.$new);
//
runtime.setup();
//
runtime.executeLib('package:$appName/$fileName', 'main');
}
class Fns {
static controlDevice(Map data) {
print("controlDevice:$data");
}
static controlScene(Map data) {
print("controlScene:$data");
}
static setVar(Map data) {
print("setVar:$data");
}
static dynamic getVar(Map data) {
print("getVar:$data");
return "exampleR";
}
}
class $Fns implements Fns, $Instance {
//
static const $type = BridgeTypeRef(BridgeTypeSpec(libraryName, 'Fns'));
static const $declaration = BridgeClassDef(BridgeClassType($type),
constructors: {'': BridgeConstructorDef(BridgeFunctionDef(returns: BridgeTypeAnnotation($type), params: [], namedParams: []))},
methods: {
'controlDevice': BridgeMethodDef(BridgeFunctionDef(
returns: BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.nullType)),
params: [BridgeParameter('args', BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.dynamicType), nullable: true), false)],
namedParams: []))
},
wrap: true);
static $Value? $new(Runtime runtime, $Value? target, List<$Value?> args) {
return $Fns.wrap(Fns());
}
//
$Fns.wrap(this.$value) : _superclass = $Object($value);
final $Instance _superclass;
//
@override
$Value? $getProperty(Runtime runtime, String identifier) {
switch (identifier) {
case 'controlDevice':
return $Function((runtime, target, args) => controlDevice(runtime, target, args));
default:
return _superclass.$getProperty(runtime, identifier);
}
}
@override
int $getRuntimeType(Runtime runtime) => runtime.lookupType($type.spec!);
@override
final Fns $value;
@override
Fns get $reified => $value;
@override
void $setProperty(Runtime runtime, String identifier, $Value value) {
return _superclass.$setProperty(runtime, identifier, value);
}
///Start API
Map _getMapData(List<$Value?> args) {
Map<$Value, $Value>? data = args[0]?.$value;
Map mapData = {};
data?.forEach((key, value) {
mapData[key.$value] = value.$value;
});
return mapData;
}
$Value? controlDevice(Runtime runtime, $Value? target, List<$Value?> args) {
Fns.controlDevice(_getMapData(args));
return null;
}
///End API
}
Log:
/opt/flutter/bin/cache/dart-sdk/bin/dart --enable-asserts /Users/law/Downloads/dart_eval-master/tryEval/example_replace_class.dart
main
controlDevice:{a: 123}
Process finished with exit code 0
-
使用Eval Plugin,定义自己的eval接口类型
import 'dart:math';
import 'package:dart_eval/dart_eval.dart';
import 'package:dart_eval/dart_eval_bridge.dart';
import 'package:dart_eval/stdlib/core.dart';
const libraryName = 'package:evalApp/evalFunctions.dart';
const appName = 'evalApp';
const fileName = 'evalMain.dart';
const source = '''
import 'dart:math';
import 'package:evalApp/evalFunctions.dart';
class Cat {
Cat(this.name);
final String name;
String speak() {
return name;
}
}
String main() {
double a = tan(20);
print(a);
controlDevice({"1":"2"});
final cat = Cat('Fluffy');
print("main");
return cat.speak();
}
''';
void main() {
DateTime dateStart = DateTime.now();
// 制作插件
MyAppPlugin myAppPlugin = MyAppPlugin();
// 定义 compile
final compiler = Compiler();
compiler.addPlugin(myAppPlugin);
// 定义 program
final program = compiler.compile({
appName: {fileName: source}
});
// 定义 runtime
final runtime = Runtime.ofProgram(program);
runtime.addPlugin(myAppPlugin);
runtime.setup();
//
runtime.executeLib('package:$appName/$fileName', 'main');
print("main end:${DateTime.now().difference(dateStart)}");
}
void anotherMain(){
DateTime dateStart = DateTime.now();
MyAppPlugin myAppPlugin = MyAppPlugin();
eval(source,plugins: [myAppPlugin]);
print("main end:${DateTime.now().difference(dateStart)}");
}
class Fns {
static controlDevice(Map data) {
print("controlDevice:$data");
}
static controlScene(Map data) {
print("controlScene:$data");
}
static setVar(Map data) {
print("setVar:$data");
}
static dynamic getVar(Map data) {
print("getVar:$data");
return "exampleR";
}
}
class MyAppPlugin implements EvalPlugin {
@override
void configureForCompile(BridgeDeclarationRegistry registry) {
print("configureForCompile");
registry.defineBridgeTopLevelFunction(const BridgeFunctionDeclaration(
libraryName,
'controlDevice',
BridgeFunctionDef(
returns: BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.nullType)),
params: [BridgeParameter('args', BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.mapType)), false)])));
}
@override
void configureForRuntime(Runtime runtime) {
print("configureForRuntime");
runtime.registerBridgeFunc(libraryName, 'controlDevice', const _$controlDevice());
}
@override
String get identifier => libraryName;
}
mixin ArgsMap {
Map _getMapData(List<$Value?> args) {
Map<$Value, $Value>? data = args[0]?.$value;
Map mapData = {};
data?.forEach((key, value) {
mapData[key.$value] = value.$value;
});
return mapData;
}
}
class _$controlDevice with ArgsMap implements EvalCallable {
const _$controlDevice();
@override
$Value? call(Runtime runtime, $Value? target, List<$Value?> args) {
Fns.controlDevice(_getMapData(args));
return const $null();
}
}
Log:
/opt/flutter/bin/cache/dart-sdk/bin/dart --enable-asserts /Users/law/Downloads/dart_eval-master/tryEval/example_custom.dart
configureForCompile
configureForRuntime
2.2371609442247427
controlDevice:{1: 2}
main
main end:0:00:00.237323
Process finished with exit code 0
daer_eval实现的库和支持的特性如下
import 'dart:core';
import 'dart:math';
import 'dart:async';
import 'dart:core';
import 'dart:convert';
import 'dart:io';
下表详细介绍了带有原生Dart代码的dart_eval支持的语言功能。桥接时,功能支持可能会有所不同。
| Feature | Support level | Tests |
|---|---|---|
| Imports | ✅ | [1], [2], [3] |
| Exports | ✅ | [1], [2] |
part / part of | ✅ | [1] |
show and hide | ✅ | [1] |
| Conditional imports | ❌ | N/A |
| Deferred imports | ❌ | N/A |
| Functions | ✅ | [1] |
| Anonymous functions | ✅ | [1], [2], [3], [4], [5] |
| Arrow functions | ✅ | [1], [2] |
| Sync generators | ❌ | N/A |
| Async generators | ❌ | N/A |
| Tear-offs | Partial | [1], [2] |
| For loops | ✅ | [1], [2] |
| While loops | ✅ | ❌ |
| Do-while loops | ✅ | ❌ |
| For-each loops | ✅ | [1] |
| Async for-each | ❌ | N/A |
| Switch statements | ❌ | N/A |
Labels and break | ❌ | N/A |
| If statements | ✅ | [1] |
| Try-catch | Partial | [1], [2] |
| Try-catch-finally | ❌ | N/A |
| Lists | ✅ | [1] |
| Iterable | ✅ | [1], [2] |
| Maps | Partial | ❌ |
| Sets | ❌ | N/A |
Collection for | ✅ | [1], [2] |
Collection if | ✅ | [1], [2] |
| Spreads | ❌ | N/A |
| Classes | ✅ | [1] |
| Class static methods | ✅ | [1], [2] |
| Getters and setters | ✅ | [1] |
| Factory constructors | ❌ | N/A |
new keyword | ✅ | [1] |
| Class inheritance | ✅ | [1] |
Abstract and implements | Partial | ❌ |
this keyword | ✅ | [1], [2] |
super keyword | ✅ | ❌ |
| Super constructor params | ✅ | [1] |
| Mixins | ❌ | N/A |
| Futures | Partial | [1], [2] |
| Async/await | ✅ | [1], [2], [3] |
| Streams | Partial | [1] |
| String interpolation | ✅ | [1] |
| Enums | ❌ | N/A |
| Generic function types | Partial | [1] |
| Typedefs | ❌ | N/A |
| Generic classes | Partial | ❌ |
Type tests (is) | ✅ | [1] |
Casting (as) | ❌ | N/A |
assert | ❌ | N/A |
| Null safety | Partial | ❌ |
| Late initialization | ❌ | N/A |
| Cascades | ❌ | ❌ |
| Ternary expressions | ✅ | [1] |
| Extension methods | ❌ | N/A |
| Const expressions | Partial | N/A |
| Isolates | ❌ | N/A |
All Done!