Skip to content

Commit d85d5f0

Browse files
committed
Fix runtime type reflection in plugin definitions
Signed-off-by: Ben Sherman <bentshermann@gmail.com>
1 parent cab6950 commit d85d5f0

10 files changed

Lines changed: 98 additions & 28 deletions

File tree

build.gradle

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ configurations {
3434
}
3535

3636
dependencies {
37-
implementation 'io.nextflow:nf-lang:26.03.1-edge'
37+
implementation 'io.nextflow:nf-lang:26.03.4-edge'
3838
implementation 'org.apache.groovy:groovy:4.0.31'
3939
implementation 'org.apache.groovy:groovy-json:4.0.31'
4040
implementation 'org.apache.groovy:groovy-yaml:4.0.31'
@@ -46,14 +46,14 @@ dependencies {
4646
runtimeOnly 'org.yaml:snakeyaml:2.2'
4747

4848
// include Nextflow runtime at build-time to extract language definitions
49-
nextflowRuntime 'io.nextflow:nextflow:26.03.1-edge'
50-
nextflowRuntime 'io.nextflow:nf-amazon:3.8.1'
49+
nextflowRuntime 'io.nextflow:nextflow:26.03.4-edge'
50+
nextflowRuntime 'io.nextflow:nf-amazon:3.9.0'
5151
nextflowRuntime 'io.nextflow:nf-azure:1.22.2'
52-
nextflowRuntime 'io.nextflow:nf-google:1.27.1'
53-
nextflowRuntime 'io.nextflow:nf-k8s:1.5.1'
54-
nextflowRuntime 'io.nextflow:nf-seqera:0.16.0'
55-
nextflowRuntime 'io.nextflow:nf-tower:1.23.0'
56-
nextflowRuntime 'io.nextflow:nf-wave:1.19.0'
52+
nextflowRuntime 'io.nextflow:nf-google:1.27.2'
53+
nextflowRuntime 'io.nextflow:nf-k8s:1.5.2'
54+
nextflowRuntime 'io.nextflow:nf-seqera:0.19.0'
55+
nextflowRuntime 'io.nextflow:nf-tower:1.26.0'
56+
nextflowRuntime 'io.nextflow:nf-wave:1.20.0'
5757

5858
testImplementation ('org.objenesis:objenesis:3.4')
5959
testImplementation ('net.bytebuddy:byte-buddy:1.14.17')

src/main/java/nextflow/lsp/ast/LanguageServerASTUtils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import nextflow.script.ast.IncludeEntryNode;
2424
import nextflow.script.ast.ProcessNode;
2525
import nextflow.script.ast.WorkflowNode;
26-
import nextflow.script.types.Types;
26+
import nextflow.script.dsl.Types;
2727
import org.codehaus.groovy.ast.ASTNode;
2828
import org.codehaus.groovy.ast.ClassNode;
2929
import org.codehaus.groovy.ast.FieldNode;

src/main/java/nextflow/lsp/services/config/ConfigAstCache.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
import nextflow.lsp.spec.PluginSpecCache;
3535
import nextflow.script.control.PhaseAware;
3636
import nextflow.script.control.Phases;
37-
import nextflow.script.types.Types;
37+
import nextflow.script.dsl.Types;
3838
import org.codehaus.groovy.ast.ASTNode;
3939
import org.codehaus.groovy.control.CompilerConfiguration;
4040
import org.codehaus.groovy.control.SourceUnit;

src/main/java/nextflow/lsp/services/config/ConfigCompletionProvider.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package nextflow.lsp.services.config;
1717

18+
import java.lang.reflect.Type;
1819
import java.net.URI;
1920
import java.util.ArrayList;
2021
import java.util.Collections;
@@ -28,7 +29,7 @@
2829
import nextflow.lsp.ast.CompletionHelper;
2930
import nextflow.lsp.services.CompletionProvider;
3031
import nextflow.lsp.util.Logger;
31-
import nextflow.script.types.Types;
32+
import nextflow.script.dsl.Types;
3233
import org.codehaus.groovy.ast.ASTNode;
3334
import org.codehaus.groovy.ast.ClassNode;
3435
import org.codehaus.groovy.ast.VariableScope;
@@ -227,7 +228,7 @@ private static CompletionItem configScopeBlock(String name, String description)
227228
return item;
228229
}
229230

230-
private static CompletionItem configOption(String name, String description, Class type) {
231+
private static CompletionItem configOption(String name, String description, Type type) {
231232
var documentation = StringGroovyMethods.stripIndent(description, true).trim();
232233
var item = new CompletionItem(name);
233234
item.setKind(CompletionItemKind.Property);

src/main/java/nextflow/lsp/services/config/ConfigHoverProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
import nextflow.lsp.ast.LanguageServerASTUtils;
2828
import nextflow.lsp.services.HoverProvider;
2929
import nextflow.lsp.util.Logger;
30-
import nextflow.script.types.Types;
30+
import nextflow.script.dsl.Types;
3131
import org.codehaus.groovy.ast.ASTNode;
3232
import org.codehaus.groovy.ast.stmt.Statement;
3333
import org.codehaus.groovy.runtime.DefaultGroovyMethods;

src/main/java/nextflow/lsp/services/config/ConfigSpecVisitor.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@
1515
*/
1616
package nextflow.lsp.services.config;
1717

18+
import java.lang.reflect.ParameterizedType;
19+
import java.lang.reflect.Type;
1820
import java.util.ArrayList;
21+
import java.util.Arrays;
1922
import java.util.HashMap;
2023
import java.util.List;
2124
import java.util.Map;
@@ -40,6 +43,7 @@
4043
import org.codehaus.groovy.ast.ASTNode;
4144
import org.codehaus.groovy.ast.ClassHelper;
4245
import org.codehaus.groovy.ast.ClassNode;
46+
import org.codehaus.groovy.ast.GenericsType;
4347
import org.codehaus.groovy.ast.expr.ClosureExpression;
4448
import org.codehaus.groovy.ast.expr.ConstantExpression;
4549
import org.codehaus.groovy.ast.expr.Expression;
@@ -168,7 +172,7 @@ public void visitConfigAssign(ConfigAssignNode node) {
168172
}
169173
// validate type
170174
var expectedTypes = option.types().stream()
171-
.map(t -> ClassHelper.makeCached(t).getPlainNodeReference())
175+
.map(t -> fromType(t))
172176
.toList();
173177
var actualType = inferredType(node.value, names);
174178
if( !isAnyAssignableFrom(expectedTypes, actualType) ) {
@@ -182,6 +186,23 @@ public void visitConfigAssign(ConfigAssignNode node) {
182186
}
183187
}
184188

189+
private ClassNode fromType(Type type) {
190+
if( type instanceof Class c ) {
191+
return ClassHelper.makeCached(c).getPlainNodeReference();
192+
}
193+
194+
if( type instanceof ParameterizedType pt ) {
195+
var cn = fromType(pt.getRawType());
196+
var gts = Arrays.stream(pt.getActualTypeArguments())
197+
.map(t -> new GenericsType(fromType(t)))
198+
.toArray(GenericsType[]::new);
199+
cn.setGenericsTypes(gts);
200+
return cn;
201+
}
202+
203+
return ClassHelper.dynamicType();
204+
}
205+
185206
private ClassNode inferredType(Expression node, List<String> scopes) {
186207
new TypeCheckingVisitorEx(sourceUnit).visit(node);
187208
var type = TypeCheckingUtils.getType(node);

src/main/java/nextflow/lsp/services/script/ScriptAstCache.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@
4444
import nextflow.script.control.ScriptResolveVisitor;
4545
import nextflow.script.control.TypeCheckingVisitor;
4646
import nextflow.script.control.TypeCheckingVisitorEx;
47+
import nextflow.script.dsl.Types;
4748
import nextflow.script.parser.ScriptParserPluginFactory;
48-
import nextflow.script.types.Types;
4949
import org.codehaus.groovy.ast.ASTNode;
5050
import org.codehaus.groovy.ast.AnnotatedNode;
5151
import org.codehaus.groovy.ast.ClassNode;

src/main/java/nextflow/lsp/services/script/ScriptHoverProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
import nextflow.script.ast.FunctionNode;
2525
import nextflow.script.ast.ProcessNode;
2626
import nextflow.script.ast.WorkflowNode;
27-
import nextflow.script.types.Types;
27+
import nextflow.script.dsl.Types;
2828
import org.codehaus.groovy.ast.ASTNode;
2929
import org.codehaus.groovy.ast.MethodNode;
3030
import org.codehaus.groovy.ast.Variable;

src/main/java/nextflow/lsp/spec/ConfigSpecFactory.java

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
package nextflow.lsp.spec;
1717

1818
import java.io.IOException;
19+
import java.lang.reflect.ParameterizedType;
20+
import java.lang.reflect.Type;
1921
import java.util.ArrayList;
2022
import java.util.Collections;
2123
import java.util.List;
@@ -111,7 +113,7 @@ private static SpecNode fromNode(Map<String,?> node) {
111113

112114
private static SpecNode.Option fromOption(Map<String,?> spec) {
113115
var description = (String) spec.get("description");
114-
var types = new ArrayList<Class>();
116+
var types = new ArrayList<Type>();
115117
types.add(fromType(spec.get("type")));
116118
if( spec.containsKey("additionalTypes") ) {
117119
var additionalTypes = (List) spec.get("additionalTypes");
@@ -150,16 +152,43 @@ private static SpecNode.Scope fromScope(Map<String,?> spec) {
150152
Map.entry("String", String.class)
151153
);
152154

153-
private static Class fromType(Object type) {
155+
private static Type fromType(Object type) {
154156
if( type instanceof String s ) {
155-
return STANDARD_TYPES.getOrDefault(s, Object.class);
157+
return rawType(s);
156158
}
157159
if( type instanceof Map m ) {
158160
var name = (String) m.get("name");
159-
// TODO: type arguments
160-
return STANDARD_TYPES.getOrDefault(name, Object.class);
161+
var argumentNames = (List) m.get("typeArguments");
162+
var rawType = rawType(name);
163+
var typeArguments = argumentNames.stream()
164+
.map(el -> (Type) rawType((String) el))
165+
.toArray(Type[]::new);
166+
return new ParameterizedTypeImpl(rawType, (Type[]) typeArguments);
161167
}
162168
throw new IllegalStateException();
163169
}
164170

171+
private static Class rawType(String name) {
172+
return STANDARD_TYPES.getOrDefault(name, Object.class);
173+
}
174+
175+
private static class ParameterizedTypeImpl implements ParameterizedType {
176+
private Type rawType;
177+
private Type[] typeArguments;
178+
179+
ParameterizedTypeImpl(Type rawType, Type[] typeArguments) {
180+
this.rawType = rawType;
181+
this.typeArguments = typeArguments;
182+
}
183+
184+
@Override
185+
public Type getOwnerType() { return null; }
186+
187+
@Override
188+
public Type getRawType() { return rawType; }
189+
190+
@Override
191+
public Type[] getActualTypeArguments() { return typeArguments; }
192+
}
193+
165194
}

src/main/java/nextflow/script/types/TypesEx.java

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package nextflow.script.types;
1717

18+
import java.lang.reflect.ParameterizedType;
19+
import java.lang.reflect.Type;
1820
import java.math.BigDecimal;
1921
import java.math.BigInteger;
2022
import java.nio.file.Path;
@@ -291,7 +293,12 @@ else if( type.isResolved() ) {
291293

292294
if( !placeholder && type.getGenericsTypes() != null ) {
293295
builder.append('<');
294-
genericsTypeNames(type.getGenericsTypes(), builder);
296+
var gts = type.getGenericsTypes();
297+
for( int i = 0; i < gts.length; i++ ) {
298+
if( i > 0 )
299+
builder.append(", ");
300+
builder.append(getName(gts[i].getType()));
301+
}
295302
builder.append('>');
296303
}
297304

@@ -301,18 +308,30 @@ else if( type.isResolved() ) {
301308
return builder.toString();
302309
}
303310

304-
private static void genericsTypeNames(GenericsType[] genericsTypes, StringBuilder builder) {
305-
for( int i = 0; i < genericsTypes.length; i++ ) {
306-
if( i > 0 )
307-
builder.append(", ");
308-
builder.append(getName(genericsTypes[i].getType()));
309-
}
311+
public static String getName(Type type) {
312+
return
313+
type instanceof Class c ? getName(c) :
314+
type instanceof ParameterizedType pt ? getName(pt) :
315+
getName(type.getTypeName());
310316
}
311317

312318
public static String getName(Class type) {
313319
return getName(normalize(type).getSimpleName());
314320
}
315321

322+
private static String getName(ParameterizedType pt) {
323+
var sb = new StringBuilder();
324+
sb.append(getName(pt.getRawType()));
325+
sb.append('<');
326+
for( int i = 0; i < pt.getActualTypeArguments().length; i++ ) {
327+
if( i > 0 )
328+
sb.append(", ");
329+
sb.append(getName(pt.getActualTypeArguments()[i]));
330+
}
331+
sb.append('>');
332+
return sb.toString();
333+
}
334+
316335
public static String getName(String name) {
317336
if( "Object".equals(name) )
318337
return "?";

0 commit comments

Comments
 (0)