Skip to content

Commit e14fb4f

Browse files
author
Doug Simon
committed
8279437: [JVMCI] exception in HotSpotJVMCIRuntime.translate can exit the VM
Reviewed-by: kvn
1 parent 77757ba commit e14fb4f

File tree

10 files changed

+460
-133
lines changed

10 files changed

+460
-133
lines changed

src/hotspot/share/jvmci/jvmciCompilerToVM.cpp

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -2376,7 +2376,7 @@ C2V_VMENTRY_PREFIX(void, detachCurrentThread, (JNIEnv* env, jobject c2vm))
23762376
}
23772377
C2V_END
23782378

2379-
C2V_VMENTRY_0(jlong, translate, (JNIEnv* env, jobject, jobject obj_handle))
2379+
C2V_VMENTRY_0(jlong, translate, (JNIEnv* env, jobject, jobject obj_handle, jboolean callPostTranslation))
23802380
requireJVMCINativeLibrary(JVMCI_CHECK_0);
23812381
if (obj_handle == NULL) {
23822382
return 0L;
@@ -2427,7 +2427,9 @@ C2V_VMENTRY_0(jlong, translate, (JNIEnv* env, jobject, jobject obj_handle))
24272427
const char* cstring = name_string.is_null() ? NULL : thisEnv->as_utf8_string(name_string);
24282428
// Create a new HotSpotNmethod instance in the peer runtime
24292429
result = peerEnv->new_HotSpotNmethod(mh, cstring, isDefault, compileIdSnapshot, JVMCI_CHECK_0);
2430-
if (nm == NULL) {
2430+
if (result.is_null()) {
2431+
// exception occurred (e.g. OOME) creating a new HotSpotNmethod
2432+
} else if (nm == NULL) {
24312433
// nmethod must have been unloaded
24322434
} else {
24332435
// Link the new HotSpotNmethod to the nmethod
@@ -2450,6 +2452,13 @@ C2V_VMENTRY_0(jlong, translate, (JNIEnv* env, jobject, jobject obj_handle))
24502452
JVMCI_THROW_MSG_0(IllegalArgumentException,
24512453
err_msg("Cannot translate object of type: %s", thisEnv->klass_name(obj)));
24522454
}
2455+
if (callPostTranslation) {
2456+
peerEnv->call_HotSpotJVMCIRuntime_postTranslation(result, JVMCI_CHECK_0);
2457+
}
2458+
// Propagate any exception that occurred while creating the translated object
2459+
if (peerEnv->transfer_pending_exception(thread, thisEnv)) {
2460+
return 0L;
2461+
}
24532462
return (jlong) peerEnv->make_global(result).as_jobject();
24542463
}
24552464

@@ -2790,7 +2799,7 @@ JNINativeMethod CompilerToVM::methods[] = {
27902799
{CC "getCurrentJavaThread", CC "()J", FN_PTR(getCurrentJavaThread)},
27912800
{CC "attachCurrentThread", CC "([BZ)Z", FN_PTR(attachCurrentThread)},
27922801
{CC "detachCurrentThread", CC "()V", FN_PTR(detachCurrentThread)},
2793-
{CC "translate", CC "(" OBJECT ")J", FN_PTR(translate)},
2802+
{CC "translate", CC "(" OBJECT "Z)J", FN_PTR(translate)},
27942803
{CC "unhand", CC "(J)" OBJECT, FN_PTR(unhand)},
27952804
{CC "updateHotSpotNmethod", CC "(" HS_NMETHOD ")V", FN_PTR(updateHotSpotNmethod)},
27962805
{CC "getCode", CC "(" HS_INSTALLED_CODE ")[B", FN_PTR(getCode)},

src/hotspot/share/jvmci/jvmciEnv.cpp

Lines changed: 148 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -278,35 +278,141 @@ void JVMCIEnv::describe_pending_exception(bool clear) {
278278
}
279279
}
280280

281-
void JVMCIEnv::translate_hotspot_exception_to_jni_exception(JavaThread* THREAD, const Handle& throwable) {
282-
assert(!is_hotspot(), "must_be");
283-
// Resolve HotSpotJVMCIRuntime class explicitly as HotSpotJVMCI::compute_offsets
284-
// may not have been called.
285-
Klass* runtimeKlass = SystemDictionary::resolve_or_fail(vmSymbols::jdk_vm_ci_hotspot_HotSpotJVMCIRuntime(), true, CHECK);
286-
JavaCallArguments jargs;
287-
jargs.push_oop(throwable);
288-
JavaValue result(T_OBJECT);
289-
JavaCalls::call_static(&result,
290-
runtimeKlass,
291-
vmSymbols::encodeThrowable_name(),
292-
vmSymbols::encodeThrowable_signature(), &jargs, THREAD);
293-
if (HAS_PENDING_EXCEPTION) {
294-
JVMCIRuntime::fatal_exception(this, "HotSpotJVMCIRuntime.encodeThrowable should not throw an exception");
281+
// Shared code for translating an exception from HotSpot to libjvmci or vice versa.
282+
class ExceptionTranslation: public StackObj {
283+
protected:
284+
JVMCIEnv* _from_env; // Source of translation. Can be nullptr.
285+
JVMCIEnv* _to_env; // Destination of translation. Never nullptr.
286+
287+
ExceptionTranslation(JVMCIEnv* from_env, JVMCIEnv* to_env) : _from_env(from_env), _to_env(to_env) {}
288+
289+
// Encodes the exception in `_from_env` into `buffer`.
290+
// Where N is the number of bytes needed for the encoding, returns N if N <= `buffer_size`
291+
// and the encoding was written to `buffer` otherwise returns -N.
292+
virtual int encode(JavaThread* THREAD, Klass* runtimeKlass, jlong buffer, int buffer_size) = 0;
293+
294+
// Decodes the exception in `buffer` in `_to_env` and throws it.
295+
virtual void decode(JavaThread* THREAD, Klass* runtimeKlass, jlong buffer) = 0;
296+
297+
public:
298+
void doit(JavaThread* THREAD) {
299+
// Resolve HotSpotJVMCIRuntime class explicitly as HotSpotJVMCI::compute_offsets
300+
// may not have been called.
301+
Klass* runtimeKlass = SystemDictionary::resolve_or_fail(vmSymbols::jdk_vm_ci_hotspot_HotSpotJVMCIRuntime(), true, CHECK);
302+
303+
int buffer_size = 2048;
304+
while (true) {
305+
ResourceMark rm;
306+
jlong buffer = (jlong) NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, jbyte, buffer_size);
307+
int res = encode(THREAD, runtimeKlass, buffer, buffer_size);
308+
if ((_from_env != nullptr && _from_env->has_pending_exception()) || HAS_PENDING_EXCEPTION) {
309+
JVMCIRuntime::fatal_exception(_from_env, "HotSpotJVMCIRuntime.encodeThrowable should not throw an exception");
310+
}
311+
if (res < 0) {
312+
int required_buffer_size = -res;
313+
if (required_buffer_size > buffer_size) {
314+
buffer_size = required_buffer_size;
315+
}
316+
} else {
317+
decode(THREAD, runtimeKlass, buffer);
318+
if (!_to_env->has_pending_exception()) {
319+
JVMCIRuntime::fatal_exception(_to_env, "HotSpotJVMCIRuntime.decodeAndThrowThrowable should throw an exception");
320+
}
321+
return;
322+
}
323+
}
295324
}
325+
};
296326

297-
oop encoded_throwable_string = result.get_oop();
327+
// Translates an exception on the HotSpot heap to an exception on the shared library heap.
328+
class HotSpotToSharedLibraryExceptionTranslation : public ExceptionTranslation {
329+
private:
330+
const Handle& _throwable;
298331

299-
ResourceMark rm;
300-
const char* encoded_throwable_chars = java_lang_String::as_utf8_string(encoded_throwable_string);
332+
int encode(JavaThread* THREAD, Klass* runtimeKlass, jlong buffer, int buffer_size) {
333+
JavaCallArguments jargs;
334+
jargs.push_oop(_throwable);
335+
jargs.push_long(buffer);
336+
jargs.push_int(buffer_size);
337+
JavaValue result(T_INT);
338+
JavaCalls::call_static(&result,
339+
runtimeKlass,
340+
vmSymbols::encodeThrowable_name(),
341+
vmSymbols::encodeThrowable_signature(), &jargs, THREAD);
342+
return result.get_jint();
343+
}
301344

302-
JNIAccessMark jni(this, THREAD);
303-
jobject jni_encoded_throwable_string = jni()->NewStringUTF(encoded_throwable_chars);
304-
jthrowable jni_throwable = (jthrowable) jni()->CallStaticObjectMethod(JNIJVMCI::HotSpotJVMCIRuntime::clazz(),
305-
JNIJVMCI::HotSpotJVMCIRuntime::decodeThrowable_method(),
306-
jni_encoded_throwable_string);
307-
jni()->Throw(jni_throwable);
345+
void decode(JavaThread* THREAD, Klass* runtimeKlass, jlong buffer) {
346+
JNIAccessMark jni(_to_env, THREAD);
347+
jni()->CallStaticVoidMethod(JNIJVMCI::HotSpotJVMCIRuntime::clazz(),
348+
JNIJVMCI::HotSpotJVMCIRuntime::decodeAndThrowThrowable_method(),
349+
buffer);
350+
}
351+
public:
352+
HotSpotToSharedLibraryExceptionTranslation(JVMCIEnv* hotspot_env, JVMCIEnv* jni_env, const Handle& throwable) :
353+
ExceptionTranslation(hotspot_env, jni_env), _throwable(throwable) {}
354+
};
355+
356+
// Translates an exception on the shared library heap to an exception on the HotSpot heap.
357+
class SharedLibraryToHotSpotExceptionTranslation : public ExceptionTranslation {
358+
private:
359+
jthrowable _throwable;
360+
361+
int encode(JavaThread* THREAD, Klass* runtimeKlass, jlong buffer, int buffer_size) {
362+
JNIAccessMark jni(_from_env, THREAD);
363+
return jni()->CallStaticIntMethod(JNIJVMCI::HotSpotJVMCIRuntime::clazz(),
364+
JNIJVMCI::HotSpotJVMCIRuntime::encodeThrowable_method(),
365+
_throwable, buffer, buffer_size);
366+
}
367+
368+
void decode(JavaThread* THREAD, Klass* runtimeKlass, jlong buffer) {
369+
JavaCallArguments jargs;
370+
jargs.push_long(buffer);
371+
JavaValue result(T_VOID);
372+
JavaCalls::call_static(&result,
373+
runtimeKlass,
374+
vmSymbols::decodeAndThrowThrowable_name(),
375+
vmSymbols::long_void_signature(), &jargs, THREAD);
376+
}
377+
public:
378+
SharedLibraryToHotSpotExceptionTranslation(JVMCIEnv* hotspot_env, JVMCIEnv* jni_env, jthrowable throwable) :
379+
ExceptionTranslation(jni_env, hotspot_env), _throwable(throwable) {}
380+
};
381+
382+
void JVMCIEnv::translate_to_jni_exception(JavaThread* THREAD, const Handle& throwable, JVMCIEnv* hotspot_env, JVMCIEnv* jni_env) {
383+
HotSpotToSharedLibraryExceptionTranslation(hotspot_env, jni_env, throwable).doit(THREAD);
308384
}
309385

386+
void JVMCIEnv::translate_from_jni_exception(JavaThread* THREAD, jthrowable throwable, JVMCIEnv* hotspot_env, JVMCIEnv* jni_env) {
387+
SharedLibraryToHotSpotExceptionTranslation(hotspot_env, jni_env, throwable).doit(THREAD);
388+
}
389+
390+
jboolean JVMCIEnv::transfer_pending_exception(JavaThread* THREAD, JVMCIEnv* peer_env) {
391+
if (is_hotspot()) {
392+
if (HAS_PENDING_EXCEPTION) {
393+
Handle throwable = Handle(THREAD, PENDING_EXCEPTION);
394+
CLEAR_PENDING_EXCEPTION;
395+
translate_to_jni_exception(THREAD, throwable, this, peer_env);
396+
return true;
397+
}
398+
} else {
399+
jthrowable ex = nullptr;
400+
{
401+
JNIAccessMark jni(this, THREAD);
402+
ex = jni()->ExceptionOccurred();
403+
if (ex != nullptr) {
404+
jni()->ExceptionClear();
405+
}
406+
}
407+
if (ex != nullptr) {
408+
translate_from_jni_exception(THREAD, ex, peer_env, this);
409+
return true;
410+
}
411+
}
412+
return false;
413+
}
414+
415+
310416
JVMCIEnv::~JVMCIEnv() {
311417
if (_throw_to_caller) {
312418
if (is_hotspot()) {
@@ -318,7 +424,7 @@ JVMCIEnv::~JVMCIEnv() {
318424
if (HAS_PENDING_EXCEPTION) {
319425
Handle throwable = Handle(THREAD, PENDING_EXCEPTION);
320426
CLEAR_PENDING_EXCEPTION;
321-
translate_hotspot_exception_to_jni_exception(THREAD, throwable);
427+
translate_to_jni_exception(THREAD, throwable, nullptr, this);
322428
}
323429
}
324430
}
@@ -801,6 +907,23 @@ JVMCIObject JVMCIEnv::call_HotSpotJVMCIRuntime_callToString(JVMCIObject object,
801907
}
802908
}
803909

910+
void JVMCIEnv::call_HotSpotJVMCIRuntime_postTranslation(JVMCIObject object, JVMCIEnv* JVMCIENV) {
911+
JavaThread* THREAD = JVMCI::compilation_tick(JavaThread::current()); // For exception macros.
912+
if (is_hotspot()) {
913+
JavaCallArguments jargs;
914+
jargs.push_oop(Handle(THREAD, HotSpotJVMCI::resolve(object)));
915+
JavaValue result(T_VOID);
916+
JavaCalls::call_static(&result,
917+
HotSpotJVMCI::HotSpotJVMCIRuntime::klass(),
918+
vmSymbols::postTranslation_name(),
919+
vmSymbols::object_void_signature(), &jargs, CHECK);
920+
} else {
921+
JNIAccessMark jni(this, THREAD);
922+
jni()->CallStaticVoidMethod(JNIJVMCI::HotSpotJVMCIRuntime::clazz(),
923+
JNIJVMCI::HotSpotJVMCIRuntime::postTranslation_method(),
924+
object.as_jobject());
925+
}
926+
}
804927

805928
JVMCIObject JVMCIEnv::call_JavaConstant_forPrimitive(JVMCIObject kind, jlong value, JVMCI_TRAPS) {
806929
JavaThread* THREAD = JVMCI::compilation_tick(JavaThread::current()); // For exception macros.

src/hotspot/share/jvmci/jvmciEnv.hpp

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -171,11 +171,15 @@ class JVMCIEnv : public ResourceObj {
171171
const char* _file; // The file and ...
172172
int _line; // ... line where this JNIEnv was created
173173

174-
// Translates an exception on the HotSpot heap to an exception on
175-
// the shared library heap. The translation includes the stack and
176-
// causes of `throwable`. The translated exception is pending in the
177-
// shared library thread upon returning.
178-
void translate_hotspot_exception_to_jni_exception(JavaThread* THREAD, const Handle& throwable);
174+
// Translates an exception on the HotSpot heap (i.e., hotspot_env) to an exception on
175+
// the shared library heap (i.e., jni_env). The translation includes the stack and cause(s) of `throwable`.
176+
// The translated exception is pending in jni_env upon returning.
177+
static void translate_to_jni_exception(JavaThread* THREAD, const Handle& throwable, JVMCIEnv* hotspot_env, JVMCIEnv* jni_env);
178+
179+
// Translates an exception on the shared library heap (i.e., jni_env) to an exception on
180+
// the HotSpot heap (i.e., hotspot_env). The translation includes the stack and cause(s) of `throwable`.
181+
// The translated exception is pending in hotspot_env upon returning.
182+
static void translate_from_jni_exception(JavaThread* THREAD, jthrowable throwable, JVMCIEnv* hotspot_env, JVMCIEnv* jni_env);
179183

180184
public:
181185
// Opens a JVMCIEnv scope for a Java to VM call (e.g., via CompilerToVM).
@@ -225,6 +229,11 @@ class JVMCIEnv : public ResourceObj {
225229
jboolean has_pending_exception();
226230
void clear_pending_exception();
227231

232+
// If this env has a pending exception, it is translated to be a pending
233+
// exception in `peer_env` and is cleared from this env. Returns true
234+
// if a pending exception was transferred, false otherwise.
235+
jboolean transfer_pending_exception(JavaThread* THREAD, JVMCIEnv* peer_env);
236+
228237
// Prints an exception and stack trace of a pending exception.
229238
void describe_pending_exception(bool clear);
230239

@@ -311,6 +320,8 @@ class JVMCIEnv : public ResourceObj {
311320

312321
jboolean call_HotSpotJVMCIRuntime_isGCSupported(JVMCIObject runtime, jint gcIdentifier);
313322

323+
void call_HotSpotJVMCIRuntime_postTranslation(JVMCIObject object, JVMCI_TRAPS);
324+
314325
BasicType kindToBasicType(JVMCIObject kind, JVMCI_TRAPS);
315326

316327
#define DO_THROW(name) \

src/hotspot/share/jvmci/jvmciJavaClasses.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it

src/hotspot/share/jvmci/jvmciJavaClasses.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -349,13 +349,14 @@
349349
objectarray_field(HotSpotJVMCIRuntime, excludeFromJVMCICompilation, "[Ljava/lang/Module;") \
350350
jvmci_method(CallNonvirtualObjectMethod, GetMethodID, call_special, JVMCIObject, HotSpotJVMCIRuntime, compileMethod, compileMethod_signature, (JVMCIObject runtime, JVMCIObject method, int entry_bci, jlong env, int id)) \
351351
jvmci_method(CallNonvirtualObjectMethod, GetMethodID, call_special, JVMCIObject, HotSpotJVMCIRuntime, isGCSupported, int_bool_signature, (JVMCIObject runtime, int gcIdentifier)) \
352-
jvmci_method(CallStaticObjectMethod, GetStaticMethodID, call_static, JVMCIObject, HotSpotJVMCIRuntime, encodeThrowable, encodeThrowable_signature, (JVMCIObject throwable)) \
353-
jvmci_method(CallStaticObjectMethod, GetStaticMethodID, call_static, JVMCIObject, HotSpotJVMCIRuntime, decodeThrowable, decodeThrowable_signature, (JVMCIObject encodedThrowable)) \
352+
jvmci_method(CallStaticBooleanMethod, GetStaticMethodID, call_static, bool, HotSpotJVMCIRuntime, encodeThrowable, encodeThrowable_signature, (JVMCIObject throwable, jlong buffer, int buffer_size)) \
353+
jvmci_method(CallStaticVoidMethod, GetStaticMethodID, call_static, void, HotSpotJVMCIRuntime, decodeAndThrowThrowable, long_void_signature, (jlong buffer)) \
354354
jvmci_method(CallNonvirtualVoidMethod, GetMethodID, call_special, void, HotSpotJVMCIRuntime, bootstrapFinished, void_method_signature, (JVMCIObject runtime, JVMCI_TRAPS)) \
355355
jvmci_method(CallNonvirtualVoidMethod, GetMethodID, call_special, void, HotSpotJVMCIRuntime, shutdown, void_method_signature, (JVMCIObject runtime)) \
356356
jvmci_method(CallStaticObjectMethod, GetStaticMethodID, call_static, JVMCIObject, HotSpotJVMCIRuntime, runtime, runtime_signature, (JVMCI_TRAPS)) \
357357
jvmci_method(CallObjectMethod, GetMethodID, call_virtual, JVMCIObject, HotSpotJVMCIRuntime, getCompiler, getCompiler_signature, (JVMCIObject runtime, JVMCI_TRAPS)) \
358358
jvmci_method(CallStaticObjectMethod, GetStaticMethodID, call_static, JVMCIObject, HotSpotJVMCIRuntime, callToString, callToString_signature, (JVMCIObject object, JVMCI_TRAPS)) \
359+
jvmci_method(CallStaticVoidMethod, GetStaticMethodID, call_static, void, HotSpotJVMCIRuntime, postTranslation, object_void_signature, (JVMCIObject object, JVMCI_TRAPS)) \
359360
end_class \
360361
start_class(JVMCIError, jdk_vm_ci_common_JVMCIError) \
361362
jvmci_constructor(JVMCIError, "(Ljava/lang/String;)V") \

0 commit comments

Comments
 (0)