Skip to content

Commit e58d830

Browse files
committed
[GR-70612] Propagate exceptions faithfully from HotSpot to libgraal when thrown in a canCallJava scope
PullRequest: labsjdk-ce/229
2 parents b345181 + d9dac73 commit e58d830

File tree

10 files changed

+116
-96
lines changed

10 files changed

+116
-96
lines changed

src/hotspot/share/jvmci/jvmci.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,17 @@ CompilerThread* CompilerThreadCanCallJava::update(JavaThread* current, bool new_
6969
return nullptr;
7070
}
7171

72-
CompilerThreadCanCallJava::CompilerThreadCanCallJava(JavaThread* current, bool new_state) {
72+
CompilerThreadCanCallJava::CompilerThreadCanCallJava(JavaThread* current, bool new_state, JVMCIEnv* env) {
7373
_current = CompilerThreadCanCallJava::update(current, new_state);
74+
_env = env;
7475
}
7576

7677
CompilerThreadCanCallJava::~CompilerThreadCanCallJava() {
7778
if (_current != nullptr) {
79+
if (_current->_can_call_java && _env != nullptr && !_env->is_hotspot() && _current->has_pending_exception()) {
80+
// Convert pending HotSpot exception while still inside scope that can call Java
81+
JVMCIEnv::transfer_pending_exception_to_jni(_current, nullptr, _env);
82+
}
7883
_current->_can_call_java = !_current->_can_call_java;
7984
}
8085
}
@@ -209,7 +214,7 @@ void JVMCI::ensure_box_caches_initialized(TRAPS) {
209214

210215
// Class resolution and initialization below
211216
// requires calling into Java
212-
CompilerThreadCanCallJava ccj(THREAD, true);
217+
CompilerThreadCanCallJava ccj(THREAD, true, nullptr);
213218

214219
for (unsigned i = 0; i < sizeof(box_classes) / sizeof(Symbol*); i++) {
215220
Klass* k = SystemDictionary::resolve_or_fail(box_classes[i], true, CHECK);

src/hotspot/share/jvmci/jvmci.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,15 @@ typedef struct _jmetadata *jmetadata;
6060
class CompilerThreadCanCallJava : StackObj {
6161
private:
6262
CompilerThread* _current; // Only non-null if state of thread changed
63+
JVMCIEnv* _env; // For translating an exception from HotSpot to JNI
6364
public:
6465
// If the current thread is a CompilerThread associated with
6566
// a JVMCI compiler where CompilerThread::_can_call_java != new_state,
6667
// then _can_call_java is set to `new_state`
6768
// Returns nullptr if no change was made, otherwise the current CompilerThread
6869
static CompilerThread* update(JavaThread* current, bool new_state);
6970

70-
CompilerThreadCanCallJava(JavaThread* current, bool new_state);
71+
CompilerThreadCanCallJava(JavaThread* current, bool new_state, JVMCIEnv* env);
7172

7273
// Resets CompilerThread::_can_call_java of the current thread if the
7374
// constructor changed it.

src/hotspot/share/jvmci/jvmciCompilerToVM.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ Handle JavaArgumentUnboxer::next_arg(BasicType expectedType) {
186186
ResourceMark rm; \
187187
bool __is_hotspot = env == thread->jni_environment(); \
188188
bool __block_can_call_java = __is_hotspot || !thread->is_Compiler_thread() || CompilerThread::cast(thread)->can_call_java(); \
189-
CompilerThreadCanCallJava ccj(thread, __block_can_call_java); \
189+
CompilerThreadCanCallJava ccj(thread, __block_can_call_java, nullptr); \
190190
JVMCIENV_FROM_JNI(JVMCI::compilation_tick(thread), env); \
191191

192192
// Entry to native method implementation that transitions
@@ -594,7 +594,7 @@ C2V_VMENTRY_0(jboolean, shouldInlineMethod,(JNIEnv* env, jobject, ARGUMENT_PAIR(
594594
C2V_END
595595

596596
C2V_VMENTRY_NULL(jobject, lookupType, (JNIEnv* env, jobject, jstring jname, ARGUMENT_PAIR(accessing_klass), jint accessing_klass_loader, jboolean resolve))
597-
CompilerThreadCanCallJava canCallJava(thread, resolve); // Resolution requires Java calls
597+
CompilerThreadCanCallJava canCallJava(thread, resolve, JVMCIENV); // Resolution requires Java calls
598598
JVMCIObject name = JVMCIENV->wrap(jname);
599599
const char* str = JVMCIENV->as_utf8_string(name);
600600
TempNewSymbol class_name = SymbolTable::new_symbol(str);
@@ -1390,7 +1390,7 @@ C2V_VMENTRY(void, reprofile, (JNIEnv* env, jobject, ARGUMENT_PAIR(method)))
13901390
if (method_data == nullptr) {
13911391
method_data = get_profiling_method_data(method, CHECK);
13921392
} else {
1393-
CompilerThreadCanCallJava canCallJava(THREAD, true);
1393+
CompilerThreadCanCallJava canCallJava(THREAD, true, JVMCIENV);
13941394
method_data->reinitialize();
13951395
}
13961396
C2V_END
@@ -2097,7 +2097,7 @@ C2V_VMENTRY(void, ensureInitialized, (JNIEnv* env, jobject, ARGUMENT_PAIR(klass)
20972097
C2V_END
20982098

20992099
C2V_VMENTRY(void, ensureLinked, (JNIEnv* env, jobject, ARGUMENT_PAIR(klass)))
2100-
CompilerThreadCanCallJava canCallJava(thread, true); // Linking requires Java calls
2100+
CompilerThreadCanCallJava canCallJava(thread, true, JVMCIENV); // Linking requires Java calls
21012101
Klass* klass = UNPACK_PAIR(Klass, klass);
21022102
if (klass == nullptr) {
21032103
JVMCI_THROW(NullPointerException);
@@ -2908,7 +2908,7 @@ C2V_VMENTRY_0(jlong, translate, (JNIEnv* env, jobject, jobject obj_handle, jbool
29082908
return 0L;
29092909
}
29102910
PEER_JVMCIENV_FROM_THREAD(THREAD, !JVMCIENV->is_hotspot());
2911-
CompilerThreadCanCallJava canCallJava(thread, PEER_JVMCIENV->is_hotspot());
2911+
CompilerThreadCanCallJava canCallJava(thread, PEER_JVMCIENV->is_hotspot(), nullptr);
29122912
PEER_JVMCIENV->check_init(JVMCI_CHECK_0);
29132913

29142914
JVMCIEnv* thisEnv = JVMCIENV;
@@ -3221,7 +3221,7 @@ C2V_VMENTRY(void, callSystemExit, (JNIEnv* env, jobject, jint status))
32213221
vm_exit_during_initialization();
32223222
}
32233223
}
3224-
CompilerThreadCanCallJava canCallJava(thread, true);
3224+
CompilerThreadCanCallJava canCallJava(thread, true, JVMCIENV);
32253225
JavaValue result(T_VOID);
32263226
JavaCallArguments jargs(1);
32273227
jargs.push_int(status);

src/hotspot/share/jvmci/jvmciEnv.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -926,7 +926,6 @@ DO_THROW(InvalidInstalledCodeException)
926926
DO_THROW(UnsatisfiedLinkError)
927927
DO_THROW(UnsupportedOperationException)
928928
DO_THROW(OutOfMemoryError)
929-
DO_THROW(NoClassDefFoundError)
930929

931930
#undef DO_THROW
932931

@@ -1370,7 +1369,7 @@ JVMCIObject JVMCIEnv::get_jvmci_type(const JVMCIKlassHandle& klass, JVMCI_TRAPS)
13701369
JavaThread* THREAD = JVMCI::compilation_tick(JavaThread::current()); // For exception macros.
13711370
jboolean exception = false;
13721371
if (is_hotspot()) {
1373-
CompilerThreadCanCallJava ccj(THREAD, true);
1372+
CompilerThreadCanCallJava ccj(THREAD, true, JVMCIENV);
13741373
JavaValue result(T_OBJECT);
13751374
JavaCallArguments args;
13761375
args.push_long(pointer);

src/hotspot/share/jvmci/jvmciEnv.hpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,6 @@ class JVMCIEnv : public ResourceObj {
381381
DO_THROW(UnsatisfiedLinkError)
382382
DO_THROW(UnsupportedOperationException)
383383
DO_THROW(OutOfMemoryError)
384-
DO_THROW(NoClassDefFoundError)
385384

386385
#undef DO_THROW
387386

src/hotspot/share/jvmci/jvmciJavaClasses.cpp

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -408,43 +408,6 @@ extern "C" {
408408
jlong JNICALL JVM_ReadSystemPropertiesInfo(JNIEnv *env, jclass c, jintArray offsets_handle);
409409
}
410410

411-
// Dumps symbols for public <init>() and <init>(String) methods of
412-
// non-abstract Throwable subtypes known by the VM. This is to
413-
// support the use of reflection in jdk.vm.ci.hotspot.TranslatedException.create().
414-
class ThrowableInitDumper : public SymbolClosure {
415-
private:
416-
fileStream* _st;
417-
public:
418-
ThrowableInitDumper(fileStream* st) { _st = st; }
419-
void do_symbol(Symbol** p) {
420-
JavaThread* THREAD = JavaThread::current(); // For exception macros.
421-
Symbol* name = *p;
422-
if (name == nullptr) {
423-
return;
424-
}
425-
Klass* k = SystemDictionary::resolve_or_null(name, CHECK_EXIT);
426-
if (k != nullptr && k->is_instance_klass()) {
427-
InstanceKlass* iklass = InstanceKlass::cast(k);
428-
if (iklass->is_subclass_of(vmClasses::Throwable_klass()) && iklass->is_public() && !iklass->is_abstract()) {
429-
const char* class_name = nullptr;
430-
Array<Method*>* methods = iklass->methods();
431-
for (int i = 0; i < methods->length(); i++) {
432-
Method* m = methods->at(i);
433-
if (m->name() == vmSymbols::object_initializer_name() &&
434-
m->is_public() &&
435-
(m->signature() == vmSymbols::void_method_signature() || m->signature() == vmSymbols::string_void_signature())) {
436-
if (class_name == nullptr) {
437-
class_name = name->as_C_string();
438-
_st->print_cr("class %s", class_name);
439-
}
440-
_st->print_cr("method %s %s %s", class_name, m->name()->as_C_string(), m->signature()->as_C_string());
441-
}
442-
}
443-
}
444-
}
445-
}
446-
};
447-
448411
#define IN_CLASS(fullClassName) current_class_name = vmSymbols::fullClassName()->as_C_string()
449412
/**
450413
* Initializes the JNI method and field ids used in JNIJVMCI.
@@ -530,8 +493,6 @@ void JNIJVMCI::initialize_ids(JNIEnv* env) {
530493
fileStream* st = JVMCIGlobals::get_jni_config_file();
531494

532495
DUMP_ALL_NATIVE_METHODS(vmSymbols::jdk_vm_ci_hotspot_CompilerToVM());
533-
ThrowableInitDumper dumper(st);
534-
vmSymbols::symbols_do(&dumper);
535496

536497
st->flush();
537498
tty->print_cr("Dumped JVMCI shared library JNI configuration to %s", JVMCILibDumpJNIConfig);

src/hotspot/share/jvmci/jvmciJavaClasses.hpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -262,9 +262,6 @@
262262
start_class(OutOfMemoryError, java_lang_OutOfMemoryError) \
263263
jvmci_constructor(OutOfMemoryError, "(Ljava/lang/String;)V") \
264264
end_class \
265-
start_class(NoClassDefFoundError, java_lang_NoClassDefFoundError) \
266-
jvmci_constructor(NoClassDefFoundError, "(Ljava/lang/String;)V") \
267-
end_class \
268265
start_class(InvalidInstalledCodeException, jdk_vm_ci_code_InvalidInstalledCodeException) \
269266
jvmci_constructor(InvalidInstalledCodeException, "(Ljava/lang/String;)V") \
270267
end_class \

src/java.base/share/classes/jdk/internal/vm/TranslatedException.java

Lines changed: 82 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
import java.io.DataInputStream;
3232
import java.io.DataOutputStream;
3333
import java.io.IOException;
34-
import java.lang.reflect.Constructor;
3534
import java.lang.reflect.InvocationTargetException;
3635
import java.util.ArrayList;
3736
import java.util.Arrays;
@@ -42,14 +41,20 @@
4241

4342
/**
4443
* Support for translating exceptions between the HotSpot heap and libjvmci heap.
45-
*
46-
* Successfully translated exceptions are wrapped in a TranslatedException instance.
47-
* This allows callers to distiguish between a translated exception and an error
44+
* <p>
45+
* Apart from {@link OutOfMemoryError}s, successfully translated exceptions have
46+
* {@link #TRANSLATED_MARKER} as the first element in their stack trace. This
47+
* allows a caller to distinguish between a translated exception and an error
4848
* that arose during translation.
4949
*/
5050
@SuppressWarnings("serial")
5151
public final class TranslatedException extends Exception {
5252

53+
/**
54+
* Marker frame prepended to outermost stack of a translated exception.
55+
*/
56+
public static final StackTraceElement TRANSLATED_MARKER = new StackTraceElement(TranslatedException.class.getName(), "translated", null, -2);
57+
5358
/**
5459
* The value returned by {@link #encodeThrowable(Throwable)} when encoding
5560
* fails due to an {@link OutOfMemoryError}.
@@ -114,7 +119,7 @@ public Throwable fillInStackTrace() {
114119
private static void debugPrintStackTrace(Throwable throwable, boolean debug) {
115120
if (debug) {
116121
System.err.print("DEBUG: ");
117-
throwable.printStackTrace();
122+
throwable.printStackTrace(System.err);
118123
}
119124
}
120125

@@ -130,26 +135,71 @@ private static Throwable initCause(Throwable throwable, Throwable cause, boolean
130135
return throwable;
131136
}
132137

138+
/**
139+
* Creates an exception if {@code className} is one of the supported
140+
* core exceptions for translation.
141+
*
142+
* @param className class name of exception to create
143+
* @param message the detailed message for the exception
144+
* @return {@code null} if {@code className} is unsupported
145+
*/
146+
private static Throwable newThrowable(String className, String message) {
147+
return switch (className) {
148+
// Exceptions
149+
case "java.lang.ArithmeticException" -> new ArithmeticException(message);
150+
case "java.lang.ArrayIndexOutOfBoundsException" -> new ArrayIndexOutOfBoundsException(message);
151+
case "java.lang.ArrayStoreException" -> new ArrayStoreException(message);
152+
case "java.lang.ClassCastException" -> new ClassCastException(message);
153+
case "java.lang.ClassNotFoundException" -> new ClassNotFoundException(message);
154+
case "java.lang.CloneNotSupportedException" -> new CloneNotSupportedException(message);
155+
case "java.lang.IllegalAccessException" -> new IllegalAccessException(message);
156+
case "java.lang.IllegalArgumentException" -> new IllegalArgumentException(message);
157+
case "java.lang.IndexOutOfBoundsException" -> new IndexOutOfBoundsException(message);
158+
case "java.lang.InstantiationException" -> new InstantiationException(message);
159+
case "java.lang.NegativeArraySizeException" -> new NegativeArraySizeException(message);
160+
case "java.lang.NoSuchFieldException" -> new NoSuchFieldException(message);
161+
case "java.lang.NoSuchMethodException" -> new NoSuchMethodException(message);
162+
case "java.lang.NullPointerException" -> new NullPointerException(message);
163+
case "java.lang.RuntimeException" -> new RuntimeException(message);
164+
case "java.lang.StringIndexOutOfBoundsException" -> new StringIndexOutOfBoundsException(message);
165+
case "java.lang.UnsupportedOperationException" -> new UnsupportedOperationException(message);
166+
167+
// Errors
168+
case "java.lang.AbstractMethodError" -> new AbstractMethodError(message);
169+
case "java.lang.BootstrapMethodError" -> new BootstrapMethodError(message);
170+
case "java.lang.ClassCircularityError" -> new ClassCircularityError(message);
171+
case "java.lang.ClassFormatError" -> new ClassFormatError(message);
172+
case "java.lang.IllegalAccessError" -> new IllegalAccessError(message);
173+
case "java.lang.IncompatibleClassChangeError" -> new IncompatibleClassChangeError(message);
174+
case "java.lang.InstantiationError" -> new InstantiationError(message);
175+
case "java.lang.InternalError" -> new InternalError(message);
176+
case "java.lang.LinkageError" -> new LinkageError(message);
177+
case "java.lang.NoClassDefFoundError" -> new NoClassDefFoundError(message);
178+
case "java.lang.NoSuchFieldError" -> new NoSuchFieldError(message);
179+
case "java.lang.NoSuchMethodError" -> new NoSuchMethodError(message);
180+
case "java.lang.OutOfMemoryError" -> new OutOfMemoryError(message);
181+
case "java.lang.StackOverflowError" -> new StackOverflowError(message);
182+
case "java.lang.UnsatisfiedLinkError" -> new UnsatisfiedLinkError(message);
183+
default -> null;
184+
};
185+
}
186+
133187
private static Throwable create(String className, String message, Throwable cause, boolean debug) {
134-
// Try create with reflection first.
135188
try {
136-
Class<?> cls = Class.forName(className);
137-
if (cause != null) {
138-
// Handle known exception types whose cause must
139-
// be set in the constructor
140-
if (cls == InvocationTargetException.class) {
141-
return new InvocationTargetException(cause, message);
142-
}
143-
if (cls == ExceptionInInitializerError.class) {
144-
return new ExceptionInInitializerError(cause);
145-
}
189+
if (className.equals(InvocationTargetException.class.getName())) {
190+
return new InvocationTargetException(cause, message);
146191
}
147-
if (message == null) {
148-
Constructor<?> cons = cls.getConstructor();
149-
return initCause((Throwable) cons.newInstance(), cause, debug);
192+
if (className.equals(ExceptionInInitializerError.class.getName())) {
193+
return new ExceptionInInitializerError(cause);
150194
}
151-
Constructor<?> cons = cls.getDeclaredConstructor(String.class);
152-
return initCause((Throwable) cons.newInstance(message), cause, debug);
195+
if (className.equals(AssertionError.class.getName())) {
196+
return initCause(new AssertionError(cause), cause,debug);
197+
}
198+
Throwable throwable = newThrowable(className, message);
199+
if (throwable != null) {
200+
return initCause(throwable, cause,debug);
201+
}
202+
return initCause(translationFailure("%s [%s]", message, className), cause, debug);
153203
} catch (Throwable translationFailure) {
154204
debugPrintStackTrace(translationFailure, debug);
155205
return initCause(translationFailure("%s [%s]", message, className), cause, debug);
@@ -190,7 +240,7 @@ private static byte[] encodeThrowable(Throwable throwable,
190240
}
191241
}
192242

193-
// Encode from inner most cause outwards
243+
// Encode from innermost cause outwards
194244
Collections.reverse(throwables);
195245

196246
for (Throwable current : throwables) {
@@ -201,8 +251,7 @@ private static byte[] encodeThrowable(Throwable throwable,
201251
stackTrace = new StackTraceElement[0];
202252
}
203253
dos.writeInt(stackTrace.length);
204-
for (int i = 0; i < stackTrace.length; i++) {
205-
StackTraceElement frame = stackTrace[i];
254+
for (StackTraceElement frame : stackTrace) {
206255
if (frame != null) {
207256
dos.writeUTF(emptyIfNull(frame.getClassLoaderName()));
208257
dos.writeUTF(emptyIfNull(frame.getModuleName()));
@@ -294,13 +343,20 @@ static Throwable decodeThrowable(byte[] encodedThrowable, boolean debug) {
294343
// Remove null entries at end of stackTrace
295344
stackTrace = Arrays.copyOf(stackTrace, stackTraceIndex);
296345
}
346+
if (dis.available() == 0) {
347+
// Prepend the marker frame to the outermost stack trace
348+
StackTraceElement[] newStackTrace = new StackTraceElement[stackTrace.length + 1];
349+
System.arraycopy(stackTrace, 0, newStackTrace, 1, stackTrace.length);
350+
newStackTrace[0] = TRANSLATED_MARKER;
351+
stackTrace = newStackTrace;
352+
}
297353
throwable.setStackTrace(stackTrace);
298354
cause = throwable;
299355
}
300-
return new TranslatedException(throwable);
356+
return throwable;
301357
} catch (Throwable translationFailure) {
302358
debugPrintStackTrace(translationFailure, debug);
303-
return translationFailure("error decoding exception: %s", encodedThrowable);
359+
return translationFailure("error decoding exception: %s", Arrays.toString(encodedThrowable));
304360
}
305361
}
306362
}

src/java.base/share/classes/jdk/internal/vm/VMSupport.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ public static byte[] serializeAgentPropertiesToByteArray() throws IOException {
116116
public static void decodeAndThrowThrowable(int format, long buffer, boolean inJVMHeap, boolean debug) throws Throwable {
117117
if (format != 0) {
118118
if (format == 4) {
119-
throw new TranslatedException(new OutOfMemoryError("in VM code and current thread cannot call Java"));
119+
throw new OutOfMemoryError("in VM code and current thread cannot call Java");
120120
}
121121
String context = String.format("while encoding an exception to translate it %s the JVM heap",
122122
inJVMHeap ? "to" : "from");

0 commit comments

Comments
 (0)