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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
package Torello.Java.Additional;

import Torello.Java.ReadOnly.ReadOnlyList;

class Validate
{
    @SuppressWarnings("unchecked")
    static void run(
            final ReadOnlyList<Byte>    tags,
            final ReadOnlyList<Object>  values
        )
    {
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // NPE Check Issue
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // 
        // There is NO SENSE in checking for NPE, this method may only be called from the 
        // ConstantPool-Constructor.  In that constructor, these two Lists's are built and assigned
        // to the results of the ROArrayListBuilder's 'build()' Method.  That method cnnot return
        // null.  If it does, NPE would be better because it means ROALB is broken, not the user,
        // nor this class.  Don't waste the cycles checking for NPE.

        final int SIZE = tags.size();


        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // Parallel Array Checker
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // 
        // This is practically like checking that tags & values are null.  This should also be
        // be impossible.  However, being only one, simple, if-statement - I'm going to leave this
        // LOC right here.  It serves more as "Program-Documentation" than an actual
        // Validation-Check

        if (SIZE != values.size()) throw new InternalError(
            "tag.size() = [" + tags.size() + "], and values.size() = [" + values.size() + "].  " +
            "However, these arrays should be Parallel, and therefore must have equal lengths."
        );


        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // Main Checker Loop
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

        // Temporary Variables, re-used throughout the entire switch statement
        Ret2<Integer, Integer>  r2      = null;
        int                     pointer = 0;

        for (int i=0; i < SIZE; i++)
        {
            final Byte      tagAsByte   = tags.get(i);
            final Object    value       = values.get(i);

            // NPE Issue:
            //
            // Again, this method may only be invoked by the ConstantPool-Constructor.
            //
            // That constructor simply WILL-NOT put 'null' into either of these two arrays - unless
            // the previous entry in the lists was for a Double or a Long.  The Java Constant-Pool 
            // design asserts that any 8-Byte-Wide Constants must force the Constant-Pool List-index
            //  to "skip" exactly one List-Index.  The Constructor's Main-Loop has this line:
            //
            // if ((o instanceof Double) || (o instanceof Long))
            // {
            //     tagsROALB.add(null);
            //     valuesROALB.add(null);
            // }
            // 
            // Other than the above lines, null cannot be one of the "tags" inside the "tags" list,
            // and so it doesn't need to be validated!  That's the value of Final-Read-Only Tables.

            if (tagAsByte == null) continue;


            // (Byte) null,   tag=0,   NOT A TAG
            // null,          tag=1,   CONSTANT_Utf8
            // null,          tag=2    UNUSED
            // 4 Bytes Wide,  tag=3,   CONSTANT_Integer
            // 4 Bytes Wide,  tag=4,   CONSTANT_Float
            // 8 Bytes Wide,  tag=5,   CONSTANT_Long
            // 8 Bytes Wide,  tag=6,   CONSTANT_Double
            // 
            // UTF-8, Integer, Float, Lng, Double - there is nothing to validate!  Note that even
            // if an "incorrect" number (or string) were stored in the Constants-List, there 
            // wouldn't be a way to identify that it was invalid!

            if (tagAsByte < 7) continue;


            switch (tagAsByte)
            {
                // 2 Bytes Wide,  tag=7,  CONSTANT_Class,   The reference stores an index
                // 2 Bytes Wide,  tag=8,  CONSTANT_String,  The reference stores an index
    
                case /* 7 */ ConstantPool.TAG_CLASS:
                case /* 8 */ ConstantPool.TAG_STRING:

                    // Reference Must Point to a UTF-8 Constant, value is guaranteed to be Integer
                    pointer = ((Integer) value).intValue();
                    checkIndexPointer(pointer, i, tags, value, ConstantPool.TAG_UTF_8);
                    break;

                // 4 Bytes Wide,  tag=9,   CONSTANT_Fieldref            Each ref stores 2 indices
                // 4 Bytes Wide,  tag=10,  CONSTANT_Methodref           Each ref stores 2 indices
                // 4 Bytes Wide,  tag=11,  CONSTANT_InterfaceMethodref, Each ref stores 2 indices

                case /* 9 */    ConstantPool.TAG_FIELD_REF:
                case /* 10 */   ConstantPool.TAG_METHOD_REF:
                case /* 11 */   ConstantPool.TAG_INTERFACE_METHOD_REF:

                    r2 = (Ret2<Integer, Integer>) value;
                    checkIndexPointer(r2.a, i, tags, value, ConstantPool.TAG_CLASS);
                    checkIndexPointer(r2.b, i, tags, value, ConstantPool.TAG_NAME_AND_TYPE);
                    break;

                // 4 Bytes Wide,  tag=12,  CONSTANT_NameAndType,  Each reference stores 2 indices
                case /* 12 */ ConstantPool.TAG_NAME_AND_TYPE:

                    r2 = (Ret2<Integer, Integer>) value;
                    checkIndexPointer(r2.a, i, tags, value, ConstantPool.TAG_UTF_8);
                    checkIndexPointer(r2.b, i, tags, value, ConstantPool.TAG_UTF_8);
                    break;


                // null,  tag=13,  UNUSED
                // null,  tag=14,  UNUSED
                // 
                // case 13:
                // case 14: throw new UnreachableError();
                // 
                // Handled by the "default:" case

            
                // 3 Bytes Wide,  tag=15,  CONSTANT_MethodHandle
                // ==> 1 byte for reference kind + 2 bytes for index

                case /* 15 */ ConstantPool.TAG_METHOD_HANDLE:

                    @SuppressWarnings("unchecked")
                    final Ret2<Byte, Integer> methHandleR2 = (Ret2<Byte, Integer>) value;

                    if ((methHandleR2.a > 9) || (methHandleR2.a < 0))

                        throw new ClassFileArrayException(
                            printIntro(i, tags.get(i), value) +
                            "Expecting a Kind between 1 and 9, but found " + methHandleR2.a + '.'
                        );

                    checkIndexPointer(
                        methHandleR2.b,
                        i,
                        tags,
                        value,
                        ConstantPool.TAG_FIELD_REF,
                        ConstantPool.TAG_METHOD_REF,
                        ConstantPool.TAG_INTERFACE_METHOD_REF
                    );

                    break;


                // 2 Bytes Wide,  tag=16,  CONSTANT_MethodType
                // 2 bytes for the index to a CONSTANT_Utf8 string

                case /* 16 */ ConstantPool.TAG_METHOD_TYPE:

                    // Reference Must Point to a UTF-8 Constant, value is guaranteed to be Integer
                    pointer = ((Integer) value).intValue();
                    checkIndexPointer(pointer, i, tags, value, ConstantPool.TAG_UTF_8);
                    break;


                // 4 Bytes Wide,  tag=17,  CONSTANT_Dynamic
                //      Chat-GPT fed my a bit of "mis-interpreted info"
                //      In its defense, I cannot find anywhere "#17" Constant-Dynamic
                // 
                // I mean, I have not needed to use these evil pieces of $hit in years,
                // but today, this page was the only one I could find to reference 
                // "Constant_Dynamic".  I didn't intentionally search StackOverflow, google
                // shoved this raw-trash in my face.
                // 
                // NOTE: Chat-GPT was taken down today for the duration of my programming
                //       session, otherwise I would ask Mr. GPT.  I have to go now..
                // 
                // https://stackoverflow.com/questions/32255023/
                // how-would-i-go-about-parsing-the-java-class-file-constant-pool
                //
                // 4 Bytes Wide,  tag=18,  CONSTANT_InvokeDynamic
                //      2 bytes for the bootstrap method index + ==> NO WAY TO VALIDATE !!!
                //      2 bytes for the name and type index

                case /* 17 */ ConstantPool.TAG_DYNAMIC:
                case /* 18 */ ConstantPool.TAG_INVOKE_DYNAMIC:

                    // NOTE: There is simply no way to validate r2Value.a !!!
                    r2 = (Ret2<Integer, Integer>) value;
                    checkIndexPointer(r2.b, i, tags, value, ConstantPool.TAG_NAME_AND_TYPE);
                    break;


                // 2 Bytes Wide,  tag=19   CONSTANT_Module,   The reference stores an index
                // 2 Bytes Wide   tag=20   CONSTANT_Package,  The reference stores an index

                case /* 19 */ ConstantPool.TAG_MODULE:
                case /* 20 */ ConstantPool.TAG_PACKAGE:

                    // Reference Must Point to a UTF-8 Constant, value is guaranteed to be Integer
                    pointer = ((Integer) value).intValue();
                    checkIndexPointer(pointer, i, tags, value, ConstantPool.TAG_UTF_8);
                    break;


                // This is theoretically completely unreachable code.
                default: throw new ConstantPoolError(
                    "An unknown Tag-Kind has been placed into the 'ConstantPool.tags' List " +
                    '[' + tagAsByte + "].  This ReadOnlyList is generated internall, inside " +
                    "this class' Constructor and, therefore, should not contain unknown tags."
                );
            }
        }
    }


