自从开始使用Dart以来,我一直在寻找一种方法来执行Dart(Text)Source(同一程序可能会动态生成)作为Code。就像臭名昭着的“eval()”函数一样。
最近我发现了一些暗示,Isolates之间的通信端口支持某种“Spawn”,似乎它可能允许这种“技巧”。在Ruby中,还可以动态加载模块作为语言功能,也许在Dart中有一些方法可以做到这一点?
任何线索或简单的例子将不胜感激。
提前致谢!
答案 0 :(得分:9)
Ladislav Thon提供了这个答案(在Dart论坛上),
我相信说Dart永远不会有eval是非常安全的。但它将有其他更结构化的动态生成代码的方法(代码名称镜像构建器)。但是现在没有类似的东西了。
有两种产生隔离的方法:spawnFunction,它从新隔离区中的现有代码运行现有函数,所以没有你想要的东西,以及spawnUri,它从给定的URI下载代码并在新的隔离区中运行它。这本质上是动态代码加载 - 但动态加载的代码与现有代码隔离。它运行在一个新的隔离区中,因此与它通信的唯一方法是通过消息传递(通过端口)。
谢谢Ladislav ......
答案 1 :(得分:3)
您可以通过首先从字符串构造数据URI,然后将其传递到Isolate.spawnUri
中来将字符串作为Dart代码运行。
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命令行应用程序/ package:build脚本。在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);
}
答案 2 :(得分:0)
仔细观察问题,至少有三种不同的函数位于可能使用eval的各种方案的基础上。 Dart至少以最小的方式处理其中至少2个。
Dart没有,也没有任何计划支持"一般"脚本执行。
然而,NoSuchMethod方法可用于有效地实现动态"注射"变量到您的本地类环境中。它用一个如下所示的字符串替换eval():eval(" String text ='你的名字在这里&#39 ;;");
Dart现在支持的第二个函数是调用方法,如下所示:eval(" Map map = SomeClass.some_method()");
搞砸了之后,我终于明白了一个简单的类可以用来存储调用一个方法所需的信息,对于一个类来说,它似乎具有一般的实用性。我可以替换一个大的易维护的switch语句,否则可能需要调用一系列方法。在Ruby中,这几乎是微不足道的,但是在Dart中有一些不那么直观的调用,所以我想得到这个"技巧"在一个地方,这适合将对您可能需要的字符串进行排序和过滤。这里的代码是"积累"使用反射将多个类(一个完整的库?)放入一个映射中,这样只需要一个键(作为一个字符串)就可以调用class.methodName()。
注意:我使用了一些"辅助方法"做地图&列表功能,您可能希望用直接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() );
}