当前位置: 首页 > 知识库问答 >
问题:

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

纪辰沛
2023-03-14

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

有两种类型的脚本。“简单”的函数只包含一个进程函数,而复杂的函数目前的结构类似于下面的示例(可能有更多私有函数/字段):

// 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是服务器应用程序将使用的“公共”功能<加载/评估文件后,将执行一次代码>自检,并在需要时对传入数据执行过程

我从旧的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引擎实例中,因此我的映射中不能有多个“process”方法
我可以继续这样做,并根据列名动态生成函数和全局变量的名称前缀。。。但那是。。。“丑”。。。

我尝试过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");

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

编辑(解决方案):

稍微修改了奥列格的答案,这似乎很有效。。。

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

共有1个答案

鲜于璞瑜
2023-03-14

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

当你在评估这样的来源时:

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

并同样访问自检功能。您需要为此修改JS源代码可能并不理想,但我认为这是必要的。

 类似资料:
  • 我已按以下格式设置文件夹: AAPL_176546(空子文件夹)AAPL_2000(空子文件夹)AAPL_30234(空子文件夹)AAPL_176546.mp3AAPL_176546.txtAAPL_2000.mp3AAAPL_2000.txtAAPL_30234.mp3AAPL_30234.txt 我希望遍历文件夹并将相应的.txt和.mp3文件移动到子文件夹中(例如move AAPL_1765

  • 我是java新手,我在VSCODE上编码。我创建2.java文件,如下图所示: 这些是每个文件: Main.java:

  • 问题内容: 我想调用一个在second.js文件的first.js文件中定义的函数。这两个文件都在HTML文件中定义,例如: 我想打电话给在定义中。根据我的搜索结果,答案是首先定义是否可以,但是根据我的测试,我没有找到任何方法。 这是我的代码: 问题答案: 除非在同一文件中定义了该函数,或者在尝试调用该函数之前已加载了该函数,否则无法调用该函数。 除非函数的范围与尝试调用该函数的范围相同或更大,否

  • 本文向大家介绍js下载文件并修改文件名,包括了js下载文件并修改文件名的使用技巧和注意事项,需要的朋友参考一下 用js下载文件,使用<a>标签,添加download属性即可。 但是如果想给文件重新命名,貌似js无法实现。 因此考虑后台实现,用java代理请求,获取文件设置文件名,返回到前端。 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持呐喊教程。

  • 本文向大家介绍js is_valid_filename验证文件名的函数,包括了js is_valid_filename验证文件名的函数的使用技巧和注意事项,需要的朋友参考一下 函数代码 windows 系统文件命名规则限制 https://msdn.microsoft.com/en-us/library/aa365247

  • 我正在尝试构建一个预算计算器来练习python。目前,我正在尝试迭代目录中的文件,然后将每个文件通过一个函数传递,以将我需要的数据提取到DataFrame(准备让它对其执行计算)。 我已经设法创建了清理数据的函数,以及迭代文件的for循环。但是,我不知道如何为每次迭代附加DataFrame。 当我运行此代码时,只有一个文件被添加到DataFrame,而不是所有文件。 提前感谢您的任何帮助