繁体   English   中英

如何在Nashorn中跟踪变量依赖关系?

[英]How do I track variable dependencies in Nashorn?

我想将Nashorn引擎用作通用计算引擎。 它功能强大,快速,具有大量内置函数,并且使用@FunctionalInterface静态方法非常容易添加新函数。 更好的是,它还提供诸如循环依赖项检查,语法检查等增值。

但是,当依赖项更改时,我需要自动更新“输出”变量。

一般的想法是,在Java中,我会得到类似以下内容的信息:

class CalculationEngine {
   Data addData(String name, Number value){
       ...
   }
   Data addData(String name, String formula){
       ...
   }
   String getScript(){
       ...
   }
}

CalculationEngine engine = new CalculationEngine();
Data datum1 = engine.addData("datum1", 1); // Constant integer 1
Data datum2 = engine.addData("datum2", 2); // Constant integer 2
Data datum3 = engine.addData("datum3", "datum1*10");
Data datum4 = engine.addData("datum4", "datum3+datum2");

CalculationEngine服务类知道如何使用Nashorn从如下所示的Data对象中创建脚本字符串:

final String script = engine.getScript(); // "var datum1=1; var datum2=2; var datum3=datum1*10; var datum4=datum3+datum2;"

我知道我可以使用Nashorn Parser解析脚本:

final CompilationUnitTree tree = parser.parse("test", script, null);

但是我如何提取依赖关系:

List<Data> whatDependsOn(Data input){
   // Process the parsed tree 
   return list;
}

这样whatDependsOn(datum2)返回[datum4],而whatDependsOn(datum1)返回[datum3,datum4]吗?

或反函数getReferencedVariables ,使getReferencedVariables(datum3)返回[datum1], getReferencedVariables(datum4)返回[datum2,datum3](并且我可以递归查询getReferencedVariables直到找到所有引用的变量)。

基本上,当我的一个Data对象的“值”发生变化(由于外部事件)时,我如何确定哪个脚本公式受到影响并需要重新计算?

我知道可以解析Nashorn脚本,但是我不知道如何使用SimpleTreeVisitorES6建立变量依赖图:

    final CompilationUnitTree tree = parser.parse("test", script, null);

    if (tree != null) {
        tree.accept(new SimpleTreeVisitorES6<Void, Void>() {
            @Override
            public Void visitVariable(VariableTree tree, Void v) {
                final Kind kind = tree.getKind();
                System.out.println("Found a variable: " + kind);
                System.out.println(" name: " + kind.toString());
                IdentifierTree binding = (IdentifierTree) tree.getBinding();
                System.out.println(" kind: " + binding.getKind().name());
                System.out.println(" name: " + binding.getName());
                System.out.println(" val: " + kind.name());

                return null;
            }
         }, null);
    }  

Nashorn开发人员之一。 您想要做的是在源代码上计算所谓的def-use关系(很可能是它们的传递闭包,但是我离题了)。 这是一个很好理解的编译器理论概念。 好消息是, CompilationUnitTree和朋友应该为您提供足够的信息,以实现用于计算此信息的算法。 坏消息是,恐怕您将不得不袖手旁观并自行实施。 您基本上必须收集这些信息,在控制流连接点(循环的后边缘和出口,if语句的末端)产生合并,但是还必须处理更多奇特的东西,例如switch / case及其后缀语义和还尝试/捕获/最终,这是最不有趣的,因为基本上控制权可以从try块中的任何地方转移到catch块。)您的算法还必须反复评估循环主体,直到收集到的静态信息达到不动点。

FWIW,在编写Nashorn时,我不得不使用Nashorn的内部解析器API来实现这些事情几次(这是不同的,但类似于公共的)。 如果您想获得启发,可以查看Nashorn静态类型分析器源代码,以推断 JavaScript函数中的局部变量类型,这是我几年前写的。 如果没有其他问题,它将为您提供一个思路,如何走一棵AST树并跟踪控制流边缘以及边缘处的部分计算的静态分析数据。

我希望有一个更简单的方法可以执行此操作……FWIW,可以帮助您保持流控制的布尔值的通用静态分析器。 祝好运。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM