获取函数内部最后计算的表达式 [英] Get last evaluated expression inside function

查看:93
本文介绍了获取函数内部最后计算的表达式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这与另一个问题有关:

最后用Java语言评估的表达式

但是我想提供更多有关我想做的事情的详细信息,并说明如何最终解决该问题。一些用户在评论中要求。

But I wanted to provide more details about what I wanted to do and show how I finally solved the problem as some users requested in the comments.

我有一些由我的应用程序用户编写的Javascript代码段。此代码段需要转到这样的模板:

I have snippets of Javascript that are written by users of my app. This snippets need to go to a kind of template like this:

var foo1 = function(data, options) {
    <snippet of code written by user>  
}

var foo2 = function(data, options) {
    <snippet of code written by user>  
}

...

表达式可能会非常不同,例如:

Expressions can be very different, from simple things like this:

data.price * data.qty

像这样更复杂的东西:

if (data.isExternal) {
    data.email;
} else {
    data.userId;
}

该函数返回的值应始终为最后计算的表达式。

The value returned by the function should be always the last evaluated expression.

在我们遇到这样的事情之前:

Before we had something like this:

var foo1 = function(data, options) {
    return eval(<snippet of code written by user>);
}

但是由于我们进行了优化和更改,我们无法继续使用eval,但是我们需要返回最后一个求值的表达式。

But due to optimizations and changes we are making, we cannot keep using eval, but we need to return the last evaluated expression.

仅添加'return'关键字将不起作用,因为表达式可以包含多个语句。因此,我需要使这些函数返回最后计算的表达式。

Just adding a 'return' keyword won't work because expressions can have several statements. So I need to make those functions return the last evaluated expressions.

限制和说明:


  • 我不能强迫用户将'return'关键字添加到他们拥有的所有脚本中,因为已经编写了许多脚本,对于像'a * b'这样的简单表达式来说也不是很直观。

  • 我正在使用Java和Rhino在服务器端运行Javascript。

推荐答案

正如人们在 Java中最后评估的表达式中指出的那样,获取最后一个评估的表达式在标准Javascript中是不可能的。

As people pointed out in Last evaluated expression in Javascript, getting the last evaluated expression is not possible in standard Javascript.

正如FelixKling所建议的,我最终要做的是操纵用户编写的脚本的AST。这样,我存储了用户编写的脚本和修改后的版本,这是我最终运行的版本。

What I finally ended up doing, as suggested by FelixKling, was to manipulate the AST of the script written by the user. This way I store the user written script and the modified version, which is the one I finally run.

为了操纵AST,我使用了Rhino并基本上将所有EXPR_RESULT节点修改为将结果存储在变量中,最后在脚本末尾返回。这是执行此操作的代码:

For manipulating the AST I used Rhino and basically modify all EXPR_RESULT nodes to store the result in a variable that I finally return at the end of the script. Here is the code to do that:

公共类ScriptsManipulationService {
private static final Logger logger = LoggerFactory.getLogger(ScriptsManipulationService.class);

public class ScriptsManipulationService { private static final Logger logger = LoggerFactory.getLogger(ScriptsManipulationService.class);

public String returnLastExpressionEvaluated(String script) {
    Parser jsParser = new Parser();
    try {
        AstRoot ast = jsParser.parse(script, "script", 1);
        ast.getType();
        ast.visitAll(new NodeVisitor() {
            @Override
            public boolean visit(AstNode node) {
                if (node.getType() == Token.EXPR_RESULT) {
                    ExpressionStatement exprNode = (ExpressionStatement) node;
                    Assignment assignmentNode = createAssignmentNode("_returnValue", exprNode.getExpression());
                    assignmentNode.setParent(exprNode);
                    exprNode.setExpression(assignmentNode);
                }
                return true;
            }
        });
        StringBuilder result = new StringBuilder();
        result.append("var _returnValue;\n");
        result.append(ast.toSource());
        result.append("return _returnValue;\n");
        return result.toString();
    } catch (Exception e) {
        logger.debug(LogUtils.format("Error parsing script"), e);
        return script;
    }
}

private Assignment createAssignmentNode(String varName, AstNode rightNode) {
    Assignment assignmentNode = new Assignment();
    assignmentNode.setType(Token.ASSIGN);
    Name leftNode = new Name();
    leftNode.setType(Token.NAME);
    leftNode.setIdentifier(varName);
    leftNode.setParent(assignmentNode);
    assignmentNode.setLeft(leftNode);
    rightNode.setParent(assignmentNode);
    assignmentNode.setRight(rightNode);
    return assignmentNode;
}

}

这样,如果您传递以下脚本:

This way, if you pass the following script:

data.price * data.qty;

您将回来:

var _returnValue;
_returnValue = data.price * data.qty;
return _returnValue;

或者如果您通过:

var _returnValue;
if (data.isExternal) {
    _returnValue = data.email;
} else {
    _returnValue = data.userId;
}
return _returnValue;

请记住,我还没有进行详尽的测试,并且会随着时间的推移进行完善,但这应该可以显示总体思路。

Please keep in mind that I haven't done an exhaustive testing and will be polishing it over time, but this should show the general idea.

这篇关于获取函数内部最后计算的表达式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