从对象内调用时,使用’eval’定义*函数时遇到麻烦

我已经用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未绑定.

IOW:运行此脚本后,我为t1定义了一个,而对t2没有定义.从逃避者内部调用eval的行为与顶层调用充分不同,因此不会记录全局定义.确实发生的是
evaler.eval返回一个函数对象,因此我假设正在定义t2并将其存储在我无法访问的其他绑定集中. (未将其定义为逃避程序的成员.)

有没有简单的解决办法?我尝试了各种修复方法,但没有偶然发现一种可行的方法. (我所做的大部分工作都集中在将对eval的调用放在一个匿名函数中,并更改了调用方式,将__parent__等链接起来.)

有关如何解决此问题的任何想法?

这是更多调查的结果:

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

我仍然在根本原因上进行一些工作,但是由于使用了jdb花费了很多时间,我现在对发生的事情有了更多的了解.正如已经讨论过的,类似函数t1()的函数语句{return 42; }做两件事.

>它会创建一个函数对象的匿名实例,就像使用表达式function()得到的一样.return 42; }
>它将匿名函数绑定到名称为t1的当前*范围.

我最初的问题是,当我从对象的方法中调用eval时为什么看不到第二种情况.

在Rhino中实际执行绑定的代码似乎在函数org.mozilla.javascript.ScriptRuntime.initFunction中.

    if (type == FunctionNode.FUNCTION_STATEMENT) {
         ....
                scope.put(name, scope, function);

对于上面的t1案例,范围是我设置为*范围的内容.这是我要定义*功能的地方,因此这是预期的结果:

main[1] print function.getFunctionName()
 function.getFunctionName() = "t1"
main[1] print scope
 scope = "com.me.testprogram.Main@24148662"

但是,在t2情况下,范围完全是另外一回事:

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"

这或多或少是我在上面撰写本文时所担心的:“在直接评估的情况下,t2绑定在全球环境中.在逃避者的情况下,t2绑定在“其他地方””在这种情况下,“其他地方”原来是NativeCall的实例…创建了t2函数,绑定到NativeCall中的t2变量,当对evaler.eval的调用返回时,NativeCall消失.

这就是事情变得有点模糊的地方…我没有做我想做的很多分析,但是我目前的工作理论是需要NativeCall范围来确保在调用执行时可以避免这种情况逃避评估(稍微备份堆栈帧,当函数“需要激活”并且具有非零函数类型时,NativeCall将由Interpreter.initFrame添加到作用域链中.我假设这些对于简单的函数调用都是正确的仅,但还没有足够的溯源来确定(也许明天).

解决方法:

您的代码实际上根本没有失败.评估将返回您从未调用的函数.

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

因此,您可以通过编写一些代码以评估到文件中,然后调用上面的代码来进一步执行此操作…

是的,这太荒谬了.

上一篇:java-在Rhino中返回主机对象


下一篇:javascript-快速查看Rhino Shell中的对象(等同于PHP var_dump?)