1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package Torello.Java.Additional;

import java.util.Arrays;
import java.nio.charset.StandardCharsets;

// This class, sort of, isolates the contents of the Constructor into a single method.  This
// class is not an End-User API method.  It isn't that nice to look at, and there really isn't
// a way to make it look better.
// 
// Instead, since the contents of this class lone method contain all details for reading
// constants out of the Constant-Pool

class ReadConstant
{
    // Reads one contant from the Constant-Pool (via/using the Class-File as-a Byte-Array)
    // This method is Package-Private, and is only called - ONCE, IN ONE PLACE - inside the
    // constructor for ConstantPool

    static Object read(
            final byte[]    bArr,
            final byte      tagAsByte,
            final int       index
        )
    {
        switch (tagAsByte)
        {
            // public static final byte TAG_UTF_8 = 1;
            case 1: // CONSTANT_UTF_8

                // First 2 bytes at the given index store the length of the UTF-8 string.
                int utf8Length = 
                        ((bArr[index] & 0xFF) << 8)
                    |   (bArr[index + 1] & 0xFF);

                // Extract the UTF-8 string from the byte array starting at index + 2
                byte[] utf8Bytes = Arrays.copyOfRange
                    (bArr, index + 2, index + 2 + utf8Length);

                // Convert the byte array into a String and return it
                return new String(utf8Bytes, StandardCharsets.UTF_8);

            // public static final byte TAG_INTEGER = 3;
            case 3: return Integer.valueOf(
                        (bArr[index] & 0xFF) << 24
                    |   (bArr[index + 1] & 0xFF) << 16 
                    |   (bArr[index + 2] & 0xFF) << 8
                    |   (bArr[index + 3] & 0xFF)
                );

            // public static final byte TAG_FLOAT = 4;
            case 4: return Float.intBitsToFloat(
                        (bArr[index] & 0xFF) << 24
                    |   (bArr[index + 1] & 0xFF) << 16
                    |   (bArr[index + 2] & 0xFF) << 8
                    |   (bArr[index + 3] & 0xFF)
                );

            // public static final byte TAG_LONG = 5;
            case 5: return Long.valueOf(
                        ((long) (bArr[index] & 0xFF) << 56)
                    |   ((long) (bArr[index + 1] & 0xFF) << 48)
                    |   ((long) (bArr[index + 2] & 0xFF) << 40)
                    |   ((long) (bArr[index + 3] & 0xFF) << 32)
                    |   ((long) (bArr[index + 4] & 0xFF) << 24)
                    |   ((long) (bArr[index + 5] & 0xFF) << 16)
                    |   ((long) (bArr[index + 6] & 0xFF) << 8)
                    |   ((long) (bArr[index + 7] & 0xFF))
                );

            // public static final byte TAG_DOUBLE = 6;
            case 6: long bits =
                        ((long) (bArr[index] & 0xFF) << 56)
                    |   ((long) (bArr[index + 1] & 0xFF) << 48)
                    |   ((long) (bArr[index + 2] & 0xFF) << 40)
                    |   ((long) (bArr[index + 3] & 0xFF) << 32)
                    |   ((long) (bArr[index + 4] & 0xFF) << 24)
                    |   ((long) (bArr[index + 5] & 0xFF) << 16)
                    |   ((long) (bArr[index + 6] & 0xFF) << 8)
                    |   ((long) (bArr[index + 7] & 0xFF));

                return Double.longBitsToDouble(bits);


            // public static final byte TAG_CLASS       = 7;
            // public static final byte TAG_STRING      = 8;
            // public static final byte TAG_METHOD_TYPE = 16;
            // public static final byte TAG_MODULE      = 19;
            // public static final byte TAG_PACKAGE     = 20;

            case 7:     // CONSTANT_Class
            case 8:     // CONSTANT_String
            case 16:    // CONSTANT_MethodType
            case 19:    // CONSTANT_Module
            case 20:    // CONSTANT_Package
                        // 2 bytes for the index to a CONSTANT_Utf8 string
    
                return Integer.valueOf((bArr[index] & 0xFF) << 8 | (bArr[index + 1] & 0xFF));


            // public static final byte TAG_FIELD_REF               = 9;
            // public static final byte TAG_METHOD_REF              = 10;
            // public static final byte TAG_INTERFACE_METHOD_REF    = 11;
            // public static final byte TAG_NAME_AND_TYPE           = 12;
            // public static final byte TAG_DYNAMIC                 = 17;
            // public static final byte TAG_INVOKE_DYNAMIC          = 18;

            case 9:     // CONSTANT_Fieldref
            case 10:    // CONSTANT_Methodref
            case 11:    // CONSTANT_InterfaceMethodref
            case 12:    // CONSTANT_NameAndType
                        // Each reference stores 2 indices

            case 17:    // CONSTANT_Dynamic
                        // 2 bytes for the bootstrap method index +
                        // 2 bytes for the name and type index

            case 18:    // CONSTANT_InvokeDynamic
                        // 2 bytes for the bootstrap method index +
                        // 2 bytes for the name and type index

                final int index1 = ((bArr[index] & 0xFF) << 8)      | (bArr[index + 1] & 0xFF);
                final int index2 = ((bArr[index + 2] & 0xFF) << 8)  | (bArr[index + 3] & 0xFF);
                    
                return new Ret2<Integer, Integer>
                    (Integer.valueOf(index1), Integer.valueOf(index2));   


            // public static final byte TAG_METHOD_HANDLE = 15;
            case 15:
                // CONSTANT_MethodHandle
                // 1 byte for reference kind + 2 bytes for index
                // The reference kind is stored in the first byte
                // The index is a 2-byte value, which follows

                final byte kind = bArr[index];

                final int i =
                        ((bArr[index + 1] & 0xFF) << 8)
                    |   (bArr[index + 2] & 0xFF);
   
                return new Ret2<Byte, Integer>(Byte.valueOf(kind), Integer.valueOf(i));


            // This cannot happen unless a corrupted Class-File was provided to the Constructor.
            // 
            // The only other possibility is that Java-Oracle has created new constants since 
            // the writing of this class.

            default: throw new ClassFileArrayException(
                    "While parsing the Constant-Pool Table, an unknown constant pool tag " +
                    "Byte-Value [" + tagAsByte + "]\n" +
                    "This is likely caused by an invalid Java '.class' File"
                );
        }
    }
}