【问题标题】:Groovy: How to get statically inferred return type from ASTGroovy:如何从 AST 获取静态推断的返回类型
【发布时间】:2018-10-24 13:04:45
【问题描述】:

我使用 Groovy 允许用户编写脚本并自定义我的 Java 应用程序的某些部分。我正在使用所有用户提供的脚本的静态编译。

我的问题是:如果我的代码期望收到例如作为用户脚本的结果的 boolean 类型的值,我有什么方法可以检查提供的用户脚本是否总是在不实际执行脚本的情况下返回布尔值?换句话说,我如何访问静态 groovy 编译器执行的类型推断的结果?我希望能够在用户编辑脚本内容时告诉用户“嘿,您的脚本并不总是返回布尔值”。

【问题讨论】:

    标签: java groovy type-systems static-compilation


    【解决方案1】:

    没有直接的方法,但有可能。布尔值特别难,因为编译器会很高兴地应用各种类型强制转换(例如,将 int 转换为布尔值而不抱怨)。我遇到了完全相同的问题并执行了以下步骤:

    1. ASTTransformation 将返回类型设置为布尔值(不是必需的,是早期迭代的半成品)
    2. ASTTransformation 用于计量所有返回语句
    3. TypeCheckingExtension 访问 ReturnStatements 并验证它们是布尔类型
    4. ASTTransformation 使用 TypeCheckingExtension 执行静态类型转换

    对于 1:

    扩展ClassCodeVisitorSupport,在visitMethod 中标识所有要返回布尔值的方法(例如检查匹配的命名约定)

    MethodNodereturnType设置为ClassHelper.boolean_TYPE

    对于 2:

    与上述相同的方法调用org.codehaus.groovy.classgen.ReturnAdder.visitMethod

    对于 3:

    扩展AbstractTypeCheckingExtension,覆盖afterVisitMethod。此时AbstractTypeCheckingExtension 将推断出方法内所有表达式的下限。使用ClassCodeVisitorSupport 子类并覆盖visitReturnStatement。使用getType(statement.expression) 获取推断类型。请注意,这可能不同于 statement.expression.type(根据 AST 的类型)。在非布尔类型上调用 addStaticTypeError

    对于 4:

    扩展StaticTypesTransformation 覆盖newVisitor 并创建一个新的StaticTypeCheckingVisitor 并在其上调用addTypeCheckingExtension 以添加您的TypeCheckingExtension

    GitHub 项目

    https://github.com/MeneDev/groovy-dsl-building-blocks-enforce-return-type

    它甚至可以作为依赖重用;)

    【讨论】:

    • 感谢您的详细回复!我将尝试实施您的方法。在 github 上有这个例子肯定会有所帮助。唉,网上关于 Groovy 的这些方面的信息很少。
    • 包含了相当多的试验和错误。我会努力的!
    【解决方案2】:

    与“典型”groovy 不同——这是 groovy 的运行时类型和 java 的编译时静态类型之间的区别。

    例如,以下方法是否会返回布尔值?

    def value(v) {
        return v;
    }
    

    然而,在 2.0 版中,有一个 @CompileStatic 注释,我认为它会强制在编译时知道所有类型。不过,不确定如何为客户的脚本代码强制“开启”。

    【讨论】:

    • 是的,这正是我们所做的。您可以将静态编译添加为 AST Customizer,效果很好。我们面临的问题是如何访问分析结果,即类型注释的 AST。
    • 你能要求用户用他的代码实现一个特定的接口吗?如果他没有返回布尔值,那应该无法编译......如果用户代码更像是一个“脚本”,您可能可以在为他编译之前强制他的脚本实现一个接口。
    • 让用户自己实现一个接口无疑是太多的要求(语法上)。一种选择是在编译之前将方法签名包装在用户代码周围。但是,用户无法在脚本中定义辅助函数,因为方法不能嵌套在 Groovy AFAIK 中。 AST 转换没有字符串连接那么麻烦,但也可以完成工作。最干净的选择仍然是让脚本保持原样并分析 AST。问题是我不知道如何在 Groovy 编译器 API 中做到这一点。
    猜你喜欢
    • 2020-11-15
    • 2021-10-23
    • 2016-04-16
    • 1970-01-01
    • 2019-02-12
    • 1970-01-01
    • 2017-10-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多