如何在java/graalvm中加载具有多个函数(每个文件同名)的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
}

processselfTest 是服务器应用程序将使用的“公共”函数。 selfTest 将在文件加载/评估后执行一次,并在需要时为传入数据执行 process


我已经开始使用旧的 JSR 223 方式:

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-context-way(在 SO 和 Oleg 的帮助下,How to store function handles from ScriptManager for later usage?):

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");

这适用于“简单”的功能。但是对于复杂的脚本,上面的函数表达式方式不起作用。


编辑(解决方案):

稍微修改了 Oleg 的答案,这似乎可以完成工作......

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();
zyp0374 回答:如何在java/graalvm中加载具有多个函数(每个文件同名)的js文件并通过文件名调用函数

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

当您评估这样的来源时:

(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 源代码可能并不理想,但我认为这是必要的。

本文链接:https://www.f2er.com/295725.html

大家都在问