自从开始使用 Dart 以来,我一直在寻找一种将 Dart(文本)源(同一程序很可能动态生成)作为代码执行的方法。就像臭名昭著的“eval()”函数。
最近我发现了一些提示,即 Isolates 之间的通信端口支持某种“Spawn”,似乎它可以允许这种“技巧”。在 Ruby 中,也可以将模块作为语言特性动态加载,也许在 Dart 中有一些方法可以做到这一点?
任何线索或一个简单的例子将不胜感激。
提前致谢!
Ladislav Thon 提供了这个答案(在 Dart 论坛上),
我相信可以肯定地说 Dart 永远不会有 eval。但它会有其他更结构化的动态生成代码的方式(代号镜像构建器)。不过,现在没有这样的事情。
有两种方法可以生成隔离:spawnFunction,它从新隔离中的现有代码运行现有函数,所以你不需要寻找任何东西,以及 spawnUri,它从给定的 URI 下载代码并在新隔离中运行它。这本质上是动态代码加载——但动态加载的代码与现有代码是隔离的。它在一个新的隔离中运行,因此与它通信的唯一方式是通过消息传递(通过端口)。
谢谢拉迪斯拉夫...
您可以将字符串作为 Dart 代码运行,方法是首先从中构造一个数据 URI,然后将其传递到Isolate.spawnUri
.
import 'dart:isolate';
void main() async {
final uri = Uri.dataFromString(
'''
void main() {
print("Hellooooooo from the other side!");
}
''',
mimeType: 'application/dart',
);
await Isolate.spawnUri(uri, [], null);
}
请注意,您只能在 JIT 模式下执行此操作,这意味着您可能会从中受益的唯一地方是 Dart VM 命令行应用程序/包:构建脚本。它不适用于 Flutter 发布版本。
要从中获取结果,您可以使用ports:
import 'dart:isolate';
void main() async {
final name = 'Eval Knievel';
final uri = Uri.dataFromString(
'''
import "dart:isolate";
void main(_, SendPort port) {
port.send("Nice to meet you, $name!");
}
''',
mimeType: 'application/dart',
);
final port = ReceivePort();
await Isolate.spawnUri(uri, [], port.sendPort);
final String response = await port.first;
print(response);
}
至少在 Ruby 中,Eval() 可以执行从单个语句(如赋值)到完成相关程序的任何内容。与大多数其他可能的执行形式相比,执行许多小片段会花费大量时间。
仔细研究这个问题,至少有三个不同的函数是可以使用 eval 的各种方案的基础。Dart 至少以最少的方式处理其中的 2 个。
Dart 没有,看起来也没有计划支持“通用”脚本执行。
但是,可以使用 NoSuchMethod 方法有效地将变量动态“注入”到本地类环境中。它将 eval() 替换为如下所示的字符串: eval("String text = 'your first name here';" );
Dart 现在支持的第二个函数是调用方法,它看起来像这样: eval("Map map = SomeClass.some_method()" );
在搞砸了这个之后,我终于明白,一个简单的类可以用来存储调用方法所需的信息,对于一个类,作为一个似乎具有通用实用性的字符串。我可以替换一个易于维护的大型 switch 语句,否则它可能需要调用一系列方法。在 Ruby 中,这几乎是微不足道的,但是在 Dart 中,有一些相当不直观的调用,所以我想在一个地方获得这个“技巧”,它适合对字符串进行排序和过滤,例如您可能需要的。
这是使用反射将尽可能多的类(整个库?)“累积”到映射中的代码,这样 class.methodName() 只需一个键(作为字符串)就可以调用。
注意:我使用了一些“辅助方法”来执行 Map & List 功能,您可能想用直接 Dart 替换它们。但是,此代码仅使用函数进行使用和测试..
这是代码:
//The used "Helpers" here..
MAP_add(var map, var key, var value){ if(key != null){map[key] = value;}return(map);}
Object MAP_fetch(var map, var key, [var dflt = null]) {var value = map[key];if (value==null) {value = dflt;}return( value );}
class ClassMethodMapper {
Map _helperMirrorsMap, _methodMap;
void accum_class_map(Object myClass){
InstanceMirror helperMirror = reflect(myClass);
List methodsAr = helperMirror.type.methods.values;
String classNm = myClass.toString().split("'")[1]; ///#FRAGILE
MAP_add(_helperMirrorsMap, classNm, helperMirror);
methodsAr.forEach(( method) {
String key = method.simpleName;
if (key.charCodeAt(0) != 95) { //Ignore private methods
MAP_add(_methodMap, "${classNm}.${key}()", method);
}
});
}
Map invoker( String methodNm ) {
var method = MAP_fetch(_methodMap, methodNm, null);
if (method != null) {
String classNm = methodNm.split('.')[0];
InstanceMirror helperMirror = MAP_fetch(_helperMirrorsMap, classNm);
helperMirror.invoke(method.simpleName, []);
}
}
ClassMethodMapper() {
_methodMap = {};
_helperMirrorsMap = {};
}
}//END_OF_CLASS( ClassMethodMapper );
============
main() {
ClassMethodMapper cMM = new ClassMethodMapper();
cMM.accum_class_map(MyFirstExampleClass);
cMM.accum_class_map(MySecondExampleClass);
//Now you're ready to execute any method (not private as per a special line of code above)
//by simply doing this:
cMM.invoker( MyFirstExampleClass.my_example_method() );
}
实际上pub.dev/packages中有一些库,但由于是年轻版本,所以有一些限制,所以我可以向你推荐这个库表达式来飞镖和颤振。
解析和评估简单表达式的库。
这个库可以处理简单的表达式,但不能处理代码块、控制流语句等。它支持大多数编程语言通用的语法。
在那里,我创建了一个代码示例来评估算术运算和数据比较。
import 'package:expressions/expressions.dart';
import 'dart:math';
@override
Widget build(BuildContext context) {
final parsing = FormulaMath();
// Expression example
String condition = "(cos(x)*cos(x)+sin(x)*sin(x)==1) && respuesta_texto == 'si'";
Expression expression = Expression.parse(condition);
var context = {
"x": pi / 5,
"cos": cos,
"sin": sin,
"respuesta_texto" : 'si'
};
// Evaluate expression
final evaluator = const ExpressionEvaluator();
var r = evaluator.eval(expression, context);
print(r);
return Scaffold(
body: Container(
margin: EdgeInsets.only(top: 50.0),
child: Column(
children: [
Text(condition),
Text(r.toString())
],
),
),
);
}
我/颤振(27188):真