    // ********************************************************************************************
    // ********************************************************************************************
    // Exception Printing Helper
    // ********************************************************************************************
    // ********************************************************************************************


    private static String printValue(Object value)
    {
        if (Ret2.class.isInstance(value))
        {
            @SuppressWarnings("rawtypes")
            Ret2 r2 = (Ret2) value;
            return '[' + r2.a.toString() + ", " + r2.b.toString() + ']';
        }

        else return '[' + value.toString() + ']';
    }


    private static String printIntro(
            final int       i,
            final Byte      tagAsByte,
            final Object    value
        )
    {
        return
            "The Constant situated at Constant-Pool index [" + i + "], has " +
                "Tag-Number [" + tagAsByte + "] (which maps to the name " +
                '\"' + ConstantPool.tagNames.get(tagAsByte) + "\").  " +
            "The value to which it is assinged is " + printValue(value);
    }


    // ********************************************************************************************
    // ********************************************************************************************
    // The Main Index Checker
    // ********************************************************************************************
    // ********************************************************************************************


    private static void checkIndexPointer(
            final int                   indexPointer,
            final int                   i,
            final ReadOnlyList<Byte>    tags,
            final Object                value,
            final byte...               expectedTags
        )
    {
        if (indexPointer < 0) throw new ClassFileArrayException(
            printIntro(i, tags.get(i), value) +
            "Unfortunately, this value is designed to be a List-Index, and therefore cannot " +
            "be negative."
        );

        if (indexPointer >= tags.size()) throw new ClassFileArrayException(
            printIntro(i, tags.get(i), value) +
            "Unfortunately, this value is designed to be a List-Index, and therefore cannot " +
            "be greater than or equal to the size of the Constant-Pool Table, which is a List " +
            "having size() = " + tags.size() + '.'
        );

        final Byte ret = tags.get(indexPointer);

        if (ret == null) throw new ClassFileArrayException(
            printIntro(i, tags.get(i), value) +
            "Unfortunately, this value is designed to be an index into the Contant-Pool Table.  " +
            "The content of the List-Entry to which this pointer points is null."
        );


        // Check that the tag that was found matches at least one of the tags within the
        // input-parameter array of acceptable tags.
        // 
        // If there is a match, return from this method immediately, because this is the very last
        // validation test - all other validation tests have already succeeded.

        for (byte expectedTag : expectedTags) if (ret == expectedTag) return;


        // If it failed, but the array contained only a single element, then throw an exception
        // using just the one element in the array for the Exception-Message.

        if (expectedTags.length == 1) throw new ClassFileArrayException(
            printIntro(i, tags.get(i), value) + '\n' +
            "Unfortunately, expecting to find a Tag-Reference to a:\n" +
                "\t[" + ConstantPool.tagNames.get(expectedTags[0]) + "], " +
                "(Tag-ID=" + expectedTags[0] + ")\n" +
            "But intead, found a Tag-Reference to a:\n" +
                "\t[" + ConstantPool.tagNames.get(ret) + "], " +
                "(Tag-ID=" + ret + ")\n"
        );

        StringBuilder sb = new StringBuilder();

        for (byte expectedTag : expectedTags) sb.append(
            "t" + ConstantPool.tagNames.get(expectedTag) + "], " +
            "(Tag-ID=" + expectedTags + ")\n"
        );

        throw new ClassFileArrayException(
            printIntro(i, tags.get(i), value) + '\n' +
            "Unfortunately, expecting to find a Tag-Reference matching one of the following:\n" +
                sb.toString() +
            "But intead, found a Tag-Reference to a:\n" +
                "\t[" + ConstantPool.tagNames.get(ret) + "], " +
                "(Tag-ID=" + ret + ")\n"
        );
    }

}