如何在java/gralvm中加载具有多个函数(每个文件具有相同名称)的js文件,并按文件名调用函数



我有一个服务器应用程序,它在启动时加载几个脚本文件(用于处理特定的数据集字段(。脚本应该被解析;表达式"-脚本的数据应该存储在映射中(按列名(,以便以后可以从那里访问和执行。

有两种类型的脚本。";简单的";其中只包含一个process函数,而复杂的当前结构类似于下面的示例(可能有更多的私有函数/字段(:

// public
function process(input) {
return _doSomething(input);
}
function selfTest() {
if (process("123") !== "123") throw "failed";
// ...
}
// private
var _allowedSymbols = ['H', 'L', 'M'];      
function _doSomething(input) {
// _allowedSymbols is used here
}

CCD_ 2和CCD_;"公共";服务器应用程序将使用的功能。selfTest将在文件加载/评估后执行一次,process将在需要时对传入数据执行。


我从旧的JSR223方式开始:

ScriptEngine engine = new ScriptEngineManager().getEngineByName("graal.js");
engine.eval("function process(input) { return input.toUpperCase(); }");
// engine.eval("function process(input) { return input + '123'; }");
Invocable inv = (Invocable) engine;
Object result = inv.invokeFunction("process", "peter");

这种方法的问题是函数数据存储在javascript引擎实例中,因此我不能有多个";过程";方法
我可以继续这样做,并根据列名动态生成函数和全局变量的名称前缀。。。但那"丑陋的">


我尝试过graalvm上下文方式(在SO和Oleg的帮助下,如何从ScriptManager中存储函数句柄以备将来使用?(:

var ctx = Context.newBuilder("js").allowAllAccess(false).build();
var src = Source.newBuilder("js", "(function u(input) { return input.toUpperCase(); })", "test.js").build();
var script = ctx.eval(src);
var result = script.execute("peter");

这适用于";简单的";功能。但是对于复杂的脚本,上面的函数表达方式是行不通的。


EDIT(解决方案(:

稍微修改了奥列格的答案,这似乎起到了作用。。。

var jsCode = """
(function() {
function process(input) { return input; }
function selfTest() { if (process("123") !== "123") throw "failed"; return true; }
return { process, selfTest }; 
})();
""";
var ctx2 = Context.newBuilder("js").allowAllAccess(false).build();
Source source = Source.newBuilder("js", jsCode, "test.js").build();
var script = ctx2.eval(source);
var fnProcess = script.getMember("process");
var result = fnProcess.execute("123");
var fnSelfTest = script.getMember("selfTest");
var result2 = fnSelfTest.execute();

要么函数是在顶级命名空间中声明的,然后名称冲突是一个问题,要么它们在它们的自定义作用域中,然后您必须有某种方法来访问和调用它们。

当你评估这样的来源时:

(function u(input) { return input.toUpperCase(); })

该评估的结果是脚本中的最后一个表达式。你可以想到这句话:

var result = ctx.eval("js", "(function u(input) { return input.toUpperCase(); })");

大约就像是在JS中一样:

result = (function u(input) { return input.toUpperCase(); })

因此,您可以使用运行它

result("HelloWorld"); 

这意味着您可以使用辅助对象返回多个函数:

// public
function process(input) {
return _doSomething(input);
}
function selfTest() {
if (process("123") !== "123") throw "failed";
// ...
}
// private
var _allowedSymbols = ['H', 'L', 'M'];      
function _doSomething(input) {
// _allowedSymbols is used here
}
returnMe = {process, selfTest}; 

这正是你在JavaScript中会做的(我认为(。

然后你可以用获得处理功能

var returnedObject = ctx.eval(src);
var processFunction = returnedObject.getMember("process");
var result = processFunction.execute("peter");

并类似地访问selfTest功能。需要为此修改JS源代码可能并不理想,但我认为这是必要的。

最新更新