Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"bad constant pool tag 17" when using a library compiled using Java 21 #19527

Closed
adamw opened this issue Jan 24, 2024 · 5 comments · Fixed by #19533
Closed

"bad constant pool tag 17" when using a library compiled using Java 21 #19527

adamw opened this issue Jan 24, 2024 · 5 comments · Fixed by #19533
Assignees
Milestone

Comments

@adamw
Copy link
Contributor

adamw commented Jan 24, 2024

Using the following snippet:

//> using scala 3.3.1
//> using dep com.softwaremill.jox:core:0.0.5

import com.softwaremill.jox.Channel

@main def main: Unit =
  new Channel(0)

Gives:

% scala-cli test.scala
Compiling project (Scala 3.3.1, JVM)
Error: error while loading Channel,
class file com/softwaremill/jox/Channel.class is broken, reading aborted with class java.lang.RuntimeException
bad constant pool tag 17 at byte 10172
[error] ./test.scala:7:7
[error] Not found: type Channel
[error]   new Channel(0)
[error]       ^^^^^^^

This is also reproducible using an SBT project, both with Scala 3.3.1, and 3.4.1-RC1-bin-20240122-ca18f4a-NIGHTLY. The error then is:

[error] error while loading Channel,
[error]   class file com/softwaremill/jox/Channel.class is broken (version 65.0),
[error]   please check the JDK compatibility of your Scala version (3.4.1-RC1-bin-20240122-ca18f4a-NIGHTLY),
[error]   reading aborted with class java.lang.RuntimeException:
[error]   bad constant pool tag 17 at byte 10172

I'm not sure if I'm reading javap's output right, but doing javap -v com.softwaremill.jox.Channel, the constant pool is:

Classfile /private/tmp/jox/com/softwaremill/jox/Channel.class
  Last modified Jan 24, 2024; size 21970 bytes
  SHA-256 checksum 2556e44fffe055696a70e17e57b205e3d077478e34ba0f5a9336269ab4ad50df
  Compiled from "Channel.java"
