javascript - 从对象内调用时使用 'eval' 定义顶级函数时出现问题

标签 javascript rhino

我已经(在 JavaScript 中)编写了一个交互式 read-eval-print-loop,它被封装
一个对象内。但是,我最近注意到解释器指定的顶级函数定义似乎没有被解释器“记住”。经过一些诊断工作,我将核心问题简化为:

   var evaler = {
     eval: function (str)
     {
       return eval(str);
     },
   };

   eval("function t1() { return 1; }");         // GOOD
   evaler.eval("function t2() { return 2; }");  // FAIL

在这一点上,我希望以下两个语句能按预期工作:
print(t1());     // => Results in 1 (This works)
print(t2());     // => Results in 2 (this fails with an error that t2 is undefined.)

我得到的是 t1 的预期值行和 t2行失败,错误为 t2是未绑定(bind)的。

IOW:运行此脚本后,我有一个 t1 的定义。 , 并且没有 t2 的定义.从 evaler 内部调用 eval 的行为与顶级调用有很大不同,因此全局定义不会被记录。发生的事情是调用evaler.eval返回一个函数对象,所以我假设 t2正在被定义并存储在我无权访问的其他一些绑定(bind)中。 (它没有被定义为 evaler 中的成员。)

有什么简单的解决方法吗?我已经尝试了各种修复方法,并没有偶然发现一种有效的方法。 (我所做的大部分工作都集中在将对 eval 的调用放在匿名函数中,并改变调用方式,链接 __parent__ 等)

关于如何解决这个问题的任何想法?

这是进一步调查的结果:

tl;dr:Rhino 在调用实例上的方法时会在作用域链中添加一个中间作用域。 t2被定义在这个中间范围内,立即被丢弃。 @Matt:您的“hacky”方法很可能是解决此问题的最佳方法。

我仍在为根本原因做一些工作,但是由于在 jdb 上度过了一段美好的时光,我现在对正在发生的事情有了更多的了解。如前所述,函数语句如 function t1() { return 42; }做两件事。
  • 它创建一个函数对象的匿名实例,就像您使用表达式 function() { return 42; } 获得的一样。
  • 它将匿名函数绑定(bind)到名称为 t1 的当前顶级作用域。 .

  • 我最初的问题是为什么我在调用 eval 时没有看到第二件事发生。从对象的方法中。

    在 Rhino 中实际执行绑定(bind)的代码似乎在函数 org.mozilla.javascript.ScriptRuntime.initFunction 中。 .
        if (type == FunctionNode.FUNCTION_STATEMENT) {
             ....
                    scope.put(name, scope, function);
    

    对于t1上例,scope是我设置的顶级范围。这是我想要定义我的顶级函数的地方,所以这是一个预期的结果:
    main[1] print function.getFunctionName()
     function.getFunctionName() = "t1"
    main[1] print scope
     scope = "com.me.testprogram.Main@24148662"
    

    然而,在 t2案例,scope完全是另一回事:
    main[1] print function.getFunctionName()
     function.getFunctionName() = "t2"
    main[1] print scope
     scope = "org.mozilla.javascript.NativeCall@23abcc03"
    

    它是 NativeCall 的父范围这是我预期的顶级范围:
    main[1] print scope.getParentScope()
     scope.getParentScope() = "com.me.testprogram.Main@24148662"
    

    这或多或少是我在上面写这个时所害怕的:“在直接 eval 的情况下,t2 被绑定(bind)在全局环境中。在 evaler 的情况下,它被绑定(bind)在 'elsewhere'” 在这种情况下,'elsewhere ' 结果是 NativeCall 的实例... t2函数被创建,绑定(bind)到 t2 NativeCall 中的变量,以及 NativeCall当调用 evaler.eval 时消失返回。

    这就是事情变得有点模糊的地方......我没有做我想做的那么多分析,但我目前的工作理论是NativeCall需要范围以确保 this指向 evaler在调用 evaler.eval 时执行. (稍微备份堆栈帧,NativeCallInterpreter.initFrame 添加到作用域链中,当函数“需要激活”并且具有非零函数类型时。我假设这些事情对于简单来说都是正确的仅函数调用,但尚未对上游进行足够的跟踪以确切知道。也许明天。)

    最佳答案

    您的代码实际上根本没有失败。 eval正在返回 function你永远不会调用它。

    print(evaler.eval("function t2() { return 2; }")()); // prints 2
    

    再详细说明一下:
    x = evaler.eval("function t2() { return 2; }"); // this returns a function
    y = x(); // this invokes it, and saves the return value
    print(y); // this prints the result
    

    编辑

    回应:

    Is there another way to create an interactive read-eval-print-loop than to use eval?



    由于您使用的是 Rhino.. 我想您可以使用 java Process 对象调用 Rhino 来使用 js 读取文件?

    假设我有这个文件:

    test.js
    function tf2() {
      return 2;
    }
    
    print(tf2());
    

    然后我可以运行这段代码,它会调用 Rhino 来评估该文件:
    process = java.lang.Runtime.getRuntime().exec('java -jar js.jar test.js');
    result = java.io.BufferedReader(java.io.InputStreamReader(process.getInputStream()));
    print(result.readLine()); // prints 2, believe it or not
    

    所以你可以更进一步,通过编写一些代码来评估文件,然后调用上面的代码......

    是的,这很荒谬。

    关于javascript - 从对象内调用时使用 'eval' 定义顶级函数时出现问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2999207/

    相关文章:

    java - Apache FOP 和 Java 热启动选项(使用 Rhino 效果更好)

    java - 无法使用 javascript 从 Java 中解析完整的 javascript if 语句

    unit-testing - 测试内部类

    Javascript - 根据封闭的 TD 内容隐藏表格行

    javascript - 如何使用带有超链接的翻译 __()

    javascript - Owl Carousel 100% 高度

    java - 为什么 Rhino (jsr-223) 不对作为对象属性的字符串求值?

    javascript - THREE.js:将摄影机从上方更改为第一人称

    javascript - 如何从客户端扩展 Firebase 身份验证登录 session ?

    javascript - 将 Rhino Scriptable bean 迁移到 Nashorn