8308237: add JDWP and JDI virtual thread support for ThreadReference.PopFrames

8308481: JDI TestScaffold does not support passing app arguments to the debuggee
8305632: Test com/sun/jdi/PopAndInvokeTest.java fails with OpaqueFrameException

Reviewed-by: alanb, sspitsyn, lmesnik
This commit is contained in:
Chris Plummer 2023-05-24 16:54:05 +00:00
parent d10467e27b
commit 89f2d458a2
9 changed files with 501 additions and 74 deletions

View File

@ -2692,10 +2692,9 @@ JDWP "Java(tm) Debug Wire Protocol"
"<code>objectref</code> is added back as well. The Java virtual machine "
"program counter is restored to the opcode of the invoke instruction."
"<p>"
"The target VM may not support, or may only provide limited support, for this "
"command when the thread is a virtual thread. It may, for example, only support "
"this command when the virtual thread is suspended at a breakpoint or singlestep "
"event."
"This command may be used to pop frames of a virtual thread when "
"it is suspended at an event. An implementation may support popping "
"the frames of a suspended virtual thread in other cases."
"<p>"
"Since JDWP version 1.4. Requires canPopFrames capability - see "
"<a href=\"#JDWP_VirtualMachine_CapabilitiesNew\">CapabilitiesNew</a>."
@ -2713,8 +2712,8 @@ JDWP "Java(tm) Debug Wire Protocol"
(Error NO_MORE_FRAMES)
(Error OPAQUE_FRAME "If one or more of the frames to pop is a native "
"method or its caller is a native method, or the "
"thread is a virtual thread and the implementation "
"is unable to pop the frames.")
"thread is a suspended virtual thread and the implementation "
"was unable to pop the frames.")
(Error NOT_IMPLEMENTED)
(Error VM_DEAD)
)

View File

