Reputation: 495
I have a scripting system where depending on where the script is executed you have access to different variables. I also want to have inferred types for a type of Auto-Completion for the script editor.
But when the types are inferred during the compile phase, I have no way of giving a Binding which explains to the compilation phase what types those dynamic variables have.
I have currently solved this by:
Not compiling the code with either @TypeChecked
nor @CompileStatic
but later manually running a subclassed StaticCompilationVisitor
on the dynamically typed codebase and manually filling in the StaticTypesMarker.INFERRED_TYPE
inside visitVariableExpression()
for the dynamic variables that I know exists.
However, this seems like the wrong way to go about it, and I would actually like to work with the VariableScope
instead. But it seems to be under rough lockdown inside the VariableScopeVisitor
, so it's difficult to pop in a CustomVariableScope
that dynamically does the lookups. I have managed to do this with reflection, replacing the VariableScopeVisitor
inside CompilationUnit
and currentScope
and such inside VaribleScopeVisitor
. It works, but I don't like working against hard-coded private field names.
This might be a long-winded way of asking: Is there an official way of handling a situation of static typing with dynamic variables? I cannot do this by setting scriptBaseClass
for reasons too complex to explain here.
If the question is unclear, please tell me and I'll try to edit in better explanations.
Upvotes: 0
Views: 104
Reputation: 495
The answer was to add a GroovyTypeCheckingExtensionSupport
to a StaticTypeCheckingVisitor
and then use visitClass
on the first ClassNode
of the CompilationUnit
.
final ClassNode classNode = this.compilationUnit.getFirstClassNode();
final StaticCompilationVisitor visitor = new StaticCompilationVisitor(this.sourceUnit, classNode);
visitor.addTypeCheckingExtension(new MyGroovyTypeCheckingExtensionSupport(visitor, this.compilationUnit));
visitor.visitClass(classNode);
visitor.performSecondPass();
And create something like the class below:
private static class MyGroovyTypeCheckingExtensionSupport extends GroovyTypeCheckingExtensionSupport {
private static final ClassNode CLASSNODE_OBJECT = ClassHelper.make(Object.class);
public MyGroovyTypeCheckingExtensionSupport(StaticTypeCheckingVisitor typeCheckingVisitor, CompilationUnit compilationUnit) {
super(typeCheckingVisitor, "", compilationUnit);
}
@Override
public boolean handleUnresolvedVariableExpression(VariableExpression vexp) {
final ClassNode type = this.getType(vexp);
if (type == null || type.equals(CLASSNODE_OBJECT)) {
if (vextp.getName().equals("something")) {
this.storeType(vexp, ClassHelper.make(SomeClass.class));
return true;
}
}
return false;
}
}
Upvotes: 1