【问题标题】:Get last evaluated expression inside function获取函数内的最后一个求值表达式
【发布时间】:2014-05-28 18:33:28
【问题描述】:

这与另一个问题有关:

Last evaluated expression in Javascript

但我想提供更多关于我想做的事情的详细信息,并展示我如何最终解决了一些用户在 cmets 中要求的问题。

我有我的应用程序用户编写的 Javascript 的 sn-ps。这个sn-ps需要去这样一种模板:

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

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

...

表达式可以非常不同,从简单的事情如下:

data.price * data.qty

像这样更复杂的事情:

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

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

在我们有这样的事情之前:

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

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

仅仅添加一个'return'关键字是行不通的,因为表达式可以有多个语句。所以我需要让这些函数返回最后评估的表达式。

限制和说明:

  • 我不能强制用户在他们拥有的所有脚本中添加“return”关键字,因为已经编写了许多脚本,对于“a * b”这样的简单表达式来说不是很直观。
  • 我正在使用 Java 和 Rhino 在服务器端运行 Javascript。

【问题讨论】:

    标签: java javascript rhino abstract-syntax-tree


    【解决方案1】:

    正如人们在 Last evaluated expression in Javascript 中指出的那样,在标准 Javascript 中无法获得最后一个评估的表达式。

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

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

    公共类 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;
    }
    

    }

    这样,如果你通过以下脚本:

    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;
    

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

    【讨论】:

      猜你喜欢
      • 2023-02-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-27
      相关资源
      最近更新 更多