File: Generator.java

package info (click to toggle)
openjdk-17 17.0.17%2B10-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 764,928 kB
  • sloc: java: 5,319,061; xml: 1,291,711; cpp: 1,202,358; ansic: 428,746; asm: 404,978; objc: 20,861; sh: 14,754; javascript: 10,743; python: 6,402; makefile: 2,404; perl: 357; awk: 351; sed: 172; jsp: 24; csh: 3
file content (452 lines) | stat: -rw-r--r-- 20,026 bytes parent folder | download | duplicates (6)
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
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
/*
 * Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 *
 */

/*
 * INVOKESPECIAL EXPECTED RESULTS
 *
 * From JVMS 3rd edition: invokespecial instruction:
 *
 * Invoke instance method; special handling for superclass, private, and instance
 * initialization method invocations
 *
 * The named method is resolved (5.4.3.3). Finally, if the resolved method is
 * protected (4.7), and it is a member of a superclass of the current class, and
 * the method is not declared in the same run-time package (5.3) as the current
 * class, then the class of objectref must be either the current class or a
 * subclass of the current class.
 *
 * Next, the resolved method is selected for invocation unless all of the
 * following conditions are true:
 *     * The ACC_SUPER flag (see Table 4.1, "Class access and property modifiers") is set for the current class.
 *     * The class of the resolved method is a superclass of the current class.
 *     * The resolved method is not an instance initialization method (3.9).
 *
 * If the above conditions are true, the actual method to be invoked is selected
 * by the following lookup procedure. Let C be the direct superclass of the
 * current class:
 *     * If C contains a declaration for an instance method with the same name and
 *       descriptor as the resolved method, then this method will be invoked.
 *       The lookup procedure terminates.
 *
 *     * Otherwise, if C has a superclass, this same lookup procedure is performed
 *       recursively using the direct superclass of C. The method to be invoked is
 *       the result of the recursive invocation of this lookup procedure.
 *
 *     * Otherwise, an AbstractMethodError? is raised.
 *
 * During resolution of the symbolic reference to the method, any of the
 * exceptions pertaining to method resolution documented in Section 5.4.3.3 can be
 * thrown.
 *
 * Otherwise, if the resolved method is an instance initialization method, and the
 * class in which it is declared is not the class symbolically referenced by the
 * instruction, a NoSuchMethodError? is thrown.
 *
 * Otherwise, if the resolved method is a class (static) method, the invokespecial
 * instruction throws an IncompatibleClassChangeError?.
 *
 * Otherwise, if no method matching the resolved name and descriptor is selected,
 * invokespecial throws an AbstractMethodError?.
 *
 * Otherwise, if the selected method is abstract, invokespecial throws an
 * AbstractMethodError?.
 *
 * RUNTIME EXCEPTIONS
 *
 * Otherwise, if objectref is null, the invokespecial instruction throws a NullPointerException?.
 *
 * Otherwise, if the selected method is native and the code that implements the
 * method cannot be bound, invokespecial throws an UnsatisfiedLinkError?.
 *
 * NOTES
 *
 * The difference between the invokespecial and the invokevirtual instructions is
 * that invokevirtual invokes a method based on the class of the object. The
 * invokespecial instruction is used to invoke instance initialization methods
 * (3.9) as well as private methods and methods of a superclass of the current
 * class.
 *
 * ACC_SUPER:
 *
 * The setting of the ACC_SUPER flag indicates which of two alternative semantics
 * for its invokespecial instruction the Java virtual machine is to express; the
 * ACC_SUPER flag exists for backward compatibility for code compiled by Sun's
 * older compilers for the Java programming language. All new implementations of
 * the Java virtual machine should implement the semantics for invokespecial
 * documented in this specification. All new compilers to the instruction set of
 * the Java virtual machine should set the ACC_SUPER flag. Sun's older compilers
 * generated ClassFile? flags with ACC_SUPER unset. Sun's older Java virtual
 * machine implementations ignore the flag if it is set.
 *
 * ACC_SUPER 0x0020 Treat superclass methods specially when invoked by the
 * invokespecial instruction.
 *
 * My Translation:
 *     1. compile-time resolved class B
 *     2. A,B,C direct superclass relationships
 *     3. If B.m is protected
 *          - if the caller is in B
 *                then runtime resolved class must be in B or C
 *          - if the caller is in C
 *                then runtime resolved class must be in C
 *     TODO: otherwise what is thrown? <noWikiWord>AbstractMethodError?
 *     4. If B.m is an instance initialization method,
 *          invoke B.m
 *     5. If backward compatible caller does not set ACC_SUPER,
 *          invoke B.m
 *     6. If B is not a superclass of the caller, e.g. A is caller, or unrelated X
 *        is the caller, invoke B.m
 *     7. Otherwise:
 *        If superclass of caller contains name/sig match, use it
 *        Else, recursively through that superclass
 *     8. If none found, throw AbstractMethodError
 *
 * Note: there is NO mention of overriding or accessibility in determining
 * resolved method, except for if the compile-time type is protected.
 *
 * Case 1: B.m is protected
 *         Caller in A: if runtime resolved class in A.m, AbstractMethodError
 *         Caller in B: if runtime resolved class in A.m, AbstractMethodError
 * Case 2: B.m is an instance initialization method
 *         Always invoke B.m
 * Case 3: older javac, caller does not set ACC_SUPER
 *         Always invoke B.m
 * Case 4: A or X (not in hierarchy) calls invokespecial on B.m, invoke B.m
 * Case 5: Caller in B:
 *           if A.m exists, call it, else <noWikiWord>AbstractMethodError
 *         Caller in C:
 *           if B.m exists, call it
 *           if B.m does not exist, and A.m exists, call it
 */