public final class com.softwaremill.jox.Channel<T extends java.lang.Object> extends java.lang.Object implements com.softwaremill.jox.Source<T>, com.softwaremill.jox.Sink<T>
  minor version: 0
  major version: 65
  flags: (0x0031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER
  this_class: #1                          // com/softwaremill/jox/Channel
  super_class: #8                         // java/lang/Object
  interfaces: 2, fields: 14, methods: 38, attributes: 5
Constant pool:
    #1 = Class              #2            // com/softwaremill/jox/Channel
    #2 = Utf8               com/softwaremill/jox/Channel
    #3 = Methodref          #1.#4         // com/softwaremill/jox/Channel."<init>":(I)V
    #4 = NameAndType        #5:#6         // "<init>":(I)V
    #5 = Utf8               <init>
    #6 = Utf8               (I)V
    #7 = Methodref          #8.#9         // java/lang/Object."<init>":()V
    #8 = Class              #10           // java/lang/Object
    #9 = NameAndType        #5:#11        // "<init>":()V
   #10 = Utf8               java/lang/Object
   #11 = Utf8               ()V
   #12 = Class              #13           // java/util/concurrent/atomic/AtomicLong
   #13 = Utf8               java/util/concurrent/atomic/AtomicLong
   #14 = Methodref          #12.#15       // java/util/concurrent/atomic/AtomicLong."<init>":(J)V
   #15 = NameAndType        #5:#16        // "<init>":(J)V
   #16 = Utf8               (J)V
   #17 = Fieldref           #1.#18        // com/softwaremill/jox/Channel.sendersAndClosedFlag:Ljava/util/concurrent/atomic/AtomicLong;
   #18 = NameAndType        #19:#20       // sendersAndClosedFlag:Ljava/util/concurrent/atomic/AtomicLong;
(...)

Which looks quite ordinary.

Using the same library from a java maven-based project works just fine. Both for deploying jox, and testing the above I'm using openjdk version "21.0.2" 2024-01-16.

The pom.xml and Channel.java are linked (to a specific commit, since they since changed to work around the problem).

@adamw adamw added itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label labels Jan 24, 2024
@bishabosha bishabosha added compat:java and removed stat:needs triage Every issue needs to have an "area" and "itype" label labels Jan 24, 2024
@adamw
Copy link
Contributor Author

adamw commented Jan 25, 2024

It turns out the problem is with switch-with-pattern-matching, which was stabilised in Java 21.

After converting all switch blocks in the java library, specifially in the Channel.java file, e.g.:

            switch (sendResult) {
                case SendResult.AWAITED -> {
                    // the thread was suspended and then resumed by a receiver or by buffer expansion
                    // not clearing the previous pointer, because of the buffering possibility
                    return null;
                }
                case SendResult.BUFFERED -> {
                    // a receiver is coming, or we are in buffer
                    // similarly as above, not clearing the previous pointer
                    return null;
                }
                case SendResult.RESUMED -> {
                    // we resumed a receiver - we can be sure that R > s
                    segment.cleanPrev();
                    return null;
                }
                case SendResult.FAILED -> {
                    // the cell was broken (hence already processed by a receiver) or interrupted (also a receiver
                    // must have been there); in both cases R > s
                    segment.cleanPrev();
                    // trying again with a new cell
                }
                case SendResult.CLOSED -> {
                    // not cleaning the previous segments - the close procedure might still need it
                    return closedReason.get();
                }
                case StoredSelectClause ss -> {
                    // we stored a select instance - there's no matching receive, not clearing the previous segment
                    return ss;
                }
                default -> throw new IllegalStateException("Unexpected result: " + sendResult);
            }

to if-else chains:

            if (sendResult == SendResult.AWAITED) {
                // the thread was suspended and then resumed by a receiver or by buffer expansion
                // not clearing the previous pointer, because of the buffering possibility
                return null;
            } else if (sendResult == SendResult.BUFFERED) {
                // a receiver is coming, or we are in buffer
                // similarly as above, not clearing the previous pointer
                return null;
            } else if (sendResult == SendResult.RESUMED) {
                // we resumed a receiver - we can be sure that R > s
                segment.cleanPrev();
                return null;
            } else if (sendResult == SendResult.FAILED) {
                // the cell was broken (hence already processed by a receiver) or interrupted (also a receiver
                // must have been there); in both cases R > s
                segment.cleanPrev();
                // trying again with a new cell
            } else if (sendResult == SendResult.CLOSED) {
                // not cleaning the previous segments - the close procedure might still need it
                return closedReason.get();
            } else if (sendResult instanceof StoredSelectClause ss) {
                // we stored a select instance - there's no matching receive, not clearing the previous segment
                return ss;
            } else {
                throw new IllegalStateException("Unexpected result: " + sendResult);
            }

The original problem ("bad constant pool tag") goes away.

Looking at javap's output of the unchanged Channel class, I think the problematic section is the following:

SourceFile: "Channel.java"
NestMembers:
  com/softwaremill/jox/Channel$2
  com/softwaremill/jox/Channel$1
BootstrapMethods:
  0: #698 REF_invokeStatic java/lang/runtime/SwitchBootstraps.typeSwitch:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #627 #13:invoke:Ljava/lang/Enum$EnumDesc;
      #631 #14:invoke:Ljava/lang/Enum$EnumDesc;
      #632 #15:invoke:Ljava/lang/Enum$EnumDesc;
      #633 #16:invoke:Ljava/lang/Enum$EnumDesc;
      #634 #17:invoke:Ljava/lang/Enum$EnumDesc;
      #134 com/softwaremill/jox/StoredSelectClause

Each switch-with-pattern-matching gets an entry in bootstrap methods, referencing SwitchBootstraps.typeSwitch. Each case then has an entry (e.g. #17:invoke:Ljava/lang/Enum$EnumDesc;). However, the #15 doesn't seem to point to the constant pool entry number 17, which in this case is:

#17 = Fieldref           #1.#18        // com/softwaremill/jox/Channel.sendersAndClosedFlag:Ljava/util/concurrent/atomic/AtomicLong;

Rather, it's some kind of reference to the enum (here, subsequent SendResult enum values). But what exactly these #13, #14, ... #17 reference I don't know - my research stopped here.

@SethTisue
Copy link
Member

Good catch! Thanks for reporting.

Scala 2 is affected as well, so I've opened scala/bug#12936

@dwijnand dwijnand assigned SethTisue and unassigned dwijnand Jan 25, 2024
@SethTisue
Copy link
Member

SethTisue commented Jan 25, 2024

I think we should consider this a blocker for 2.12.19 and 2.13.13, so I'll attempt a fix there first, then forward-port to Dotty.

@He-Pin
Copy link

He-Pin commented Jan 25, 2024

Yes, this should be a blocker, library will try to publish --relase 8 with Java 21.

I just upgraded all my systems and published all my internal jars built with Java 21 but with --release 8, so should be a blocker.

@adamw thanks

@SethTisue
Copy link
Member

SethTisue commented Jan 25, 2024

Scala 2 PR: scala/scala#10675

@SethTisue SethTisue changed the title "bad constant pool tag" when using a library compiled using Java 21 "bad constant pool tag 17" when using a library compiled using Java 21 Jan 25, 2024
SethTisue added a commit to SethTisue/dotty that referenced this issue Jan 26, 2024
SethTisue added a commit to SethTisue/dotty that referenced this issue Jan 26, 2024
forward-port of scala/scala#10675 and scala/scala#8595
references scala/bug#12396 and scala/bug#11635
fixes scala#19527 ("bad constant pool tag 17")
also fixes unreported potential "bad constant pool tag 19" and
"bad constant pool tag 20" errors
SethTisue added a commit that referenced this issue Jan 30, 2024
forward-port of scala/scala#10675 and scala/scala#8595
references scala/bug#12396 and scala/bug#11635
fixes #19527 ("bad constant pool tag 17")
also fixes unreported potential "bad constant pool tag 19" and "bad constant pool tag 20" errors
@Kordyjan Kordyjan added this to the 3.4.1 milestone Feb 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants