Skip to content

Commit 623104e

Browse files
committed
Add tests for BSM resolution on error throw
When an error is thrown during resolveInvokeDynamic, we return a special error-throwing MethodHandle. This MH has no paramters, which can cause problems for OSR Bookkeeping since no arguments will be popped off the stack, causing the assertion failure in ILGen "cannot create a pending push temporary that exceeds that number of slots for this method" since we are trying to push the return value onto the stack when it is already full This commit tests arguments are popped and that the assertion does not occur. Signed-off-by: Matthew Hall <matthew.hall3@outlook.com>
1 parent 58c1275 commit 623104e

File tree

5 files changed

+280
-2
lines changed

5 files changed

+280
-2
lines changed

test/functional/Jsr292/build.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
<include name="**/indyn/Helper.java" />
6666
<include name="**/indyn/SimpleIndyGenerator.java" />
6767
<include name="**/CrossPackageHelper.java"/>
68+
<include name="**/indy/IndyOSRTest.java"/>
6869
<classpath>
6970
<pathelement location="${LIB_DIR}/asm-all.jar" />
7071
<pathelement location="${LIB_DIR}/junit4.jar" />

test/functional/Jsr292/playlist.xml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,29 @@ SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-ex
447447
<impl>ibm</impl>
448448
</impls>
449449
</test>
450+
<test>
451+
<testCaseName>jsr292_InDyn_OSRTest</testCaseName>
452+
<variations>
453+
<variation>-Xjit:"dontInline={*dummy*},limit={*dummy*},count=1,disableAsyncCompilation,initialOptLevel=warm,disableGuardedCountingRecompilation"</variation>
454+
</variations>
455+
<command>TR_EnableStartupNextGenHCRAtAllOpts=1 $(JAVA_COMMAND) $(JVM_OPTIONS) \
456+
-cp $(Q)$(TEST_RESROOT)$(D)jsr292test.jar$(P)$(RESOURCES_DIR)$(P)$(TESTNG)$(P)$(LIB_DIR)$(D)asm-all.jar$(Q) \
457+
org.testng.TestNG -d $(REPORTDIR) $(Q)$(TEST_RESROOT)$(D)testng$(XMLSUFFIX).xml$(Q) \
458+
-testnames jsr292_InDyn_OSRTest \
459+
-groups $(TEST_GROUP) \
460+
-excludegroups $(DEFAULT_EXCLUDE); \
461+
$(TEST_STATUS)</command>
462+
<levels>
463+
<level>extended</level>
464+
</levels>
465+
<groups>
466+
<group>functional</group>
467+
</groups>
468+
<impls>
469+
<impl>openj9</impl>
470+
<impl>ibm</impl>
471+
</impls>
472+
</test>
450473
<!--
451474
TODO: The following test cases specific to Lookup are temporarily excluded in Java 14
452475
as there is no backward compatibility of these APIs since Java 14 due to the new changes
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
/*
2+
* Copyright IBM Corp. and others 2001
3+
*
4+
* This program and the accompanying materials are made available under
5+
* the terms of the Eclipse Public License 2.0 which accompanies this
6+
* distribution and is available at https://www.eclipse.org/legal/epl-2.0/
7+
* or the Apache License, Version 2.0 which accompanies this distribution and
8+
* is available at https://www.apache.org/licenses/LICENSE-2.0.
9+
*
10+
* This Source Code may also be made available under the following
11+
* Secondary Licenses when the conditions for such availability set
12+
* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU
13+
* General Public License, version 2 with the GNU Classpath
14+
* Exception [1] and GNU General Public License, version 2 with the
15+
* OpenJDK Assembly Exception [2].
16+
*
17+
* [1] https://www.gnu.org/software/classpath/license.html
18+
* [2] https://openjdk.org/legal/assembly-exception.html
19+
*
20+
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0
21+
*/
22+
package com.ibm.j9.jsr292.indyn;
23+
24+
import org.openj9.test.util.VersionCheck;
25+
26+
import org.testng.annotations.Test;
27+
import org.testng.annotations.DataProvider;
28+
29+
import org.testng.Assert;
30+
import org.testng.AssertJUnit;
31+
import org.objectweb.asm.*;
32+
import static org.objectweb.asm.Opcodes.*;
33+
34+
import java.lang.invoke.MethodHandle;
35+
import java.lang.invoke.MethodHandles;
36+
import java.lang.invoke.ConstantCallSite;
37+
import java.lang.invoke.CallSite;
38+
import java.lang.invoke.MethodType;
39+
import java.lang.reflect.Method;
40+
41+
public class IndyOSRTest {
42+
43+
private static Throwable thrower = null;
44+
private static String dummyClassPathName = "com/ibm/j9/jsr292/indyn/TestBSMError";
45+
private static String dummyClassFullName = "com.ibm.j9.jsr292.indyn.TestBSMError";
46+
47+
// generate method with invokedynamic bytecode that uses the BSM "bootstrap" below
48+
private static byte[] generate(int version) {
49+
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
50+
MethodVisitor mv;
51+
cw.visit(VersionCheck.major() + V1_8 - 8, ACC_PUBLIC, dummyClassPathName + version, null, "java/lang/Object", null);
52+
53+
mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "dummy", "()V", null, null);
54+
mv.visitCode();
55+
56+
Handle bsm = new Handle(
57+
H_INVOKESTATIC,
58+
"com/ibm/j9/jsr292/indyn/IndyOSRTest",
59+
"bootstrap",
60+
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;",
61+
false
62+
);
63+
64+
mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
65+
mv.visitLdcInsn(1L);
66+
mv.visitLdcInsn(2L);
67+
//mv.visitVarInsn(ILOAD, 0);
68+
mv.visitLdcInsn(3);
69+
mv.visitLdcInsn(4);
70+
mv.visitInvokeDynamicInsn("someName", "(JJII)Ljava/lang/String;", bsm);
71+
mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
72+
mv.visitInsn(RETURN);
73+
mv.visitMaxs(6, 4);
74+
mv.visitEnd();
75+
cw.visitEnd();
76+
return cw.toByteArray();
77+
}
78+
79+
80+
81+
public static CallSite bootstrap(MethodHandles.Lookup lookup, String name, MethodType mt) throws Throwable {
82+
if (thrower == null) {
83+
MethodHandle target = MethodHandles.lookup().findStatic(IndyTest.class, "sanity", MethodType.methodType(String.class, long.class, long.class, int.class, int.class));
84+
return new ConstantCallSite(target);
85+
}
86+
System.out.println("Throwing");
87+
throw thrower;
88+
}
89+
90+
public static String sanity(long a, long b, int c, int d) {
91+
return "bootstrap" + a + "," + b + "," + c + "," + d;
92+
}
93+
94+
private class ByteArrayClassLoader extends ClassLoader {
95+
public Class<?> getc(String name, byte[] b) {
96+
return defineClass(name,b,0,b.length);
97+
}
98+
}
99+
100+
@DataProvider(name="throwableProvider")
101+
public static Object[][] throwableProvider() {
102+
return new Object[][] {{new NullPointerException(), 1}, {new StackOverflowError(), 2}, {new IllegalArgumentException(), 3},
103+
{new ClassCastException(), 4}};
104+
}
105+
106+
@Test(groups = {"level.extended"}, dataProvider="throwableProvider", invocationCount=1)
107+
public void testBSMErrorThrow(Throwable t, int version) {
108+
thrower = t;
109+
ByteArrayClassLoader c = new ByteArrayClassLoader();
110+
byte[] b = IndyOSRTest.generate(version);
111+
System.out.println(b.length);
112+
Class<?> cls = c.getc(dummyClassFullName + version, b);
113+
114+
// attempt once in the interpreter
115+
try {
116+
if (t == null){
117+
Assert.assertTrue(cls.getMethod("dummy").invoke(null).equals("bootstrap1,2,3,4"));
118+
} else {
119+
cls.getMethod("dummy").invoke(null);
120+
}
121+
} catch(IllegalAccessException e) {
122+
Assert.fail("Cannot access dummy method");
123+
} catch(NoSuchMethodException e) {
124+
Assert.fail("Cannot find dummy method");
125+
} catch (java.lang.reflect.InvocationTargetException e) {
126+
if (t == null) {
127+
Assert.fail("Bootsrap method should not throw error when parameter t is null");
128+
}
129+
} catch (Throwable t2) {
130+
System.out.println("Caught something");
131+
}
132+
133+
// run again after compilation
134+
try {
135+
if (t == null){
136+
Assert.assertTrue(cls.getMethod("dummy").invoke(null).equals("bootstrap1,2,3,4"));
137+
} else {
138+
cls.getMethod("dummy").invoke(null);
139+
}
140+
} catch(IllegalAccessException e) {
141+
Assert.fail("Cannot access dummy method");
142+
} catch(NoSuchMethodException e) {
143+
Assert.fail("Cannot find dummy method");
144+
} catch (java.lang.reflect.InvocationTargetException e) {
145+
if (t == null) {
146+
Assert.fail("Bootsrap method should not throw error when parameter t is null");
147+
}
148+
}
149+
}
150+
}

test/functional/Jsr292/src/com/ibm/j9/jsr292/indyn/IndyTest.java

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@
2424
import org.openj9.test.util.VersionCheck;
2525

2626
import org.testng.annotations.Test;
27+
2728
import org.testng.Assert;
2829
import org.testng.AssertJUnit;
29-
import static org.objectweb.asm.Opcodes.ARETURN;
30+
import org.objectweb.asm.*;
31+
import static org.objectweb.asm.Opcodes.*;
3032

3133
import java.lang.invoke.MethodHandle;
3234
import java.lang.invoke.MethodType;
@@ -403,4 +405,93 @@ public void test_CallSiteNullErrorRethrown() {
403405
Assert.assertTrue(VersionCheck.major() >= 11);
404406
}
405407
}
408+
409+
private static long var1 = 1871533038L;
410+
private double var2 = 1;
411+
private int var3 = 1;
412+
private int var4 = 4;
413+
private volatile boolean running = true;
414+
415+
private void stop() {
416+
running = false;
417+
}
418+
419+
private static Throwable thrower;
420+
421+
private static byte[] generate() {
422+
ClassWriter cw = new ClassWriter(0);
423+
MethodVisitor mv;
424+
425+
cw.visit(VersionCheck.major() + V1_8 - 8, ACC_PUBLIC, "TestBSMError", null, "java/lang/Object", null);
426+
mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "test", "(I)V", null, null);
427+
mv.visitCode();
428+
429+
Handle bsm = new Handle(
430+
H_INVOKESTATIC,
431+
"IndyTest",
432+
"bootstrap",
433+
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;)Ljava/lang/invoke/CallSite;",
434+
false
435+
);
436+
mv.visitInvokeDynamicInsn("run", "()V", bsm);
437+
mv.visitInsn(RETURN);
438+
mv.visitMaxs(1,1);
439+
mv.visitEnd();
440+
cw.visitEnd();
441+
return cw.toByteArray();
442+
}
443+
444+
public static CallSite bootstrap(MethodHandles.Lookup lookup, String name, MethodType mt, String template) throws Throwable {
445+
if (thrower == NULL) {
446+
MethodHandle target = MethodHandles.lookup().findStatic(IdyTest.class, "sanity", MethodType.methodType(String.class));
447+
return ConstantCallSite(target);
448+
}
449+
System.out.println("Throwing\n");
450+
throw thrower;
451+
}
452+
453+
public static String sanity() {
454+
return "Sanity"
455+
}
456+
457+
public String test(int x) {
458+
return "test" + x;
459+
}
460+
461+
public void recurse() {
462+
if (!running) {
463+
return;
464+
}
465+
for (int i = 1; i < 5; i++) {
466+
467+
String t = "Test" + var3 + "," + var4;
468+
try {
469+
java.lang.reflect.Method method = IndyTest.class.getMethod("recurse", void.class);
470+
method.invoke(this);
471+
} catch (NoSuchMethodException e) {
472+
// ...
473+
} catch (IllegalAccessException e) {
474+
// ...
475+
} catch (java.lang.reflect.InvocationTargetException e) {
476+
// ...
477+
}
478+
}
479+
String val = "Value: " + IndyTest.var1 + "," + Double.doubleToLongBits(var2) + "," + var3 + "," + var4 + "," + var3;
480+
}
481+
482+
// test that String concatenation doesn't not give wrong callsite (resulting in ILGen assertion error)
483+
// when resolution is done in deep recursion
484+
@Test(groups = {"level.extended"})
485+
public void testOSRRecurseStringConcat() {
486+
IndyTest tester = new IndyTest();
487+
Thread thread = new Thread(() -> tester.recurse());
488+
thread.start();
489+
490+
try {
491+
Thread.sleep(30000); // let thread run for 30s
492+
tester.stop();
493+
thread.join();
494+
} catch (InterruptedException e) {}
495+
System.out.println("Ran with no errors");
496+
}
406497
}

test/functional/Jsr292/testng.xml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,17 @@
116116
<class name="com.ibm.j9.jsr292.indyn.ComplexIndyTest"/>
117117
</classes>
118118
</test>
119-
</suite>
119+
120+
<test name="jsr292_InDyn_OSRTest">
121+
<classes>
122+
<class name="com.ibm.j9.jsr292.indyn.IndyOSRTest"/>
123+
</classes>
124+
</test>
125+
126+
<test name="jsr292_InDyn_OSRTest">
127+
<classes>
128+
<class name="com.ibm.j9.jsr292.indyn.IndyOSRTest"/>
129+
</classes>
130+
</test>
131+
132+
</suite>

0 commit comments

Comments
 (0)