fix: handle post-Java 8 constant pool entries in LineNumbers - #3465
Open
pjfanning wants to merge 1 commit into
Open
fix: handle post-Java 8 constant pool entries in LineNumbers#3465pjfanning wants to merge 1 commit into
pjfanning wants to merge 1 commit into
Conversation
`LineNumbers` parses class files to recover source file and line number information for actor and lambda error reporting. Its constant pool reader handles the entry kinds that existed in class file format 52.0 (Java 8) but not the ones added since: - CONSTANT_Dynamic (tag 17, format 55.0 / Java 11) - CONSTANT_Module and CONSTANT_Package (tags 19 and 20, format 53.0) Entry sizes in the constant pool are tag dependent, so an unrecognised tag makes every subsequent entry unreadable. The match had no default case either, so this surfaced as a MatchError, which `getInfo` catches and turns into `UnknownSourceFormat`. The result was source information silently degrading to "parse error: 17" rather than failing loudly. Add the three missing tags and a default case that throws a described exception instead of a MatchError, and correct the class comment, which still claimed support only up to format 52.0. Note that neither scalac nor javac emits CONSTANT_Dynamic for the code in this repository today: scanning 16414 compiled classes across the Scala 2.13 and Scala 3 builds found none. This is a latent robustness fix for user class files rather than one for a failure seen in Pekko itself. The test splices each entry kind into the constant pool of a real class file, since no compiler here produces them. It fails on the unfixed parser for tags 17, 19 and 20, and covers that an unknown tag still yields an UnknownSourceFormat result rather than propagating.
This was referenced Aug 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
LineNumbersparses class files to recover the source file and line numbers used in actor and lambda error reporting. Its constant pool reader (LineNumbers.scala:122-168) covers the entry kinds present in class file format 52.0 (Java 8), but not the ones added since:CONSTANT_DynamicCONSTANT_ModuleCONSTANT_PackageEntry sizes in the constant pool are tag dependent, so an unrecognised tag makes every later entry unreadable. The match also had no default case, so this surfaced as a
MatchError, whichgetInfocatches at line 200 and converts intoUnknownSourceFormat. Source information therefore degraded silently:The stale class comment was the giveaway — it still read "works for all normal classes up to format 52:0 (JDK8)".
Modification
Add the three missing tags with their correct sizes (tag 17 is two shorts and one pool slot; 19 and 20 are one short and one slot), plus a default case that throws a described
UnsupportedOperationExceptionrather than aMatchError. Correct the class comment.Scope — please read before weighing this
I want to be straight about how much this matters in practice. Neither scalac nor javac emits
CONSTANT_Dynamicfor the code in this repository today. I scanned 16,414 compiled classes — 5,518 from the Scala 2.13 build and 10,896 from Scala 3 — and found zero containing tag 17.So this is a latent robustness fix for user class files, not a fix for a failure observed in Pekko. It becomes reachable when
LineNumbersis pointed at a class compiled from another source (javacin configurations that emit condy, a future scalac, or another JVM language). The missing default case is the part I would argue for regardless: an unknown future tag should not reach users as aMatchError.Result
Same fixture, before and after:
UnknownSourceFormat(parse error: 17 (of class java.lang.Byte))CondyGen.java:4-7Tests
New
LineNumbersConstantPoolSpecinactor-tests. Since no compiler available here emits these entries, the test takes a real class file and splices the entry into its constant pool, then parses it. No binary fixture is checked in — the class file is read from the classpath at runtime and modified in memory.Four tests: a baseline that the unmodified class still parses (so a failure below is attributable to the new entry), one for
CONSTANT_Dynamic, one forCONSTANT_Module/CONSTANT_Package, and one asserting an unknown tag still yieldsUnknownSourceFormatrather than propagating.Verified the tests bite: reverting only the production change fails the tag 17 and tag 19/20 tests, while the baseline and unknown-tag tests still pass.
actor-tests/testOnly org.apache.pekko.util.LineNumbersConstantPoolSpec org.apache.pekko.util.LineNumberSpec— 11 tests, all passsbt actor/mimaReportBinaryIssues— clean;LineNumbersinternals are private, no signature changesscalafmtrun on both modules; header added bysbt headerCreateAllThe spec is placed in
src/test/scalarather than thescala-2/scala-3variants, since it asserts nothing version specific.References
Found during a sweep for patterns left over from the Java 8 / Scala 2.12 baseline. Most of that sweep came back clean —
sun.misc.Unsafeis already onVarHandle,JavaConvertersis fully migrated,onSpinWaitandFiles/readAllBytesare adopted — so this is the one item from it with real substance.