You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Untyped API doesn't collapse CPOOL-referenced simple types (asymmetric with typed parser) #108
The untyped parser (UntypedJafarParser / Values.resolvedDeep()) does not collapse "simple type" constant-pool-referenced fields to their scalar value, while the typed parser's codegen does (via MetadataClass.isSimpleType() in UntypedCodeGenerator/CodeGenerator). This makes the two APIs produce structurally different shapes for the same JFR data, and there's no public helper to normalize it on the untyped side.
i.e. every CPOOL-referenced field — regardless of whether its target type is marked simpleType="true" in JFR metadata — comes back as a single-entry wrapper Map, not the plain scalar.
Digging into parser-codegen/src/main/java/io/jafar/parser/internal_api/UntypedCodeGenerator.java: fieldType.isSimpleType() is checked and used to decide whether a field reads as a plain value vs. a nested object — but only for inline (non-CPOOL) fields (see the early-return for the ConstantPoolAccessor path around line 525-527, which happens before the isSimpleType() check at line 530). So any field with hasConstantPool() never gets simple-type unwrapping, whether parsed via the typed or untyped API.
The one place in the codebase that does handle this is jfr-shell/src/main/java/io/jafar/shell/cli/TableRenderer.java:
privatestaticStringtoCell(Objectv) {
if (v == null) return"";
...
if (vinstanceofMap<?, ?> m) {
if (m.size() == 1) {
returntoCell(m.values().iterator().next());
}
...
— but this is a private, display-only method in the CLI module. It formats a String for one table cell; it doesn't reconstruct a resolved Map/scalar you can consume programmatically, and it isn't part of the published io.btrace:jafar-parser artifact's public API (Values, ComplexType).
Impact
Consumers of the untyped API (e.g. Values.resolvedDeep()) have no way to distinguish "genuine multi-field struct" from "CPOOL-referenced simple-type wrapper" without either (a) hardcoding knowledge of each simple type's wrapper key name (name, value, "", string, ...) per call site, or (b) reimplementing the same size() == 1 heuristic TableRenderer already uses, in every consuming project.
We hit this migrating DataDog/java-profiler's JFR-based test assertions off JMC's FlightRecordingLoader onto jafar: fields like jdk.types.ThreadState/datadog.types.ExecutionMode (JVM/JFR built-ins) and custom simple types like our own CounterName/AttributeValue all needed manual per-field unwrapping to read as plain strings.
Suggested fix
Make untyped resolution symmetric with the typed path: either
Have ConstantPoolAccessor/the CP resolution path consult MetadataClass.isSimpleType() the same way the inline-field path does, and resolve simple-type CPOOL references directly to their scalar value, or
Failing that, expose a public, reusable utility (e.g. Values.collapseSimpleTypes(Map) or fold it into Values.resolvedDeep()) that generically collapses simple-type wrapper values, so consumers don't need to reinvent TableRenderer's size() == 1 trick themselves.
Happy to put up a PR for either approach if there's a preference — just wanted to raise it first since it touches the constant-pool resolution path.
Version
io.btrace:jafar-parser:0.26.2 (Maven Central). Also confirmed present in a local build from main at the time of writing (0.27.0-SNAPSHOT).
Activity