import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Opcodes;

import java.lang.foreign.MemorySegment;
import java.lang.foreign.ValueLayout;
import java.lang.module.ModuleFinder;
import java.util.Arrays;

public class CompilerCrash {

    private static MemorySegment classFileBuffer;
    private static final String[] cache = new String[65535];

    public static void main(String[] args) throws Exception {
        System.out.println(Runtime.version());
        byte[] bytes;
        try (var mr = ModuleFinder.ofSystem()
                .find("java.sql.rowset")
                .orElseThrow().open()) {
            var bb = mr.read("com/sun/rowset/CachedRowSetImpl.class").orElseThrow();
            bytes = new byte[bb.remaining()];
            bb.get(bytes);
            bytes[7] = 44 + 25; // Patch major version to keep asm happy.
            classFileBuffer = MemorySegment.ofArray(bytes);
        }
        for (int i = 0; i < 5000; i++) {
            test(bytes);
        }
    }

    private static void test(byte[] bytes) {
        Arrays.fill(cache, null);
        new MyClassReader(bytes).accept(new ClassVisitor(Opcodes.ASM9) {
        }, 0);
    }

    private static final class MyClassReader extends ClassReader {

        MyClassReader(byte[] classFile) {
            super(classFile);
        }

        @Override
        public String readUTF8(int offset, char[] charBuffer) {
            int constantPoolEntryIndex = readUnsignedShort(offset);
            if (offset == 0 || constantPoolEntryIndex == 0) {
                return null;
            }
            return readUtf(constantPoolEntryIndex, charBuffer);
        }

        private String readUtf(final int constantPoolEntryIndex, final char[] charBuffer) {
            String value = cache[constantPoolEntryIndex];
            if (value != null) {
                return value;
            }
            int cpInfoOffset = getItem(constantPoolEntryIndex);
            return cache[constantPoolEntryIndex]
                    = readUtf(cpInfoOffset + 2, readUnsignedShort(cpInfoOffset), charBuffer);
        }

        private static final ValueLayout.OfByte BYTE = ValueLayout.JAVA_BYTE;

        private String readUtf(final int utfOffset, final int utfLength, final char[] charBuffer) {
            int currentOffset = utfOffset;
            int endOffset = currentOffset + utfLength;
            int strLength = 0;
            MemorySegment classBuffer = classFileBuffer;
            while (currentOffset < endOffset) {
                int currentByte = classBuffer.get(BYTE, currentOffset++);
                if ((currentByte & 0x80) == 0) {
                    charBuffer[strLength++] = (char) (currentByte & 0x7F);
                } else if ((currentByte & 0xE0) == 0xC0) {
                    charBuffer[strLength++]
                            = (char) (((currentByte & 0x1F) << 6) + (classBuffer.get(BYTE, currentOffset++) & 0x3F));
                } else {
                    charBuffer[strLength++]
                            = (char) (((currentByte & 0xF) << 12)
                            + ((classBuffer.get(BYTE, currentOffset++) & 0x3F) << 6)
                            + (classBuffer.get(BYTE, currentOffset++) & 0x3F));
                }
            }
            return new String(charBuffer, 0, strLength);
        }
    }
}
