From a90eb74c2de63f5cb1a16f847f91c228eb9a9141 Mon Sep 17 00:00:00 2001 From: Sergio Salvi Date: Tue, 25 Aug 2026 12:04:45 -0700 Subject: [PATCH] Fix reflect crash on modules using glob imports (#1816) This excludes glob imports when creating a module's mirror --- .../src/main/java/org/pkl/core/runtime/VmTyped.java | 6 +++++- .../LanguageSnippetTests/input/api/reflect6.pkl | 13 +++++++++++++ .../LanguageSnippetTests/output/api/reflect6.pcf | 4 ++++ stdlib/reflect.pkl | 2 +- 4 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input/api/reflect6.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/output/api/reflect6.pcf diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmTyped.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmTyped.java index e83c14c72..1e3fb1284 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmTyped.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmTyped.java @@ -115,7 +115,11 @@ public final class VmTyped extends VmObject { if (bodyNode instanceof WrapperNode wrapper) { bodyNode = (ExpressionNode) wrapper.getDelegateNode(); } - builder.add(member.getName().toString(), ((ImportNode) bodyNode).getImportUri().toString()); + // Glob imports (`import*`) don't import a type; they resolve to a mapping. + // Exclude them here, consistent with `imports` also not covering import expressions. + if (bodyNode instanceof ImportNode importNode) { + builder.add(member.getName().toString(), importNode.getImportUri().toString()); + } } } return builder.build(); diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/api/reflect6.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/api/reflect6.pkl new file mode 100644 index 000000000..990796644 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/api/reflect6.pkl @@ -0,0 +1,13 @@ +import "pkl:reflect" +import* "../../input-helper/globtest/child/*.pkl" as globbed + +// regression test for VmTyped.getImports() +// `member.isImport()` is true for glob imports too, so reflecting a module that +// uses `import*` previously threw ClassCastException when casting the glob +// import's body to ImportNode. +// Glob imports resolve to a mapping rather than a module type, so they are +// excluded from `imports` (which only reports regular imports). +res = reflect.Module(module).imports + +// the glob import itself still works +globbedKeys = globbed.keys.toListing() diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/api/reflect6.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/api/reflect6.pcf new file mode 100644 index 000000000..c7928ac4a --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/api/reflect6.pcf @@ -0,0 +1,4 @@ +res = Map("reflect", "pkl:reflect") +globbedKeys { + "../../input-helper/globtest/child/moduleC.pkl" +} diff --git a/stdlib/reflect.pkl b/stdlib/reflect.pkl index 1a48b5128..6ce546ee1 100644 --- a/stdlib/reflect.pkl +++ b/stdlib/reflect.pkl @@ -201,7 +201,7 @@ external class Module extends Declaration { /// Map keys are the identifiers by which imports are accessed within this module. /// Map values are the URIs of the imported modules. /// - /// Does not cover import expressions. + /// Does not cover import expressions or glob imports. imports: Map /// The classes declared in this module.