//   TODO: classes without ACC_SUPER attribute
//   TODO: B.m is an instance initialization method

/*
 *   invokespecial <method-spec>
 *
 * invokespecial is used in certain special cases to invoke a method
 * Specifically, invokespecial is used to invoke:
 *      - the instance initialization method, <init>
 *      - a private method of this
 *      - a method in a superclass of this
 *
 * The main use of invokespecial is to invoke an object's instance
 * initialization method, <init>, during the construction phase for a new object.
 * For example, when you write in Java:
 *
 *      new StringBuffer()
 *
 * code like the following is generated:
 *      new java/lang/StringBuffer         ; create a new StringBuffer
 *      dup                                ; make an extra reference to the new instance
 *                                         ; now call an instance initialization method
 *      invokespecial java/lang/StringBuffer/<init>()V
 *                                         ; stack now contains an initialized StringBuffer.
 *
 * invokespecial is also used by the Java language by the 'super' keyword to
 * access a superclass's version of a method. For example, in the class:
 *
 *     class Example {
 *         // override equals
 *         public boolean equals(Object x) {
 *              // call Object's version of equals
 *              return super.equals(x);
 *         }
 *     }
 *
 * the 'super.equals(x)' expression is compiled to:
 *
 *     aload_0  ; push 'this' onto the stack
 *     aload_1  ; push the first argument (i.e. x) onto the stack
 *              ; now invoke Object's equals() method.
 *     invokespecial java/lang/Object/equals(Ljava/lang/Object;)Z
 *
 * Finally, invokespecial is used to invoke a private method. Remember that
 * private methods are only visible to other methods belonging the same class as
 * the private method.
 *
 * Before performing the method invocation, the class and the method identified
 * by <method-spec> are resolved. See Chapter 9 for a description of how methods
 * are resolved.
 *
 * invokespecial first looks at the descriptor given in <method-spec>, and
 * determines how many argument words the method takes (this may be zero). It
 * pops these arguments off the operand stack. Next it pops objectref (a
 * reference to an object) off the operand stack. objectref must be an instance
 * of the class named in <method-spec>, or one of its subclasses. The interpreter
 * searches the list of methods defined by the class named in <method-spec>,
 * looking for a method called methodname whose descriptor is descriptor. This
 * search is not based on the runtime type of objectref, but on the compile time
 * type given in <method-spec>.
 *
 * Once a method has been located, invokespecial calls the method. First, if
 * the method is marked as synchronized, the monitor associated with objectref is
 * entered. Next, a new stack frame structure is established on the call stack.
 * Then the arguments for the method (which were popped off the current method's
 * operand stack) are placed in local variables of the new stack frame structure.
 * arg1 is stored in local variable 1, arg2 is stored in local variable 2 and so
 * on. objectref is stored in local variable 0 (the local variable used for the
 * special Java variable this). Finally, execution continues at the first
 *instruction in the bytecode of the new method.
 *
 * Methods marked as native are handled slightly differently. For native
 * methods, the runtime system locates the platform-specific code for the method,
 * loading it and linking it into the JVM if necessary. Then the native method
 * code is executed with the arguments popped from the operand stack. The exact
 * mechanism used to invoke native methods is implementation-specific.
 *
 * When the method called by invokespecial returns, any single (or double) word
 * return result is placed on the operand stack of the current method. If the
 * invoked method was marked as synchronized, the monitor associated with
 * objectref is exited. Execution continues at the instruction that follows
 * invokespecial in the bytecode.
 *
 * Notes
 *
 * 1. In Java Virtual Machine implementations prior to version JDK 1.02, this
 * instruction was called invokenonvirtual, and was less restrictive than
 * invokespecial - it wasn't limited to invoking only superclass, private or
 * <init> methods. The class access flag ACC_SUPER (see Chapter 4) is used to
 * indicate which semantics are used by a class. In older class files, the
 * ACC_SUPER flag is unset. In all new classes, the ACC_SUPER flag should be set,
 * indicating that the restrictions enforced by invokespecial are obeyed. (In
 * practice, all the common uses of invokenonvirtual continue to be supported
 * by invokespecial, so this change should have little impact on JVM users).
 *
 */

