Groovy: How to get statically inferred return type from AST

393 Views Asked by At

I'm using Groovy to allow users to script and customize certain parts of my Java application. I am using static compilation of all user-provided scripts.

My question is: if my code expects to receive e.g. a value of type boolean as the result of the user script, is there any way for me to check whether or not the provided user script will always return a boolean without actually executing the script? In other words, how can I access the results of the type inference performed by the static groovy compiler? I would like to be able to tell the user "hey, your script does not always return a boolean" while they are editing the script content.

2

There are 2 best solutions below

3
On BEST ANSWER

There is no straight-forward way, but it's possible. Boolean is especially hard as the compiler will happily apply all sorts of type-coersions (e.g. transform an int to boolean without complaining). I've had the exact same problem and did the following steps:

  1. ASTTransformation to set the return type to boolean (not required, was an artifact of a half-working earlier iteration)
  2. ASTTransformation to meterialize all return statements
  3. TypeCheckingExtension to visit the ReturnStatements and verify they are of type boolean
  4. ASTTransformation to perform static type transformation with the TypeCheckingExtension

For 1:

Extend ClassCodeVisitorSupport, in visitMethod identify all methods you want to return boolean (e.g. check for matching naming conventions)

Set the returnType of the MethodNode to ClassHelper.boolean_TYPE

For 2:

For the same methods as above call org.codehaus.groovy.classgen.ReturnAdder.visitMethod

For 3:

Extend AbstractTypeCheckingExtension, override afterVisitMethod. At this point the AbstractTypeCheckingExtension will have inferred the lower bound of all expressions inside the method. Use a ClassCodeVisitorSupport subclass and override visitReturnStatement. Use getType(statement.expression) to get the inferred type. Note that this can be different from statement.expression.type (the type according to the AST). Call addStaticTypeError on non boolean type.

For 4:

Extend StaticTypesTransformation Override newVisitor and create a new StaticTypeCheckingVisitor and call addTypeCheckingExtension on it to add your TypeCheckingExtension

GitHub Project

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

It may even be reusable as a dependency ;)

3
On

Not with "Typical" groovy--it's the difference between groovy's runtime typing and java's compile-time static typing.

For instance, is the following method going to return a Boolean?

def value(v) {
    return v;
}

In version 2.0, however, there IS a @CompileStatic annotation that I think would force all types to be know at compile time. Not sure how you would force this "On" for your client's script code though.