8279437: [JVMCI] exception in HotSpotJVMCIRuntime.translate can exit the VM
Reviewed-by: kvn
This commit is contained in:
parent
77757ba974
commit
e14fb4f4aa
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2022, 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
|
||||
@ -2376,7 +2376,7 @@ C2V_VMENTRY_PREFIX(void, detachCurrentThread, (JNIEnv* env, jobject c2vm))
|
||||
}
|
||||
C2V_END
|
||||
|
||||
C2V_VMENTRY_0(jlong, translate, (JNIEnv* env, jobject, jobject obj_handle))
|
||||
C2V_VMENTRY_0(jlong, translate, (JNIEnv* env, jobject, jobject obj_handle, jboolean callPostTranslation))
|
||||
requireJVMCINativeLibrary(JVMCI_CHECK_0);
|
||||
if (obj_handle == NULL) {
|
||||
return 0L;
|
||||
@ -2427,7 +2427,9 @@ C2V_VMENTRY_0(jlong, translate, (JNIEnv* env, jobject, jobject obj_handle))
|
||||
const char* cstring = name_string.is_null() ? NULL : thisEnv->as_utf8_string(name_string);
|
||||
// Create a new HotSpotNmethod instance in the peer runtime
|
||||
result = peerEnv->new_HotSpotNmethod(mh, cstring, isDefault, compileIdSnapshot, JVMCI_CHECK_0);
|
||||
if (nm == NULL) {
|
||||
if (result.is_null()) {
|
||||
// exception occurred (e.g. OOME) creating a new HotSpotNmethod
|
||||
} else if (nm == NULL) {
|
||||
// nmethod must have been unloaded
|
||||
} else {
|
||||
// Link the new HotSpotNmethod to the nmethod
|
||||
@ -2450,6 +2452,13 @@ C2V_VMENTRY_0(jlong, translate, (JNIEnv* env, jobject, jobject obj_handle))
|
||||
JVMCI_THROW_MSG_0(IllegalArgumentException,
|
||||
err_msg("Cannot translate object of type: %s", thisEnv->klass_name(obj)));
|
||||
}
|
||||
if (callPostTranslation) {
|
||||
peerEnv->call_HotSpotJVMCIRuntime_postTranslation(result, JVMCI_CHECK_0);
|
||||
}
|
||||
// Propagate any exception that occurred while creating the translated object
|
||||
if (peerEnv->transfer_pending_exception(thread, thisEnv)) {
|
||||
return 0L;
|
||||
}
|
||||
return (jlong) peerEnv->make_global(result).as_jobject();
|
||||
}
|
||||
|
||||
@ -2790,7 +2799,7 @@ JNINativeMethod CompilerToVM::methods[] = {
|
||||
{CC "getCurrentJavaThread", CC "()J", FN_PTR(getCurrentJavaThread)},
|
||||
{CC "attachCurrentThread", CC "([BZ)Z", FN_PTR(attachCurrentThread)},
|
||||
{CC "detachCurrentThread", CC "()V", FN_PTR(detachCurrentThread)},
|
||||
{CC "translate", CC "(" OBJECT ")J", FN_PTR(translate)},
|
||||
{CC "translate", CC "(" OBJECT "Z)J", FN_PTR(translate)},
|
||||
{CC "unhand", CC "(J)" OBJECT, FN_PTR(unhand)},
|
||||
{CC "updateHotSpotNmethod", CC "(" HS_NMETHOD ")V", FN_PTR(updateHotSpotNmethod)},
|
||||
{CC "getCode", CC "(" HS_INSTALLED_CODE ")[B", FN_PTR(getCode)},
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2022, 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
|
||||
@ -278,35 +278,141 @@ void JVMCIEnv::describe_pending_exception(bool clear) {
|
||||
}
|
||||
}
|
||||
|
||||
void JVMCIEnv::translate_hotspot_exception_to_jni_exception(JavaThread* THREAD, const Handle& throwable) {
|
||||
assert(!is_hotspot(), "must_be");
|
||||
// Resolve HotSpotJVMCIRuntime class explicitly as HotSpotJVMCI::compute_offsets
|
||||
// may not have been called.
|
||||
Klass* runtimeKlass = SystemDictionary::resolve_or_fail(vmSymbols::jdk_vm_ci_hotspot_HotSpotJVMCIRuntime(), true, CHECK);
|
||||
JavaCallArguments jargs;
|
||||
jargs.push_oop(throwable);
|
||||
JavaValue result(T_OBJECT);
|
||||
JavaCalls::call_static(&result,
|
||||
runtimeKlass,
|
||||
vmSymbols::encodeThrowable_name(),
|
||||
vmSymbols::encodeThrowable_signature(), &jargs, THREAD);
|
||||
if (HAS_PENDING_EXCEPTION) {
|
||||
JVMCIRuntime::fatal_exception(this, "HotSpotJVMCIRuntime.encodeThrowable should not throw an exception");
|
||||
// Shared code for translating an exception from HotSpot to libjvmci or vice versa.
|
||||
class ExceptionTranslation: public StackObj {
|
||||
protected:
|
||||
JVMCIEnv* _from_env; // Source of translation. Can be nullptr.
|
||||
JVMCIEnv* _to_env; // Destination of translation. Never nullptr.
|
||||
|
||||
ExceptionTranslation(JVMCIEnv* from_env, JVMCIEnv* to_env) : _from_env(from_env), _to_env(to_env) {}
|
||||
|
||||
// Encodes the exception in `_from_env` into `buffer`.
|
||||
// Where N is the number of bytes needed for the encoding, returns N if N <= `buffer_size`
|
||||
// and the encoding was written to `buffer` otherwise returns -N.
|
||||
virtual int encode(JavaThread* THREAD, Klass* runtimeKlass, jlong buffer, int buffer_size) = 0;
|
||||
|
||||
// Decodes the exception in `buffer` in `_to_env` and throws it.
|
||||
virtual void decode(JavaThread* THREAD, Klass* runtimeKlass, jlong buffer) = 0;
|
||||
|
||||
public:
|
||||
void doit(JavaThread* THREAD) {
|
||||
// Resolve HotSpotJVMCIRuntime class explicitly as HotSpotJVMCI::compute_offsets
|
||||
// may not have been called.
|
||||
Klass* runtimeKlass = SystemDictionary::resolve_or_fail(vmSymbols::jdk_vm_ci_hotspot_HotSpotJVMCIRuntime(), true, CHECK);
|
||||
|
||||
int buffer_size = 2048;
|
||||
while (true) {
|
||||
ResourceMark rm;
|
||||
jlong buffer = (jlong) NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, jbyte, buffer_size);
|
||||
int res = encode(THREAD, runtimeKlass, buffer, buffer_size);
|
||||
if ((_from_env != nullptr && _from_env->has_pending_exception()) || HAS_PENDING_EXCEPTION) {
|
||||
JVMCIRuntime::fatal_exception(_from_env, "HotSpotJVMCIRuntime.encodeThrowable should not throw an exception");
|
||||
}
|
||||
if (res < 0) {
|
||||
int required_buffer_size = -res;
|
||||
if (required_buffer_size > buffer_size) {
|
||||
buffer_size = required_buffer_size;
|
||||
}
|
||||
} else {
|
||||
decode(THREAD, runtimeKlass, buffer);
|
||||
if (!_to_env->has_pending_exception()) {
|
||||
JVMCIRuntime::fatal_exception(_to_env, "HotSpotJVMCIRuntime.decodeAndThrowThrowable should throw an exception");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Translates an exception on the HotSpot heap to an exception on the shared library heap.
|
||||
class HotSpotToSharedLibraryExceptionTranslation : public ExceptionTranslation {
|
||||
private:
|
||||
const Handle& _throwable;
|
||||
|
||||
int encode(JavaThread* THREAD, Klass* runtimeKlass, jlong buffer, int buffer_size) {
|
||||
JavaCallArguments jargs;
|
||||
jargs.push_oop(_throwable);
|
||||
jargs.push_long(buffer);
|
||||
jargs.push_int(buffer_size);
|
||||
JavaValue result(T_INT);
|
||||
JavaCalls::call_static(&result,
|
||||
runtimeKlass,
|
||||
vmSymbols::encodeThrowable_name(),
|
||||
vmSymbols::encodeThrowable_signature(), &jargs, THREAD);
|
||||
return result.get_jint();
|
||||
}
|
||||
|
||||
oop encoded_throwable_string = result.get_oop();
|
||||
void decode(JavaThread* THREAD, Klass* runtimeKlass, jlong buffer) {
|
||||
JNIAccessMark jni(_to_env, THREAD);
|
||||
jni()->CallStaticVoidMethod(JNIJVMCI::HotSpotJVMCIRuntime::clazz(),
|
||||
JNIJVMCI::HotSpotJVMCIRuntime::decodeAndThrowThrowable_method(),
|
||||
buffer);
|
||||
}
|
||||
public:
|
||||
HotSpotToSharedLibraryExceptionTranslation(JVMCIEnv* hotspot_env, JVMCIEnv* jni_env, const Handle& throwable) :
|
||||
ExceptionTranslation(hotspot_env, jni_env), _throwable(throwable) {}
|
||||
};
|
||||
|
||||
ResourceMark rm;
|
||||
const char* encoded_throwable_chars = java_lang_String::as_utf8_string(encoded_throwable_string);
|
||||
// Translates an exception on the shared library heap to an exception on the HotSpot heap.
|
||||
class SharedLibraryToHotSpotExceptionTranslation : public ExceptionTranslation {
|
||||
private:
|
||||
jthrowable _throwable;
|
||||
|
||||
JNIAccessMark jni(this, THREAD);
|
||||
jobject jni_encoded_throwable_string = jni()->NewStringUTF(encoded_throwable_chars);
|
||||
jthrowable jni_throwable = (jthrowable) jni()->CallStaticObjectMethod(JNIJVMCI::HotSpotJVMCIRuntime::clazz(),
|
||||
JNIJVMCI::HotSpotJVMCIRuntime::decodeThrowable_method(),
|
||||
jni_encoded_throwable_string);
|
||||
jni()->Throw(jni_throwable);
|
||||
int encode(JavaThread* THREAD, Klass* runtimeKlass, jlong buffer, int buffer_size) {
|
||||
JNIAccessMark jni(_from_env, THREAD);
|
||||
return jni()->CallStaticIntMethod(JNIJVMCI::HotSpotJVMCIRuntime::clazz(),
|
||||
JNIJVMCI::HotSpotJVMCIRuntime::encodeThrowable_method(),
|
||||
_throwable, buffer, buffer_size);
|
||||
}
|
||||
|
||||
void decode(JavaThread* THREAD, Klass* runtimeKlass, jlong buffer) {
|
||||
JavaCallArguments jargs;
|
||||
jargs.push_long(buffer);
|
||||
JavaValue result(T_VOID);
|
||||
JavaCalls::call_static(&result,
|
||||
runtimeKlass,
|
||||
vmSymbols::decodeAndThrowThrowable_name(),
|
||||
vmSymbols::long_void_signature(), &jargs, THREAD);
|
||||
}
|
||||
public:
|
||||
SharedLibraryToHotSpotExceptionTranslation(JVMCIEnv* hotspot_env, JVMCIEnv* jni_env, jthrowable throwable) :
|
||||
ExceptionTranslation(jni_env, hotspot_env), _throwable(throwable) {}
|
||||
};
|
||||
|
||||
void JVMCIEnv::translate_to_jni_exception(JavaThread* THREAD, const Handle& throwable, JVMCIEnv* hotspot_env, JVMCIEnv* jni_env) {
|
||||
HotSpotToSharedLibraryExceptionTranslation(hotspot_env, jni_env, throwable).doit(THREAD);
|
||||
}
|
||||
|
||||
void JVMCIEnv::translate_from_jni_exception(JavaThread* THREAD, jthrowable throwable, JVMCIEnv* hotspot_env, JVMCIEnv* jni_env) {
|
||||
SharedLibraryToHotSpotExceptionTranslation(hotspot_env, jni_env, throwable).doit(THREAD);
|
||||
}
|
||||
|
||||
jboolean JVMCIEnv::transfer_pending_exception(JavaThread* THREAD, JVMCIEnv* peer_env) {
|
||||
if (is_hotspot()) {
|
||||
if (HAS_PENDING_EXCEPTION) {
|
||||
Handle throwable = Handle(THREAD, PENDING_EXCEPTION);
|
||||
CLEAR_PENDING_EXCEPTION;
|
||||
translate_to_jni_exception(THREAD, throwable, this, peer_env);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
jthrowable ex = nullptr;
|
||||
{
|
||||
JNIAccessMark jni(this, THREAD);
|
||||
ex = jni()->ExceptionOccurred();
|
||||
if (ex != nullptr) {
|
||||
jni()->ExceptionClear();
|
||||
}
|
||||
}
|
||||
if (ex != nullptr) {
|
||||
translate_from_jni_exception(THREAD, ex, peer_env, this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
JVMCIEnv::~JVMCIEnv() {
|
||||
if (_throw_to_caller) {
|
||||
if (is_hotspot()) {
|
||||
@ -318,7 +424,7 @@ JVMCIEnv::~JVMCIEnv() {
|
||||
if (HAS_PENDING_EXCEPTION) {
|
||||
Handle throwable = Handle(THREAD, PENDING_EXCEPTION);
|
||||
CLEAR_PENDING_EXCEPTION;
|
||||
translate_hotspot_exception_to_jni_exception(THREAD, throwable);
|
||||
translate_to_jni_exception(THREAD, throwable, nullptr, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -801,6 +907,23 @@ JVMCIObject JVMCIEnv::call_HotSpotJVMCIRuntime_callToString(JVMCIObject object,
|
||||
}
|
||||
}
|
||||
|
||||
void JVMCIEnv::call_HotSpotJVMCIRuntime_postTranslation(JVMCIObject object, JVMCIEnv* JVMCIENV) {
|
||||
JavaThread* THREAD = JVMCI::compilation_tick(JavaThread::current()); // For exception macros.
|
||||
if (is_hotspot()) {
|
||||
JavaCallArguments jargs;
|
||||
jargs.push_oop(Handle(THREAD, HotSpotJVMCI::resolve(object)));
|
||||
JavaValue result(T_VOID);
|
||||
JavaCalls::call_static(&result,
|
||||
HotSpotJVMCI::HotSpotJVMCIRuntime::klass(),
|
||||
vmSymbols::postTranslation_name(),
|
||||
vmSymbols::object_void_signature(), &jargs, CHECK);
|
||||
} else {
|
||||
JNIAccessMark jni(this, THREAD);
|
||||
jni()->CallStaticVoidMethod(JNIJVMCI::HotSpotJVMCIRuntime::clazz(),
|
||||
JNIJVMCI::HotSpotJVMCIRuntime::postTranslation_method(),
|
||||
object.as_jobject());
|
||||
}
|
||||
}
|
||||
|
||||
JVMCIObject JVMCIEnv::call_JavaConstant_forPrimitive(JVMCIObject kind, jlong value, JVMCI_TRAPS) {
|
||||
JavaThread* THREAD = JVMCI::compilation_tick(JavaThread::current()); // For exception macros.
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2022, 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
|
||||
@ -171,11 +171,15 @@ class JVMCIEnv : public ResourceObj {
|
||||
const char* _file; // The file and ...
|
||||
int _line; // ... line where this JNIEnv was created
|
||||
|
||||
// Translates an exception on the HotSpot heap to an exception on
|
||||
// the shared library heap. The translation includes the stack and
|
||||
// causes of `throwable`. The translated exception is pending in the
|
||||
// shared library thread upon returning.
|
||||
void translate_hotspot_exception_to_jni_exception(JavaThread* THREAD, const Handle& throwable);
|
||||
// Translates an exception on the HotSpot heap (i.e., hotspot_env) to an exception on
|
||||
// the shared library heap (i.e., jni_env). The translation includes the stack and cause(s) of `throwable`.
|
||||
// The translated exception is pending in jni_env upon returning.
|
||||
static void translate_to_jni_exception(JavaThread* THREAD, const Handle& throwable, JVMCIEnv* hotspot_env, JVMCIEnv* jni_env);
|
||||
|
||||
// Translates an exception on the shared library heap (i.e., jni_env) to an exception on
|
||||
// the HotSpot heap (i.e., hotspot_env). The translation includes the stack and cause(s) of `throwable`.
|
||||
// The translated exception is pending in hotspot_env upon returning.
|
||||
static void translate_from_jni_exception(JavaThread* THREAD, jthrowable throwable, JVMCIEnv* hotspot_env, JVMCIEnv* jni_env);
|
||||
|
||||
public:
|
||||
// Opens a JVMCIEnv scope for a Java to VM call (e.g., via CompilerToVM).
|
||||
@ -225,6 +229,11 @@ public:
|
||||
jboolean has_pending_exception();
|
||||
void clear_pending_exception();
|
||||
|
||||
// If this env has a pending exception, it is translated to be a pending
|
||||
// exception in `peer_env` and is cleared from this env. Returns true
|
||||
// if a pending exception was transferred, false otherwise.
|
||||
jboolean transfer_pending_exception(JavaThread* THREAD, JVMCIEnv* peer_env);
|
||||
|
||||
// Prints an exception and stack trace of a pending exception.
|
||||
void describe_pending_exception(bool clear);
|
||||
|
||||
@ -311,6 +320,8 @@ public:
|
||||
|
||||
jboolean call_HotSpotJVMCIRuntime_isGCSupported(JVMCIObject runtime, jint gcIdentifier);
|
||||
|
||||
void call_HotSpotJVMCIRuntime_postTranslation(JVMCIObject object, JVMCI_TRAPS);
|
||||
|
||||
BasicType kindToBasicType(JVMCIObject kind, JVMCI_TRAPS);
|
||||
|
||||
#define DO_THROW(name) \
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2022, 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
|
||||
|
@ -349,13 +349,14 @@
|
||||
objectarray_field(HotSpotJVMCIRuntime, excludeFromJVMCICompilation, "[Ljava/lang/Module;") \
|
||||
jvmci_method(CallNonvirtualObjectMethod, GetMethodID, call_special, JVMCIObject, HotSpotJVMCIRuntime, compileMethod, compileMethod_signature, (JVMCIObject runtime, JVMCIObject method, int entry_bci, jlong env, int id)) \
|
||||
jvmci_method(CallNonvirtualObjectMethod, GetMethodID, call_special, JVMCIObject, HotSpotJVMCIRuntime, isGCSupported, int_bool_signature, (JVMCIObject runtime, int gcIdentifier)) \
|
||||
jvmci_method(CallStaticObjectMethod, GetStaticMethodID, call_static, JVMCIObject, HotSpotJVMCIRuntime, encodeThrowable, encodeThrowable_signature, (JVMCIObject throwable)) \
|
||||
jvmci_method(CallStaticObjectMethod, GetStaticMethodID, call_static, JVMCIObject, HotSpotJVMCIRuntime, decodeThrowable, decodeThrowable_signature, (JVMCIObject encodedThrowable)) \
|
||||
jvmci_method(CallStaticBooleanMethod, GetStaticMethodID, call_static, bool, HotSpotJVMCIRuntime, encodeThrowable, encodeThrowable_signature, (JVMCIObject throwable, jlong buffer, int buffer_size)) \
|
||||
jvmci_method(CallStaticVoidMethod, GetStaticMethodID, call_static, void, HotSpotJVMCIRuntime, decodeAndThrowThrowable, long_void_signature, (jlong buffer)) \
|
||||
jvmci_method(CallNonvirtualVoidMethod, GetMethodID, call_special, void, HotSpotJVMCIRuntime, bootstrapFinished, void_method_signature, (JVMCIObject runtime, JVMCI_TRAPS)) \
|
||||
jvmci_method(CallNonvirtualVoidMethod, GetMethodID, call_special, void, HotSpotJVMCIRuntime, shutdown, void_method_signature, (JVMCIObject runtime)) \
|
||||
jvmci_method(CallStaticObjectMethod, GetStaticMethodID, call_static, JVMCIObject, HotSpotJVMCIRuntime, runtime, runtime_signature, (JVMCI_TRAPS)) \
|
||||
jvmci_method(CallObjectMethod, GetMethodID, call_virtual, JVMCIObject, HotSpotJVMCIRuntime, getCompiler, getCompiler_signature, (JVMCIObject runtime, JVMCI_TRAPS)) \
|
||||
jvmci_method(CallStaticObjectMethod, GetStaticMethodID, call_static, JVMCIObject, HotSpotJVMCIRuntime, callToString, callToString_signature, (JVMCIObject object, JVMCI_TRAPS)) \
|
||||
jvmci_method(CallStaticVoidMethod, GetStaticMethodID, call_static, void, HotSpotJVMCIRuntime, postTranslation, object_void_signature, (JVMCIObject object, JVMCI_TRAPS)) \
|
||||
end_class \
|
||||
start_class(JVMCIError, jdk_vm_ci_common_JVMCIError) \
|
||||
jvmci_constructor(JVMCIError, "(Ljava/lang/String;)V") \
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2022, 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
|
||||
@ -105,9 +105,8 @@
|
||||
template(compileMethod_signature, "(Ljdk/vm/ci/hotspot/HotSpotResolvedJavaMethod;IJI)Ljdk/vm/ci/hotspot/HotSpotCompilationRequestResult;") \
|
||||
template(isGCSupported_name, "isGCSupported") \
|
||||
template(encodeThrowable_name, "encodeThrowable") \
|
||||
template(encodeThrowable_signature, "(Ljava/lang/Throwable;)Ljava/lang/String;") \
|
||||
template(decodeThrowable_name, "decodeThrowable") \
|
||||
template(decodeThrowable_signature, "(Ljava/lang/String;)Ljava/lang/Throwable;") \
|
||||
template(encodeThrowable_signature, "(Ljava/lang/Throwable;JI)I") \
|
||||
template(decodeAndThrowThrowable_name, "decodeAndThrowThrowable") \
|
||||
template(fromMetaspace_name, "fromMetaspace") \
|
||||
template(method_fromMetaspace_signature, "(J)Ljdk/vm/ci/hotspot/HotSpotResolvedJavaMethod;") \
|
||||
template(constantPool_fromMetaspace_signature, "(J)Ljdk/vm/ci/hotspot/HotSpotConstantPool;") \
|
||||
@ -123,6 +122,7 @@
|
||||
template(getCompiler_signature, "()Ljdk/vm/ci/runtime/JVMCICompiler;") \
|
||||
template(callToString_name, "callToString") \
|
||||
template(callToString_signature, "(Ljava/lang/Object;)Ljava/lang/String;") \
|
||||
template(postTranslation_name, "postTranslation") \
|
||||
template(getName_name, "getName") \
|
||||
template(bootstrapFinished_name, "bootstrapFinished") \
|
||||
template(forPrimitive_name, "forPrimitive") \
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2022, 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
|
||||
@ -849,7 +849,7 @@ final class CompilerToVM {
|
||||
/**
|
||||
* @see HotSpotJVMCIRuntime#translate(Object)
|
||||
*/
|
||||
native long translate(Object obj);
|
||||
native long translate(Object obj, boolean callPostTranslation);
|
||||
|
||||
/**
|
||||
* @see HotSpotJVMCIRuntime#unhand(Class, long)
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2022, 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
|
||||
@ -46,6 +46,8 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.vm.ci.code.Architecture;
|
||||
@ -66,6 +68,7 @@ import jdk.vm.ci.runtime.JVMCICompiler;
|
||||
import jdk.vm.ci.runtime.JVMCICompilerFactory;
|
||||
import jdk.vm.ci.runtime.JVMCIRuntime;
|
||||
import jdk.vm.ci.services.JVMCIServiceLocator;
|
||||
import jdk.vm.ci.services.Services;
|
||||
|
||||
/**
|
||||
* HotSpot implementation of a JVMCI runtime.
|
||||
@ -199,14 +202,44 @@ public final class HotSpotJVMCIRuntime implements JVMCIRuntime {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the exception encoded in {@code buffer} and throws it.
|
||||
*
|
||||
* @param buffer a native byte buffer containing an exception encoded by
|
||||
* {@link #encodeThrowable}
|
||||
*/
|
||||
@VMEntryPoint
|
||||
static Throwable decodeThrowable(String encodedThrowable) throws Throwable {
|
||||
return TranslatedException.decodeThrowable(encodedThrowable);
|
||||
static void decodeAndThrowThrowable(long buffer) throws Throwable {
|
||||
Unsafe unsafe = UnsafeAccess.UNSAFE;
|
||||
int encodingLength = unsafe.getInt(buffer);
|
||||
byte[] encoding = new byte[encodingLength];
|
||||
unsafe.copyMemory(null, buffer + 4, encoding, Unsafe.ARRAY_BYTE_BASE_OFFSET, encodingLength);
|
||||
throw TranslatedException.decodeThrowable(encoding);
|
||||
}
|
||||
|
||||
/**
|
||||
* If {@code bufferSize} is large enough, encodes {@code throwable} into a byte array and writes
|
||||
* it to {@code buffer}. The encoding in {@code buffer} can be decoded by
|
||||
* {@link #decodeAndThrowThrowable}.
|
||||
*
|
||||
* @param throwable the exception to encode
|
||||
* @param buffer a native byte buffer
|
||||
* @param bufferSize the size of {@code buffer} in bytes
|
||||
* @return the number of bytes written into {@code buffer} if {@code bufferSize} is large
|
||||
* enough, otherwise {@code -N} where {@code N} is the value {@code bufferSize} needs to
|
||||
* be to fit the encoding
|
||||
*/
|
||||
@VMEntryPoint
|
||||
static String encodeThrowable(Throwable throwable) throws Throwable {
|
||||
return TranslatedException.encodeThrowable(throwable);
|
||||
static int encodeThrowable(Throwable throwable, long buffer, int bufferSize) throws Throwable {
|
||||
byte[] encoding = TranslatedException.encodeThrowable(throwable);
|
||||
int requiredSize = 4 + encoding.length;
|
||||
if (bufferSize < requiredSize) {
|
||||
return -requiredSize;
|
||||
}
|
||||
Unsafe unsafe = UnsafeAccess.UNSAFE;
|
||||
unsafe.putInt(buffer, encoding.length);
|
||||
unsafe.copyMemory(encoding, Unsafe.ARRAY_BYTE_BASE_OFFSET, null, buffer + 4, encoding.length);
|
||||
return requiredSize;
|
||||
}
|
||||
|
||||
@VMEntryPoint
|
||||
@ -235,6 +268,10 @@ public final class HotSpotJVMCIRuntime implements JVMCIRuntime {
|
||||
// Note: The following one is not used (see InitTimer.ENABLED). It is added here
|
||||
// so that -XX:+JVMCIPrintProperties shows the option.
|
||||
InitTimer(Boolean.class, false, "Specifies if initialization timing is enabled."),
|
||||
ForceTranslateFailure(String.class, null, "Forces HotSpotJVMCIRuntime.translate to throw an exception in the context " +
|
||||
"of the peer runtime. The value is a filter that can restrict the forced failure to matching translated " +
|
||||
"objects. See HotSpotJVMCIRuntime.postTranslation for more details. This option exists soley to test " +
|
||||
"correct handling of translation failure."),
|
||||
PrintConfig(Boolean.class, false, "Prints VM configuration available via JVMCI."),
|
||||
AuditHandles(Boolean.class, false, "Record stack trace along with scoped foreign object reference wrappers " +
|
||||
"to debug issue with a wrapper being used after its scope has closed."),
|
||||
@ -1180,7 +1217,88 @@ public final class HotSpotJVMCIRuntime implements JVMCIRuntime {
|
||||
* @see "https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#global_and_local_references"
|
||||
*/
|
||||
public long translate(Object obj) {
|
||||
return compilerToVm.translate(obj);
|
||||
return compilerToVm.translate(obj, Option.ForceTranslateFailure.getString() != null);
|
||||
}
|
||||
|
||||
private static final Pattern FORCE_TRANSLATE_FAILURE_FILTER_RE = Pattern.compile("(?:(method|type|nmethod)/)?([^:]+)(?::(hotspot|native))?");
|
||||
|
||||
/**
|
||||
* Forces translation failure based on {@code translatedObject} and the value of
|
||||
* {@link Option#ForceTranslateFailure}. The value is zero or more filters separated by a comma.
|
||||
* The syntax for a filter is:
|
||||
*
|
||||
* <pre>
|
||||
* Filter = [ TypeSelector "/" ] Substring [ ":" JVMCIEnvSelector ] .
|
||||
* TypeSelector = "type" | "method" | "nmethod"
|
||||
* JVMCIEnvSelector = "native" | "hotspot"
|
||||
* </pre>
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* <pre>
|
||||
* -Djvmci.ForceTranslateFailure=nmethod/StackOverflowError:native,method/computeHash,execute
|
||||
* </pre>
|
||||
*
|
||||
* will cause failure of:
|
||||
* <ul>
|
||||
* <li>translating a {@link HotSpotNmethod} to the libjvmci heap whose fully qualified name
|
||||
* contains "StackOverflowError"</li>
|
||||
* <li>translating a {@link HotSpotResolvedJavaMethodImpl} to the libjvmci or HotSpot heap whose
|
||||
* fully qualified name contains "computeHash"</li>
|
||||
* <li>translating a {@link HotSpotNmethod}, {@link HotSpotResolvedJavaMethodImpl} or
|
||||
* {@link HotSpotResolvedObjectTypeImpl} to the libjvmci or HotSpot heap whose fully qualified
|
||||
* name contains "execute"</li>
|
||||
* </ul>
|
||||
*/
|
||||
@VMEntryPoint
|
||||
static void postTranslation(Object translatedObject) {
|
||||
String value = Option.ForceTranslateFailure.getString();
|
||||
String toMatch;
|
||||
String type;
|
||||
if (translatedObject instanceof HotSpotResolvedJavaMethodImpl) {
|
||||
toMatch = ((HotSpotResolvedJavaMethodImpl) translatedObject).format("%H.%n");
|
||||
type = "method";
|
||||
} else if (translatedObject instanceof HotSpotResolvedObjectTypeImpl) {
|
||||
toMatch = ((HotSpotResolvedObjectTypeImpl) translatedObject).toJavaName();
|
||||
type = "type";
|
||||
} else if (translatedObject instanceof HotSpotNmethod) {
|
||||
HotSpotNmethod nmethod = (HotSpotNmethod) translatedObject;
|
||||
if (nmethod.getMethod() != null) {
|
||||
toMatch = nmethod.getMethod().format("%H.%n");
|
||||
} else {
|
||||
toMatch = String.valueOf(nmethod.getName());
|
||||
}
|
||||
type = "nmethod";
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
String[] filters = value.split(",");
|
||||
for (String filter : filters) {
|
||||
Matcher m = FORCE_TRANSLATE_FAILURE_FILTER_RE.matcher(filter);
|
||||
if (!m.matches()) {
|
||||
throw new JVMCIError(Option.ForceTranslateFailure + " filter does not match " + FORCE_TRANSLATE_FAILURE_FILTER_RE + ": " + filter);
|
||||
}
|
||||
String typeSelector = m.group(1);
|
||||
String substring = m.group(2);
|
||||
String jvmciEnvSelector = m.group(3);
|
||||
if (jvmciEnvSelector != null) {
|
||||
if (jvmciEnvSelector.equals("native")) {
|
||||
if (!Services.IS_IN_NATIVE_IMAGE) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (Services.IS_IN_NATIVE_IMAGE) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (typeSelector != null && !typeSelector.equals(type)) {
|
||||
continue;
|
||||
}
|
||||
if (toMatch.contains(substring)) {
|
||||
throw new JVMCIError("translation of " + translatedObject + " failed due to matching " + Option.ForceTranslateFailure + " filter \"" + filter + "\"");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 2022, 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
|
||||
@ -22,13 +22,20 @@
|
||||
*/
|
||||
package jdk.vm.ci.hotspot;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Formatter;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import jdk.vm.ci.common.JVMCIError;
|
||||
|
||||
/**
|
||||
* Support for translating exceptions between different runtime heaps.
|
||||
@ -36,6 +43,26 @@ import java.util.Objects;
|
||||
@SuppressWarnings("serial")
|
||||
final class TranslatedException extends Exception {
|
||||
|
||||
/**
|
||||
* The value returned by {@link #encodeThrowable(Throwable)} when encoding fails due to an
|
||||
* {@link OutOfMemoryError}.
|
||||
*/
|
||||
private static final byte[] FALLBACK_ENCODED_OUTOFMEMORYERROR_BYTES;
|
||||
|
||||
/**
|
||||
* The value returned by {@link #encodeThrowable(Throwable)} when encoding fails for any reason
|
||||
* other than {@link OutOfMemoryError}.
|
||||
*/
|
||||
private static final byte[] FALLBACK_ENCODED_THROWABLE_BYTES;
|
||||
static {
|
||||
try {
|
||||
FALLBACK_ENCODED_THROWABLE_BYTES = encodeThrowable(new TranslatedException("error during encoding", "<unknown>"), false);
|
||||
FALLBACK_ENCODED_OUTOFMEMORYERROR_BYTES = encodeThrowable(new OutOfMemoryError(), false);
|
||||
} catch (IOException e) {
|
||||
throw new JVMCIError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class name of exception that could not be instantiated.
|
||||
*/
|
||||
@ -110,83 +137,74 @@ final class TranslatedException extends Exception {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes an exception message to distinguish a null message from an empty message.
|
||||
*
|
||||
* @return {@code value} with a space prepended iff {@code value != null}
|
||||
*/
|
||||
private static String encodeMessage(String value) {
|
||||
return value != null ? ' ' + value : value;
|
||||
private static String emptyIfNull(String value) {
|
||||
return value == null ? "" : value;
|
||||
}
|
||||
|
||||
private static String decodeMessage(String value) {
|
||||
if (value.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
return value.substring(1);
|
||||
}
|
||||
|
||||
private static String encodedString(String value) {
|
||||
return Objects.toString(value, "").replace('|', '_');
|
||||
private static String emptyAsNull(String value) {
|
||||
return value.isEmpty() ? null : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes {@code throwable} including its stack and causes as a string. The encoding format of
|
||||
* a single exception is:
|
||||
*
|
||||
* <pre>
|
||||
* <exception class name> '|' <exception message> '|' <stack size> '|' [ <classLoader> '|' <module> '|' <moduleVersion> '|' <class> '|' <method> '|' <file> '|' <line> '|' ]*
|
||||
* </pre>
|
||||
*
|
||||
* Each exception is encoded before the exception it causes.
|
||||
* Encodes {@code throwable} including its stack and causes as a {@linkplain GZIPOutputStream
|
||||
* compressed} byte array that can be decoded by {@link #decodeThrowable}.
|
||||
*/
|
||||
@VMEntryPoint
|
||||
static String encodeThrowable(Throwable throwable) throws Throwable {
|
||||
static byte[] encodeThrowable(Throwable throwable) throws Throwable {
|
||||
try {
|
||||
Formatter enc = new Formatter();
|
||||
return encodeThrowable(throwable, true);
|
||||
} catch (OutOfMemoryError e) {
|
||||
return FALLBACK_ENCODED_OUTOFMEMORYERROR_BYTES;
|
||||
} catch (Throwable e) {
|
||||
return FALLBACK_ENCODED_THROWABLE_BYTES;
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] encodeThrowable(Throwable throwable, boolean withCauseAndStack) throws IOException {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
try (DataOutputStream dos = new DataOutputStream(new GZIPOutputStream(baos))) {
|
||||
List<Throwable> throwables = new ArrayList<>();
|
||||
for (Throwable current = throwable; current != null; current = current.getCause()) {
|
||||
throwables.add(current);
|
||||
if (!withCauseAndStack) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Encode from inner most cause outwards
|
||||
Collections.reverse(throwables);
|
||||
|
||||
for (Throwable current : throwables) {
|
||||
enc.format("%s|%s|", current.getClass().getName(), encodedString(encodeMessage(current.getMessage())));
|
||||
StackTraceElement[] stackTrace = current.getStackTrace();
|
||||
dos.writeUTF(current.getClass().getName());
|
||||
dos.writeUTF(emptyIfNull(current.getMessage()));
|
||||
StackTraceElement[] stackTrace = withCauseAndStack ? current.getStackTrace() : null;
|
||||
if (stackTrace == null) {
|
||||
stackTrace = new StackTraceElement[0];
|
||||
}
|
||||
enc.format("%d|", stackTrace.length);
|
||||
dos.writeInt(stackTrace.length);
|
||||
for (int i = 0; i < stackTrace.length; i++) {
|
||||
StackTraceElement frame = stackTrace[i];
|
||||
if (frame != null) {
|
||||
enc.format("%s|%s|%s|%s|%s|%s|%d|", encodedString(frame.getClassLoaderName()),
|
||||
encodedString(frame.getModuleName()), encodedString(frame.getModuleVersion()),
|
||||
frame.getClassName(), frame.getMethodName(),
|
||||
encodedString(frame.getFileName()), frame.getLineNumber());
|
||||
dos.writeUTF(emptyIfNull(frame.getClassLoaderName()));
|
||||
dos.writeUTF(emptyIfNull(frame.getModuleName()));
|
||||
dos.writeUTF(emptyIfNull(frame.getModuleVersion()));
|
||||
dos.writeUTF(emptyIfNull(frame.getClassName()));
|
||||
dos.writeUTF(emptyIfNull(frame.getMethodName()));
|
||||
dos.writeUTF(emptyIfNull(frame.getFileName()));
|
||||
dos.writeInt(frame.getLineNumber());
|
||||
}
|
||||
}
|
||||
}
|
||||
return enc.toString();
|
||||
} catch (Throwable e) {
|
||||
assert printStackTrace(e);
|
||||
try {
|
||||
return e.getClass().getName() + "|" + encodedString(e.getMessage()) + "|0|";
|
||||
} catch (Throwable e2) {
|
||||
assert printStackTrace(e2);
|
||||
return "java.lang.Throwable|too many errors during encoding|0|";
|
||||
}
|
||||
}
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the stack of the current thread without the frames between this call and the one just
|
||||
* below the frame of the first method in {@link CompilerToVM}. The chopped frames are specific
|
||||
* to the implementation of {@link HotSpotJVMCIRuntime#decodeThrowable(String)}.
|
||||
* below the frame of the first method in {@link CompilerToVM}. The chopped frames are for the
|
||||
* VM call to {@link HotSpotJVMCIRuntime#decodeAndThrowThrowable}.
|
||||
*/
|
||||
private static StackTraceElement[] getStackTraceSuffix() {
|
||||
private static StackTraceElement[] getMyStackTrace() {
|
||||
StackTraceElement[] stack = new Exception().getStackTrace();
|
||||
for (int i = 0; i < stack.length; i++) {
|
||||
StackTraceElement e = stack[i];
|
||||
@ -206,43 +224,47 @@ final class TranslatedException extends Exception {
|
||||
* {@link #encodeThrowable}
|
||||
*/
|
||||
@VMEntryPoint
|
||||
static Throwable decodeThrowable(String encodedThrowable) {
|
||||
try {
|
||||
int i = 0;
|
||||
String[] parts = encodedThrowable.split("\\|");
|
||||
static Throwable decodeThrowable(byte[] encodedThrowable) {
|
||||
try (DataInputStream dis = new DataInputStream(new GZIPInputStream(new ByteArrayInputStream(encodedThrowable)))) {
|
||||
Throwable cause = null;
|
||||
Throwable throwable = null;
|
||||
while (i != parts.length) {
|
||||
String exceptionClassName = parts[i++];
|
||||
String exceptionMessage = decodeMessage(parts[i++]);
|
||||
StackTraceElement[] myStack = getMyStackTrace();
|
||||
while (dis.available() != 0) {
|
||||
String exceptionClassName = dis.readUTF();
|
||||
String exceptionMessage = emptyAsNull(dis.readUTF());
|
||||
throwable = create(exceptionClassName, exceptionMessage, cause);
|
||||
int stackTraceDepth = Integer.parseInt(parts[i++]);
|
||||
|
||||
StackTraceElement[] suffix = getStackTraceSuffix();
|
||||
StackTraceElement[] stackTrace = new StackTraceElement[stackTraceDepth + suffix.length];
|
||||
int stackTraceDepth = dis.readInt();
|
||||
StackTraceElement[] stackTrace = new StackTraceElement[stackTraceDepth + myStack.length];
|
||||
int stackTraceIndex = 0;
|
||||
int myStackIndex = 0;
|
||||
for (int j = 0; j < stackTraceDepth; j++) {
|
||||
String classLoaderName = parts[i++];
|
||||
String moduleName = parts[i++];
|
||||
String moduleVersion = parts[i++];
|
||||
String className = parts[i++];
|
||||
String methodName = parts[i++];
|
||||
String fileName = parts[i++];
|
||||
int lineNumber = Integer.parseInt(parts[i++]);
|
||||
if (classLoaderName.isEmpty()) {
|
||||
classLoaderName = null;
|
||||
String classLoaderName = emptyAsNull(dis.readUTF());
|
||||
String moduleName = emptyAsNull(dis.readUTF());
|
||||
String moduleVersion = emptyAsNull(dis.readUTF());
|
||||
String className = emptyAsNull(dis.readUTF());
|
||||
String methodName = emptyAsNull(dis.readUTF());
|
||||
String fileName = emptyAsNull(dis.readUTF());
|
||||
int lineNumber = dis.readInt();
|
||||
StackTraceElement ste = new StackTraceElement(classLoaderName, moduleName, moduleVersion, className, methodName, fileName, lineNumber);
|
||||
|
||||
if (ste.isNativeMethod()) {
|
||||
// Best effort attempt to weave stack traces from two heaps into
|
||||
// a single stack trace using native method frames as stitching points.
|
||||
// This is not 100% reliable as there's no guarantee that native method
|
||||
// frames only exist for calls between HotSpot and libjvmci.
|
||||
while (myStackIndex < myStack.length) {
|
||||
StackTraceElement suffixSTE = myStack[myStackIndex++];
|
||||
if (suffixSTE.isNativeMethod()) {
|
||||
break;
|
||||
}
|
||||
stackTrace[stackTraceIndex++] = suffixSTE;
|
||||
}
|
||||
}
|
||||
if (moduleName.isEmpty()) {
|
||||
moduleName = null;
|
||||
}
|
||||
if (moduleVersion.isEmpty()) {
|
||||
moduleVersion = null;
|
||||
}
|
||||
if (fileName.isEmpty()) {
|
||||
fileName = null;
|
||||
}
|
||||
stackTrace[j] = new StackTraceElement(classLoaderName, moduleName, moduleVersion, className, methodName, fileName, lineNumber);
|
||||
stackTrace[stackTraceIndex++] = ste;
|
||||
}
|
||||
while (myStackIndex < myStack.length) {
|
||||
stackTrace[stackTraceIndex++] = myStack[myStackIndex++];
|
||||
}
|
||||
System.arraycopy(suffix, 0, stackTrace, stackTraceDepth, suffix.length);
|
||||
throwable.setStackTrace(stackTrace);
|
||||
cause = throwable;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, 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
|
||||
@ -24,7 +24,8 @@
|
||||
/*
|
||||
* @test
|
||||
* @requires vm.jvmci
|
||||
* @modules jdk.internal.vm.ci/jdk.vm.ci.hotspot:open
|
||||
* @modules jdk.internal.vm.ci/jdk.vm.ci.hotspot:+open
|
||||
* java.base/jdk.internal.misc
|
||||
* @library /compiler/jvmci/jdk.vm.ci.hotspot.test/src
|
||||
* @run testng/othervm
|
||||
* -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:-UseJVMCICompiler
|
||||
@ -41,6 +42,9 @@ import java.lang.reflect.Method;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
|
||||
|
||||
public class TestTranslatedException {
|
||||
@SuppressWarnings("serial")
|
||||
public static class Untranslatable extends RuntimeException {
|
||||
@ -56,7 +60,7 @@ public class TestTranslatedException {
|
||||
Class<?> translatedExceptionClass = Class.forName("jdk.vm.ci.hotspot.TranslatedException");
|
||||
|
||||
Method encode = translatedExceptionClass.getDeclaredMethod("encodeThrowable", Throwable.class);
|
||||
Method decode = translatedExceptionClass.getDeclaredMethod("decodeThrowable", String.class);
|
||||
Method decode = translatedExceptionClass.getDeclaredMethod("decodeThrowable", byte[].class);
|
||||
encode.setAccessible(true);
|
||||
decode.setAccessible(true);
|
||||
|
||||
@ -64,11 +68,50 @@ public class TestTranslatedException {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
throwable = new ExceptionInInitializerError(new InvocationTargetException(new RuntimeException(String.valueOf(i), throwable), "invoke"));
|
||||
}
|
||||
String encoding = (String) encode.invoke(null, throwable);
|
||||
byte[] encoding = (byte[]) encode.invoke(null, throwable);
|
||||
Throwable decoded = (Throwable) decode.invoke(null, encoding);
|
||||
assertThrowableEquals(throwable, decoded);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void encodeDecodeTest2() throws Exception {
|
||||
Unsafe unsafe = Unsafe.getUnsafe();
|
||||
int bufferSize = 512;
|
||||
long buffer = 0L;
|
||||
while (true) {
|
||||
buffer = unsafe.allocateMemory(bufferSize);
|
||||
try {
|
||||
Throwable throwable = new ExceptionInInitializerError(new InvocationTargetException(new Untranslatable("test exception", new NullPointerException()), "invoke"));
|
||||
for (int i = 0; i < 10; i++) {
|
||||
throwable = new ExceptionInInitializerError(new InvocationTargetException(new RuntimeException(String.valueOf(i), throwable), "invoke"));
|
||||
}
|
||||
|
||||
Method encode = HotSpotJVMCIRuntime.class.getDeclaredMethod("encodeThrowable", Throwable.class, long.class, int.class);
|
||||
Method decode = HotSpotJVMCIRuntime.class.getDeclaredMethod("decodeAndThrowThrowable", long.class);
|
||||
encode.setAccessible(true);
|
||||
decode.setAccessible(true);
|
||||
|
||||
int res = (Integer) encode.invoke(null, throwable, buffer, bufferSize);
|
||||
|
||||
if (res < 0) {
|
||||
bufferSize = -res;
|
||||
} else {
|
||||
try {
|
||||
decode.invoke(null, buffer);
|
||||
throw new AssertionError("expected decodeAndThrowThrowable to throw an exception");
|
||||
} catch (InvocationTargetException e) {
|
||||
Throwable decoded = e.getCause();
|
||||
assertThrowableEquals(throwable, decoded);
|
||||
}
|
||||
return;
|
||||
}
|
||||
} finally {
|
||||
unsafe.freeMemory(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertThrowableEquals(Throwable original, Throwable decoded) {
|
||||
try {
|
||||
Assert.assertEquals(original == null, decoded == null);
|
||||
|
Loading…
x
Reference in New Issue
Block a user