@ -370,15 +370,14 @@ public interface ThreadReference extends ObjectReference {
* Thus the target program may
* proceed differently than the user would expect.
* <P>
* The specified thread must be suspended.
* This thread must be suspended.
* <P>
* All <code>StackFrame</code> objects for this thread are
* invalidated.
* <p>
* The target VM may not support, or may only provide limited support,
* for popping stack frames when the thread is a virtual thread.
* It may, for example, only support this operation when the virtual
* thread is suspended at a breakpoint or singlestep event.
* This method may be used to pop frames of a virtual thread when
* it is suspended at an event. An implementation may support popping
* the frames of a suspended virtual thread in other cases.
* <P>
* No events are generated by this method.
* <P>
@ -403,8 +402,8 @@ public interface ThreadReference extends ObjectReference {
* @throws java.lang.IllegalArgumentException if <CODE>frame</CODE>
* is not on this thread's call stack.
*
* @throws OpaqueFrameException if this thread is a virtual thread and the
* target VM is unable to pop the frames.
* @throws OpaqueFrameException if this thread is a suspended virtual thread and the
* target VM was unable to pop the frames.
*
* @throws NativeMethodException if one of the frames that would be
* popped is that of a native method or if the frame previous to

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2023, 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
@ -396,9 +396,27 @@ public class StackFrameImpl extends MirrorImpl
switch (exc.errorCode()) {
case JDWP.Error.OPAQUE_FRAME:
if (thread.isVirtual()) {
throw new OpaqueFrameException(); // can only happen with virtual threads
// We first need to find out if the current frame is native, or if the
// previous frame is native, in which case we throw NativeMethodException
for (int i = 0; i < 2; i++) {
StackFrameImpl sf;
try {
sf = (StackFrameImpl)thread.frame(i);
} catch (IndexOutOfBoundsException e) {
// This should never happen, but we need to check for it.
break;
}
sf.validateStackFrame();
MethodImpl meth = (MethodImpl)sf.location().method();
if (meth.isNative()) {
throw new NativeMethodException();
}
}
// No native frames involved. Must have been due to thread
// not being mounted.
throw new OpaqueFrameException();
} else {
throw new NativeMethodException(); // can only happen with platform threads
throw new NativeMethodException();
}
case JDWP.Error.THREAD_NOT_SUSPENDED:
throw new IncompatibleThreadStateException(

View File

@ -94,32 +94,6 @@ vmTestbase/nsk/jdb/repeat/repeat001/repeat001.java 8300707 generic-all
vmTestbase/nsk/jdi/ExceptionEvent/catchLocation/location002/TestDescription.java 8278470 generic-all
####
## JVMTI PopFrame() is returning OPAQUE_FRAME because vthreads are not supported.
## Note: vmTestbase/nsk/jdi/ThreadReference/popFrames/popframes001 was converted
## to support vthreads and expect the OPAQUE_FRAME error. The others were
## not because they don't add any additional value.
vmTestbase/nsk/jdb/pop_exception/pop_exception001/pop_exception001.java 8285414 generic-all
vmTestbase/nsk/jdi/VirtualMachine/redefineClasses/redefineclasses002/TestDescription.java 8285414 generic-all
vmTestbase/nsk/jdi/Scenarios/invokeMethod/popframes001/TestDescription.java 8285414 generic-all
vmTestbase/nsk/jdi/BScenarios/hotswap/tc01x002/TestDescription.java 8285414 generic-all
vmTestbase/nsk/jdi/BScenarios/hotswap/tc02x001/TestDescription.java 8285414 generic-all
vmTestbase/nsk/jdi/BScenarios/hotswap/tc02x002/TestDescription.java 8285414 generic-all
vmTestbase/nsk/jdi/BScenarios/hotswap/tc04x001/TestDescription.java 8285414 generic-all
vmTestbase/nsk/jdi/BScenarios/hotswap/tc04x002/TestDescription.java 8285414 generic-all
vmTestbase/nsk/jdi/BScenarios/hotswap/tc06x001/TestDescription.java 8285414 generic-all
vmTestbase/nsk/jdi/BScenarios/hotswap/tc08x001/TestDescription.java 8285414 generic-all
vmTestbase/nsk/jdi/BScenarios/hotswap/tc10x002/TestDescription.java 8285414 generic-all
vmTestbase/nsk/jdi/ThreadReference/popFrames/popframes002/TestDescription.java 8285414 generic-all
vmTestbase/nsk/jdi/ThreadReference/popFrames/popframes003/TestDescription.java 8285414 generic-all
vmTestbase/nsk/jdi/ThreadReference/popFrames/popframes004/TestDescription.java 8285414 generic-all
vmTestbase/nsk/jdi/ThreadReference/popFrames/popframes001/TestDescription.java 8308237 generic-all
####
## JVMTI ForceEarlyReturn not supported for vthreads (JVMTI_ERROR_OPAQUE_FRAME)
## Note forceEarlyReturn002 was converted to support vthreads. The rest were not

View File

@ -93,8 +93,6 @@ import java.io.*;
public class popframes001 extends JDIBase {
static boolean vthreadMode = "Virtual".equals(System.getProperty("main.wrapper"));
public static void main (String argv[]) {
int result = run(argv, System.out);
@ -221,10 +219,6 @@ public class popframes001 extends JDIBase {
try {
testRun();
if (vthreadMode) {
return 0; // just exit. we already got the expected OpaqueFrameException
}
log2("waiting for VMDeathEvent");
getEventSet();
if (eventIterator.nextEvent() instanceof VMDeathEvent)
@ -356,24 +350,10 @@ public class popframes001 extends JDIBase {
log2("......thread2Ref.popFrames(stackFrame);");
try {
thread2Ref.popFrames(stackFrame);
if (vthreadMode) {
log3("ERROR: Expected OpaqueFrameException");
testExitCode = FAILED;
}
} catch ( Exception e ) {
if (vthreadMode && (e instanceof OpaqueFrameException)) {
// pass. resume thread and exit
log2("......got expected OpaqueFrameException");
log2("......thread2Ref.resume();");
thread2Ref.resume();
breakpointForCommunication();
vm.resume();
break;
} else {
log3("ERROR: " + e.getClass().getSimpleName());
testExitCode = FAILED;
throw e;
}
} catch ( IncompatibleThreadStateException e ) {
log3("ERROR: IncompatibleThreadStateException");
testExitCode = FAILED;
break;
}
log2("......thread2Ref.resume();");

View File

@ -36,9 +36,6 @@ com/sun/jdi/JdbStopThreadTest.java 8285422 generic-all
com/sun/jdi/JdbStopThreadidTest.java 8285422 generic-all
com/sun/jdi/MethodEntryExitEvents.java 8285422 generic-all
com/sun/jdi/MultiBreakpointsTest.java 8285422 generic-all
com/sun/jdi/PopAndStepTest.java 8285422 generic-all
com/sun/jdi/PopAsynchronousTest.java 8285422 generic-all
com/sun/jdi/PopSynchronousTest.java 8285422 generic-all
com/sun/jdi/RedefineCrossStart.java 8285422 generic-all
com/sun/jdi/RedefineG.java 8285422 generic-all
com/sun/jdi/RedefineNestmateAttr/TestNestmateAttr.java 8285422 generic-all
@ -46,7 +43,6 @@ com/sun/jdi/RedefineTTYLineNumber.java 8285422 generic-all
com/sun/jdi/ReferrersTest.java 8285422 generic-all
com/sun/jdi/SetLocalWhileThreadInNative.java 8285422 generic-all
com/sun/jdi/StepTest.java 8285422 generic-all
com/sun/jdi/PopAndInvokeTest.java 8305632 generic-all
com/sun/jdi/cds/CDSBreakpointTest.java 8307778 generic-all
com/sun/jdi/cds/CDSDeleteAllBkptsTest.java 8307778 generic-all
com/sun/jdi/cds/CDSFieldWatchpoints.java 8307778 generic-all

View File

@ -0,0 +1,362 @@
/*
* Copyright (c) 2023, 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.
*/
/**
* @test
* @summary Call popFrames() on threads in various states not covered
* well by other tests. Most notably, this test includes
* test cases for a suspended but unmounted virtual thread.
* It is mostly for testing for OpaqueFrameException and
* NativeMethodException.
*
* @run build TestScaffold VMConnection TargetListener TargetAdapter
* @run compile -g PopFramesTest.java
* @run driver PopFramesTest SLEEP_NATIVE
* @run driver PopFramesTest LOOP_NATIVE
* @run driver PopFramesTest SLEEP_PRENATIVE
* @run driver PopFramesTest LOOP_PRENATIVE
* @run driver PopFramesTest SLEEP_NONATIVE
* @run driver PopFramesTest LOOP_NONATIVE
*/
import com.sun.jdi.*;
import com.sun.jdi.event.*;
import java.util.*;
/*
* There are six test modes covered by this test:
* SLEEP_NATIVE
* LOOP_NATIVE
* SLEEP_PRENATIVE
* LOOP_PRENATIVE
* SLEEP_NONATIVE
* LOOP_NONATIVE
*
* SLEEP: the debuggee blocks in Thread.sleep().
* LOOP: the debuggee sits in a tight loop.
* NATIVE: there is a native frame within the set of frames to pop.
* PRENATIVE: there is a native frame before the set of frames to pop.
* NONATIVE: there is no native frame (purposefully) present in the stack.
*
* In all cases the thread is suspended and errors such as IllegalArgumentException
* and InvalidStackFrameException should not happen. The popFrames() calls should
* either pass, or produce OpaqueFrameException or NativeMethodException.
*
* Call stacks for each test mode (and expected result):
* - Note in all cases the popMethod() frame is the frame passed to popFrames().
* - Note that Thread.sleep() usually results in the native Thread.sleep0() frame
* being at the top of the stack. However, for a mounted virtual thread
* it does not result in any native frames due to how the VM parks virtual threads.
*
* SLEEP_NATIVE (NativeMethodException):
* Thread.sleep() + methods called by Thread.sleep()
* loopOrSleep()
* upcallMethod()
* doUpcall() <-- native method
* popMethod()
* main()
*
* LOOP_NATIVE (NativeMethodException):
* loopOrSleep() <-- tight loop
* upcallMethod()
* doUpcall() <-- native method
* popMethod()
* main()
*
* SLEEP_PRENATIVE (NativeMethodException due to Thread.sleep() blocking in a native method):
* Thread.sleep() + methods called by Thread.sleep()
* loopOrSleep()
* popMethod()
* upcallMethod()
* doUpcall() <-- native method
* main()
*
* LOOP_PRENATIVE (no exception):
* loopOrSleep() <-- tight loop
* popMethod()
* upcallMethod()
* doUpcall() <-- native method
* main()
*
* SLEEP_NONATIVE (NativeMethodException for platform thread or OpaqueFrameException
* for virtual thread. See explanation in runTests().):
* Thread.sleep() + methods called by Thread.sleep()
* loopOrSleep()
* popMethod()
* main()
*
* LOOP_NONATIVE (no exception):
* loopOrSleep() <-- tight loop
* popMethod()
* main()
*/
class PopFramesTestTarg {
static TestMode mode;
static {
System.loadLibrary("PopFramesTestTarg");
}
/*
* This is the method whose frame (and all those after it) will be popped.
*/
public static void popMethod() {
System.out.println(" debuggee: in popMethod");
if (mode.isCallNative()) {
doUpcall();
} else {
loopOrSleep();
}
}
public static void loopOrSleep() {
if (mode.isDoLoop()) {
while (true);
} else {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
public static native void doUpcall(); // native method that will call upcallMethod()
public static void upcallMethod() {
if (mode.isCallPrenative()) {
popMethod();
} else {
loopOrSleep();
}
}
public static void main(String[] args) {
System.out.println(" debuggee: Howdy!");
// We expect just one argument, which is the test mode, such as SLEEP_NONATIVE.
if (args.length != 1) {
throw new RuntimeException("Must pass 1 arguments to PopFramesTestTarg");
}
System.out.println(" debuggee: args[0]: " + args[0]);
mode = Enum.valueOf(TestMode.class, args[0]); // convert test mode string to an enum
System.out.println(" debuggee: test mode: " + mode);
if (mode.isCallNative()) {
popMethod(); // call popMethod() directly, and it will call out to native
} else if (mode.isCallPrenative()) {
doUpcall(); // call native method that will call back into java to call popMethod()
} else {
popMethod(); // call popMethod() directly
}
System.out.println(" debuggee: Goodbye from PopFramesTest!");
}
}
/*
* The different modes the test can be run in. See test description comment above.
*/
enum TestMode {
SLEEP_NATIVE,
LOOP_NATIVE,
SLEEP_PRENATIVE,
LOOP_PRENATIVE,
SLEEP_NONATIVE,
LOOP_NONATIVE;
// Returns true if debuggee should block in an infinite loop. Otherwise it calls Thread.sleep().
boolean isDoLoop() {
return this == LOOP_NATIVE || this == LOOP_PRENATIVE || this == LOOP_NONATIVE;
}
// Returns true if debuggee should introduce a native frame within the set of frames to pop.
boolean isCallNative() {
return this == LOOP_NATIVE || this == SLEEP_NATIVE;
}
// Returns true if debuggee should introduce a native frame before the set of frames to pop.
// The purpose is to cause the virtual thread to be pinned.
boolean isCallPrenative() {
return this == LOOP_PRENATIVE || this == SLEEP_PRENATIVE;
}
}
/********** test program **********/
public class PopFramesTest extends TestScaffold {
private static TestMode mode;
PopFramesTest(String args[]) {
super(args);
}
public static void main(String[] args) throws Exception {
// We should get one argument that indicates the test mode, such as SLEEP_NONATIVE.
if (args.length != 1) {
throw new RuntimeException("Must pass one argument to PopFramesTestTarg");
}
mode = Enum.valueOf(TestMode.class, args[0]); // convert test mode string to an enum
/*
* The @run command looks something like:
* @run driver PopFramesTest SLEEP_NONATIVE
* We need to pass SLEEP_NONATIVE to the debuggee. We also need to insert
* -Djava.library.path so the native method can be accessed if called.
*/
String nativePath = "-Djava.library.path=" + System.getProperty("java.library.path");
String[] newArgs = new String[2];
newArgs[0] = nativePath;
newArgs[1] = args[0]; // pass test mode, such as SLEEP_NONATIVE
new PopFramesTest(newArgs).startTests();
}
StackFrame frameFor(ThreadReference thread, String methodName) throws Exception {
Iterator it = thread.frames().iterator();
while (it.hasNext()) {
StackFrame frame = (StackFrame)it.next();
if (frame.location().method().name().equals(methodName)) {
return frame;
}
}
failure("FAIL: " + methodName + " not on stack");
return null;
}
public void printStack(ThreadReference thread, String msg) throws Exception {
System.out.println(msg);
List<StackFrame> stack_frames = thread.frames();
int i = 0;
String sourceName;
for (StackFrame f : stack_frames) {
try {
sourceName = f.location().sourceName();
} catch (AbsentInformationException aie) {
sourceName = "Unknown source";
}
System.out.println("frame[" + i++ +"]: " + f.location().method() +
" (bci:"+ f.location().codeIndex() + ")" +
" (" + sourceName + ":"+ f.location().lineNumber() + ")");
}
}
/********** test core **********/
protected void runTests() throws Exception {
BreakpointEvent bpe = startTo("PopFramesTestTarg", "loopOrSleep", "()V");
ClassType targetClass = (ClassType)bpe.location().declaringType();
ThreadReference mainThread = bpe.thread();
// Resume main thread until it is in Thread.sleep() or the infinite loop.
mainThread.resume();
try {
Thread.sleep(1000); // give thread chance to get into Thread.sleep() or loop
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
mainThread.suspend(); // Suspend thread while in Thread.sleep() or loop
printStack(mainThread, "Debuggee stack before popFrames():");
/*
* Figure out which exception popFrames() should throw.
*/
Class expected_exception;
switch(mode) {
case SLEEP_NATIVE:
case LOOP_NATIVE:
case SLEEP_PRENATIVE:
/*
* For the two NATIVE cases, there is a native frame within the set of frames
* to pop. For the SLEEP_PRENATIVE case, there also ends up being a native
* frame. It will either be Thread.sleep0() for platform threads or
* Unsafe.park() for virtual threads. See the SLEEP_NATIVE comment below
* for more details.
*/
expected_exception = NativeMethodException.class;
break;
case LOOP_PRENATIVE:
case LOOP_NONATIVE:
/*
* For these two test cases, there are no native frames within the set of
* frames to pop, nor in the frame previous to the frame to pop, so no
* exception is expected.
*/
expected_exception = null;
break;
case SLEEP_NONATIVE:
/*
* For platform threads, Thread.sleep() results in the Thread.sleep0() native
* frame on the stack, so the end result is NativeMethodException. For virtual
* threads it is not quite so simple. If the thead is pinned (such as when
* there is already a native method on the stack), you end up in
* VirtualThread.parkOnCarrierThread(), which calls Unsafe.park(), which is a
* native method, so again this results in NativeMethodException. However, for
* a virtual thread that is not pinned (which is true for this test case), you
* end up with no native methods on the stack due to how Continuation.yield()
* works. So you have an unmounted virtual thread with no native frames, which
* results in OpaqueFrameException being thrown.
*/
String mainWrapper = System.getProperty("main.wrapper");
if ("Virtual".equals(mainWrapper)) {
expected_exception = OpaqueFrameException.class;
} else {
expected_exception = NativeMethodException.class;
}
break;
default:
throw new RuntimeException("Bad test mode: " + mode);
}
/*
* Pop all the frames up to and including the popMethod() frame.
*/
try {
mainThread.popFrames(frameFor(mainThread, "popMethod"));
if (expected_exception != null) {
failure("failure: popFrames() did not get expected exception: " + expected_exception);
}
} catch (Exception ex) {
if (expected_exception == ex.getClass()) {
System.out.println("success: popFrames() got expected exception: " + ex);
} else {
failure("failure: popFrames() got unexpected exception: " + ex);
}
}
printStack(mainThread, "Debuggee stack after popFrames():");
/*
* Most tests do a listenUntilVMDisconnect() here, but there is no real need for it
* with this test, and doing so would require finding a way to get the debuggee
* to exit the endless loop it might be in. When we return, TestScaffold will
* call TestScaffold.shutdown(), causing the debuggee process to be terminated quickly.
*/
if (testFailed) {
throw new Exception("PopFramesTest failed");
}
System.out.println("Passed:");
}
}

View File

@ -361,8 +361,44 @@ abstract public class TestScaffold extends TargetAdapter {
}
protected void startUp(String targetName) {
List<String> argList = new ArrayList(Arrays.asList(args));
argList.add(targetName);
/*
* args[] contains all VM arguments followed by the app arguments.
* We need to insert targetName between the two types of arguments.
*/
boolean expectSecondArg = false;
boolean foundFirstAppArg = false;
List<String> argList = new ArrayList();
for (int i = 0; i < args.length; i++) {
String arg = args[i].trim();
if (foundFirstAppArg) {
argList.add(arg);
continue;
}
if (expectSecondArg) {
expectSecondArg = false;
argList.add(arg);
continue;
}
if (doubleWordArgs.contains(arg)) {
expectSecondArg = true;
argList.add(arg);
continue;
}
if (arg.startsWith("-")) {
argList.add(arg);
continue;
}
// We reached the first app argument.
argList.add(targetName);
argList.add(arg);
foundFirstAppArg = true;
}
if (!foundFirstAppArg) {
// Add the target since we didn't do that in the above loop.
argList.add(targetName);
}
println("run args: " + argList);
connect(argList.toArray(args));
waitForVMStart();
@ -461,8 +497,11 @@ abstract public class TestScaffold extends TargetAdapter {
testFailed = true;
}
final List<String> doubleWordArgs = List.of("-cp", "-classpath", "--add-opens", "--class-path",
"--upgrade-module-path", "--add-modules", "-d", "--add-exports", "--patch-module", "--module-path");
final List<String> doubleWordArgs = List.of(
"-connect", "-trace", // special TestScaffold args
"-cp", "-classpath", "--add-opens", "--class-path",
"--upgrade-module-path", "--add-modules", "-d", "--add-exports",
"--patch-module", "--module-path");
private ArgInfo parseArgs(String args[]) {
ArgInfo argInfo = new ArgInfo();

View File

@ -0,0 +1,60 @@
/*
* Copyright (c) 2023, 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include "jni.h"
static jclass test_class;
static jmethodID mid;
static jint current_jni_version = JNI_VERSION_19;
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env;
jclass cl;
(*vm)->GetEnv(vm, (void **) &env, current_jni_version);
cl = (*env)->FindClass(env, "PopFramesTestTarg");
test_class = (*env)->NewGlobalRef(env, cl);
mid = (*env)->GetStaticMethodID(env, test_class, "upcallMethod", "()V");
return current_jni_version;
}
/*
* Class: NativeMethod
* Method: doUpcall
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_PopFramesTestTarg_doUpcall(JNIEnv *env, jobject obj) {
(*env)->CallStaticVoidMethod(env, obj, mid);
if ((*env)->ExceptionCheck(env)) {
(*env)->ExceptionDescribe(env);
(*env)->FatalError(env, "Exception thrown");
}
}