package invokespecial;

import static jdk.internal.org.objectweb.asm.Opcodes.*;
import shared.AbstractGenerator;
import shared.AccessType;

import java.util.HashMap;
import java.util.Map;

public class Generator extends AbstractGenerator {
    public static void main (String[] args) throws Exception {
        new Generator(args).run();
    }
    public Generator(String[] args) {
        super(args);
    }

    protected Checker getChecker(Class paramClass, Class targetClass) {
        return new Checker(paramClass, targetClass);
    }

    public void run() throws Exception {
        // Specify package names
        String pkg1 = "a.";
        String pkg2 = "b.";
        String[] packages = new String[] { "", pkg1, pkg2 };

        boolean isPassed = true;

        // HIERARCHIES
        // The following triples will be used during further
        // hierarchy construction and will specify packages for A, B and C
        String[][] packageSets = new String[][] {
              {   "",   "",   "" }
            , {   "", pkg1, pkg1 }
            , {   "", pkg1, pkg2 }
            , { pkg1,   "", pkg1 }
            , { pkg1,   "", pkg2 }
            , { pkg1, pkg1,   "" }
            , { pkg1, pkg2,   "" }
            , { pkg1, pkg1, pkg1 }
            , { pkg1, pkg1, pkg2 }
            , { pkg1, pkg2, pkg1 }
            , { pkg1, pkg2, pkg2 }
        };

        String [] header = new String[] {
            String.format("%30s %35s", "Method access modifiers", "Call site location")
                , String.format("%4s  %-10s %-10s %-10s   %7s %7s %7s %7s %7s %7s %7s"
                        , "  # "
                        , "A.m()"
                        , "B.m()"
                        , "C.m()"
                        , "  A  "
                        , "pkgA"
                        , "  B  "
                        , " pkgB"
                        , "  C  "
                        , "pkgC "
                        , "  X  "
                        )
                , "-----------------------------------------------------------------------------------------------------------"
        };

        // Print header
        for (String str : header) {
            System.out.println(str);
        }

        // Iterate over all interesting package combinations
        for (String[] pkgSet : packageSets) {
            String packageA = pkgSet[0];
            String packageB = pkgSet[1];
            String packageC = pkgSet[2];

            String classNameA = packageA + "A";
            String classNameB = packageB + "B";
            String classNameC = packageC + "C";

            // For all possible access modifier combinations
            for (AccessType accessFlagA : AccessType.values()) {
                for (AccessType accessFlagB : AccessType.values()) {
                    for (AccessType accessFlagC : AccessType.values()) {
                        Map<String, byte[]> classes = new HashMap<String, byte[]>();

                        String calleeClassName = classNameB;
                        int classFlags = ACC_PUBLIC;

                        // The following hierarhcy is created:
                        //     c.C extends b.B extends a.A extends Object - base hierarchy
                        //     X extends Object - external caller
                        //     c.Caller, b.Caller, a.Caller extends Object - package callers

                        // Generate result storage
                        classes.put(
                                "Result"
                                , new ClassGenerator(
                                    "Result"
                                    , "java.lang.Object"
                                    , ACC_PUBLIC
                                    )
                                .addField(
                                    ACC_PUBLIC | ACC_STATIC
                                    , "value"
                                    , "java.lang.String"
                                    )
                                .getClassFile()
                                );

                        // Generate class A
                        classes.put(
                                classNameA
                                , new ClassGenerator(
                                    classNameA
                                    , "java.lang.Object"
                                    , classFlags
                                    )
                                .addTargetConstructor(accessFlagA)
                                .addTargetMethod(accessFlagA)
                                .addCaller(calleeClassName)
                                .getClassFile()
                                );

                        // Generate class B
                        classes.put(
                                classNameB
                                , new ClassGenerator(
                                    classNameB
                                    , classNameA
                                    , classFlags
                                    )
                                .addTargetConstructor(accessFlagB)
                                .addTargetMethod(accessFlagB)
                                .addCaller(calleeClassName)
                                .getClassFile()
                                );

                        // Generate class C
                        classes.put(
                                classNameC
                                , new ClassGenerator(
                                    classNameC
                                    , classNameB
                                    , classFlags
                                    )
                                .addTargetConstructor(accessFlagC)
                                .addTargetMethod(accessFlagC)
                                .addCaller(calleeClassName)
                                .getClassFile()
                                );

                        // Generate class X
                        String classNameX = "x.X";
                        classes.put(
                                classNameX
                                , new ClassGenerator(
                                    classNameX
                                    , "java.lang.Object"
                                    , classFlags
                                    )
                                .addTargetMethod(accessFlagC)
                                .addCaller(calleeClassName)
                                .getClassFile()
                                );

                        // Generate package callers
                        for (String pkg : packages) {
                            classes.put(
                                    pkg+"Caller"
                                    , new ClassGenerator(
                                        pkg+"Caller"
                                        , "java.lang.Object"
                                        , classFlags
                                        )
                                    .addCaller(calleeClassName)
                                    .getClassFile()
                                    );
                        }

                        String[] callSites = new String[] {
                                classNameA
                                , packageA+"Caller"
                                , classNameB
                                , packageB+"Caller"
                                , classNameC
                                , packageC+"Caller"
                                , classNameX
                        };

                        String caseDescription = String.format(
                                    "%-10s %-10s %-10s| "
                                    , classNameA + " " + accessFlagA
                                    , classNameB + " " + accessFlagB
                                    , classNameC + " " + accessFlagC
                                    );

                        boolean result = exec(classes, caseDescription, calleeClassName, classNameC, callSites);
                        isPassed = isPassed && result;
                    }
                }
            }
        }

        // Print footer
        for (int i = header.length-1; i >= 0; i--) {
            System.out.println(header[i]);
        }

        if (executeTests) {
            System.out.printf("\nEXECUTION STATUS: %s\n", (isPassed? "PASSED" : "FAILED"));
        }
    }
}