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

Java11中的Nashorn在计算命名函数时的行为与Java8不同

郑西岭
2023-03-14

我有一个Java应用程序,允许用户通过定义JavaScript函数在运行时操作某些对象。我们目前正在用Java8中的Nashorn来实现这一点,但我们正在考虑转移到Java11。一旦我们使用了Java11,我们就可以在GraalVM中提供这个功能,但是现在我们需要保持Java8的兼容性-

在Java11中,当我们评估函数时,Nashorn的行为似乎会根据函数是否命名而有所不同,这在Java8中并非如此。以下是在Java11中使用JJS的示例

$ jjs -v
nashorn 11.0.6
Warning: The jjs tool is planned to be removed from a future JDK release
jjs> function foo() {}
jjs> function () {}
function () {}

请注意,第一个函数定义不返回任何内容。在Java8中,即使函数被命名,它也确实返回函数:

$ jjs -v
nashorn 1.8.0_252
jjs> function foo() {}
function foo() {}

我们目前调用这些脚本的方式是:

CompiledScript compiled = scriptEngine.compile(userProvidedScript);
Object evaled = compiled.eval(bindings);
scriptEngine.invokeMethod(evaled, "call", evaled, ... input parameters ...)

好奇的是,是否有人知道这一问题的根本原因以及任何好的解决办法?我需要支持函数(…) 以及函数foo(…) 出于后台原因。由于这是在我们的Java应用程序中完成的,我们可能会以某种方式包装用户提供的脚本,或者尝试从绑定中获取脚本(这似乎很容易出错,因为可以定义多个脚本,Java 8行为将用于调用最后定义的脚本)。


共有1个答案

司徒俊雄
2023-03-14

可能是因为Nashorn的匿名函数语句(实际上称为“函数声明”)的自定义功能也意外应用于实名函数声明。由于Nashorn文档中没有描述这种行为,我想他们不希望这种行为,并摆脱了它。

解决方案:

转换源代码,以便作为其最终行为,它产生由您最后命名的函数声明定义的函数对象。

考虑我的测试,看看这将在OpenJDK 8和11中工作:

nashorn@1.8.0_302:

jjs> function bar() { foo(); }; function foo() { bar(); }     
function foo() { bar(); }

jjs> function bar() { foo(); }; function foo() { bar(); }; foo
function foo() { bar(); }

nashorn@11.0.6:

jjs> function bar() { foo(); }; function foo() { bar(); }

jjs> function bar() { foo(); }; function foo() { bar(); }; foo
function foo() { bar(); }

要弄清楚使用什么名称,您应该能够解析JS并使用jdk.nashorn.api.tree包处理其AST。

您的树访问者/函数名称累加器可能如下所示:

private static class FuncDeclarationVisitor extends SimpleTreeVisitorES5_1<Void, Void> {
    public String lastFunctionName = null;

    @Override
    public Void visitFunctionDeclaration(FunctionDeclarationTree node, Void param) {
        // Anonymous function declaration ==> null
        IdentifierTree functionName = node.getName();
        lastFunctionName = functionName != null ? functionName.getName() : null;
        return super.visitFunctionDeclaration(node, param);
    }
}

你可以这样调用它:

CompilationUnitTree parsedTree;     // Use Parser's parse method

FuncDeclarationVisitor visitor = new FuncDeclarationVisitor();
parsedTree.accept(visitor);

return visitor.lastFunctionName;     // Null if the last function declaration was anonymous

但我认为没有任何方法可以修改AST,然后将其发送到NashornScriptEngine的编译器。您可能需要在源文本本身中添加一些内容。

此外,您可能还希望访问者检测到其他类型的节点,这些节点不是函数声明,这样您就不会意外地转换脚本,并破坏可能产生的其他表达式(非函数)。

 类似资料:
  • 我想为Nashorn提供一个函数,比如: 上面的代码不编译,因为需要一个对象,而lambdas不是对象。我如何将javascript名称设置为java函数? 编辑澄清: 在JavaScript中,我们可以这样写: 如果我用Java编写了,我如何将该函数分配给JavaScript变量?

  • 在GCP nodejs云函数中,我使用dotenv在简单模式下重命名运行时触发器调用的函数:我通过dotenv-yaml从env_TEST. yml配置文件中读取部署函数的名称 PROD云函数触发器称为prod_fixNameFunction UAT云函数触发器称为uat_fixNameFunction 称为test_fixNameFunction的TEST云函数触发器 因为我有3.env_PRO

  • 主要内容:1 Java8 JavaScript Nashorn的介绍,2 案例:使用终端执行,3 案例:Java代码执行JavaScript文件,4 案例:Java源代码嵌入JavaScript代码,5 案例:嵌入JavaScript表达式,6 案例:JavaScript文件的heredocs,7 案例:Java文件中设置JavaScript变量1 Java8 JavaScript Nashorn的介绍 Nashorn是JavaScript引擎。它用于在JVM(Java虚拟机)上动态执行Java

  • 基本上每次for循环运行时,我都希望创建一个具有新名称的数组。所以在程序的末尾,我可以打印出包含所有变量的每一个数组。这里是我目前为止创建的数组的代码,但是每次运行循环语句时,都会覆盖数组的编写。

  • 本文向大家介绍在使用计算属性的时,函数名和data数据源中的数据可以同名吗?相关面试题,主要包含被问及在使用计算属性的时,函数名和data数据源中的数据可以同名吗?时的应答技巧和注意事项,需要的朋友参考一下 莫名其妙的问题。可以同名,但data会覆盖methods。并且本就不该同名,同名说明你命名不规范。 然后解释为什么会覆盖,因为Props、methods、data、computed、watch

  • 有时候,能够知道一个计算执行消耗的时间是非常有意义的,尤其是在对比和基准测试中。最简单的一个办法就是在计算开始之前设置一个起始时候,再由计算结束时的结束时间,最后取出它们的差值,就是这个计算所消耗的时间。想要实现这样的做法,可以使用 time 包中的 Now() 和 Sub 函数: start := time.Now() longCalculation() end := time.Now() de