Skip to content

Commit 2a8a8d3

Browse files
[GR-73418] [GR-73419] Fix VMAccess and espresso exception issues.
PullRequest: graal/23279
2 parents fd518d2 + b079745 commit 2a8a8d3

7 files changed

Lines changed: 157 additions & 73 deletions

File tree

compiler/src/jdk.graal.compiler.hostvmaccess/src/jdk/graal/compiler/hostvmaccess/HostVMAccess.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,16 @@ public JavaConstant invoke(ResolvedJavaMethod method, JavaConstant receiver, Jav
128128
JavaKind parameterKind = signature.getParameterKind(i);
129129
JavaConstant argument = arguments[i];
130130
if (parameterKind.isObject()) {
131-
unboxedArguments[i] = snippetReflection.asObject(parameterTypes[i], argument);
131+
if (argument.isNull()) {
132+
unboxedArguments[i] = null;
133+
} else {
134+
unboxedArguments[i] = snippetReflection.asObject(parameterTypes[i], argument);
135+
if (unboxedArguments[i] == null) {
136+
throw new IllegalArgumentException(
137+
"Illegal argument type: arguments[" + i + "] of type " + providers.getMetaAccess().lookupJavaType(arguments[i]).toClassName() +
138+
" could not be converted to a " + parameterTypes[i]);
139+
}
140+
}
132141
} else {
133142
assert parameterKind.isPrimitive();
134143
unboxedArguments[i] = argument.asBoxedPrimitive();
@@ -144,7 +153,16 @@ public JavaConstant invoke(ResolvedJavaMethod method, JavaConstant receiver, Jav
144153
if (Modifier.isStatic(reflectionMethod.getModifiers())) {
145154
unboxedReceiver = null;
146155
} else {
147-
unboxedReceiver = snippetReflection.asObject(reflectionMethod.getDeclaringClass(), receiver);
156+
if (receiver.isNull()) {
157+
unboxedReceiver = null;
158+
} else {
159+
unboxedReceiver = snippetReflection.asObject(reflectionMethod.getDeclaringClass(), receiver);
160+
if (unboxedReceiver == null) {
161+
throw new IllegalArgumentException(
162+
"Illegal argument type: receiver of type " + providers.getMetaAccess().lookupJavaType(receiver).toClassName() +
163+
" could not be converted to a " + reflectionMethod.getDeclaringClass());
164+
}
165+
}
148166
}
149167
JavaKind returnKind = method.getSignature().getReturnKind();
150168
Object result = reflectionMethod.invoke(unboxedReceiver, unboxedArguments);

espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalResolvedJavaMethod.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,11 @@ JavaConstant invoke(JavaConstant receiver, JavaConstant... arguments) {
380380
if (guestException == null || guestException.isNull()) {
381381
throw e;
382382
}
383+
/*
384+
* `guestException` might be a handle to the exception carrier (`EspressoException`) but
385+
* we want a handle to the exception object (`StaticObject` of guest type `Throwable`)
386+
*/
387+
guestException = access.invokeJVMCIHelper("getExceptionObject", guestException);
383388
throw new InvocationException(new EspressoExternalObjectConstant(access, guestException), e);
384389
}
385390
if (isConstructor()) {

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/external/JVMCIInteropHelper.java

Lines changed: 60 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
import com.oracle.truffle.espresso.meta.EspressoError;
6969
import com.oracle.truffle.espresso.meta.Meta;
7070
import com.oracle.truffle.espresso.runtime.EspressoContext;
71+
import com.oracle.truffle.espresso.runtime.EspressoException;
7172
import com.oracle.truffle.espresso.runtime.staticobject.StaticObject;
7273
import com.oracle.truffle.espresso.substitutions.continuations.Target_org_graalvm_continuations_IdentityHashCodes;
7374
import com.oracle.truffle.espresso.vm.InterpreterToVM;
@@ -125,6 +126,7 @@ public final class JVMCIInteropHelper implements ContextAccess, TruffleObject {
125126
InvokeMember.GET_REFLECT_FIELD,
126127
InvokeMember.GET_REFLECT_EXECUTABLE,
127128
InvokeMember.READ_OBJECT_ARRAY_ELEMENT,
129+
InvokeMember.GET_EXCEPTION_OBJECT,
128130
};
129131
ALL_MEMBERS = new KeysArray<>(members);
130132
ALL_MEMBERS_SET = Set.of(members);
@@ -189,6 +191,7 @@ abstract static class InvokeMember {
189191
static final String GET_REFLECT_FIELD = "getReflectField";
190192
static final String GET_REFLECT_EXECUTABLE = "getReflectExecutable";
191193
static final String READ_OBJECT_ARRAY_ELEMENT = "readObjectArrayElement";
194+
static final String GET_EXCEPTION_OBJECT = "getExceptionObject";
192195

193196
@Specialization(guards = "GET_FLAGS.equals(member)")
194197
static int getFlags(JVMCIInteropHelper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
@@ -1073,6 +1076,63 @@ static Object resolveLinkToTarget(JVMCIInteropHelper receiver, @SuppressWarnings
10731076
return method;
10741077
}
10751078

1079+
@Specialization(guards = "READ_OBJECT_ARRAY_ELEMENT.equals(member)")
1080+
static Object readObjectArrayElement(JVMCIInteropHelper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
1081+
@Bind Node node,
1082+
@CachedLibrary(limit = "1") @Shared InteropLibrary intInterop,
1083+
@Cached @Shared InlinedBranchProfile typeError,
1084+
@Cached @Shared InlinedBranchProfile arityError,
1085+
@Cached @Shared InlinedBranchProfile indexError) throws ArityException, UnsupportedTypeException {
1086+
assert receiver != null;
1087+
EspressoLanguage language = EspressoLanguage.get(node);
1088+
assert language.isExternalJVMCIEnabled();
1089+
if (arguments.length != 2) {
1090+
arityError.enter(node);
1091+
throw ArityException.create(2, 2, arguments.length);
1092+
}
1093+
if (!(arguments[0] instanceof StaticObject staticObject && staticObject.isArray())) {
1094+
typeError.enter(node);
1095+
throw UnsupportedTypeException.create(arguments, "Expected an array as first argument");
1096+
}
1097+
int index;
1098+
try {
1099+
index = intInterop.asInt(arguments[1]);
1100+
} catch (UnsupportedMessageException e) {
1101+
typeError.enter(node);
1102+
throw UnsupportedTypeException.create(arguments, "Expected an integer as second argument");
1103+
}
1104+
if (index < 0 || index >= staticObject.length(language)) {
1105+
indexError.enter(node);
1106+
throw EspressoContext.get(node).getMeta().throwArrayIndexOutOfBounds(index, staticObject.length(language));
1107+
}
1108+
return staticObject.<StaticObject[]> unwrap(language)[index];
1109+
}
1110+
1111+
@Specialization(guards = "GET_EXCEPTION_OBJECT.equals(member)")
1112+
static Object getExceptionObject(JVMCIInteropHelper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
1113+
@Bind Node node,
1114+
@Cached @Shared InlinedBranchProfile typeError,
1115+
@Cached @Shared InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
1116+
assert receiver != null;
1117+
if (arguments.length != 1) {
1118+
arityError.enter(node);
1119+
throw ArityException.create(1, 1, arguments.length);
1120+
}
1121+
if (arguments[0] instanceof EspressoException exception) {
1122+
return exception.getGuestException();
1123+
}
1124+
if (arguments[0] instanceof StaticObject exception) {
1125+
Meta meta = EspressoContext.get(node).getMeta();
1126+
if (!InterpreterToVM.instanceOf(exception, meta.java_lang_Throwable)) {
1127+
typeError.enter(node);
1128+
throw UnsupportedTypeException.create(arguments, "Expected an guest object of type Throwable");
1129+
}
1130+
return exception;
1131+
}
1132+
typeError.enter(node);
1133+
throw UnsupportedTypeException.create(arguments, "Expected an EspressoException or a guest object of type Throwable");
1134+
}
1135+
10761136
@Fallback
10771137
@SuppressWarnings("unused")
10781138
static Object doUnknown(JVMCIInteropHelper receiver, String member, Object[] arguments) throws UnknownIdentifierException {
@@ -1131,38 +1191,6 @@ private static Method getSingleReflectMethodArgument(Object[] arguments, Node no
11311191
typeError.enter(node);
11321192
throw UnsupportedTypeException.create(arguments, "Expected a java.lang.reflect.Executable object");
11331193
}
1134-
1135-
@Specialization(guards = "READ_OBJECT_ARRAY_ELEMENT.equals(member)")
1136-
static Object readObjectArrayElement(JVMCIInteropHelper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
1137-
@Bind Node node,
1138-
@CachedLibrary(limit = "1") @Shared InteropLibrary intInterop,
1139-
@Cached @Shared InlinedBranchProfile typeError,
1140-
@Cached @Shared InlinedBranchProfile arityError,
1141-
@Cached @Shared InlinedBranchProfile indexError) throws ArityException, UnsupportedTypeException {
1142-
assert receiver != null;
1143-
EspressoLanguage language = EspressoLanguage.get(node);
1144-
assert language.isExternalJVMCIEnabled();
1145-
if (arguments.length != 2) {
1146-
arityError.enter(node);
1147-
throw ArityException.create(2, 2, arguments.length);
1148-
}
1149-
if (!(arguments[0] instanceof StaticObject staticObject && staticObject.isArray())) {
1150-
typeError.enter(node);
1151-
throw UnsupportedTypeException.create(arguments, "Expected an array as first argument");
1152-
}
1153-
int index;
1154-
try {
1155-
index = intInterop.asInt(arguments[1]);
1156-
} catch (UnsupportedMessageException e) {
1157-
typeError.enter(node);
1158-
throw UnsupportedTypeException.create(arguments, "Expected an integer as second argument");
1159-
}
1160-
if (index < 0 || index >= staticObject.length(language)) {
1161-
indexError.enter(node);
1162-
throw EspressoContext.get(node).getMeta().throwArrayIndexOutOfBounds(index, staticObject.length(language));
1163-
}
1164-
return staticObject.<StaticObject[]> unwrap(language)[index];
1165-
}
11661194
}
11671195

11681196
@ExportMessage

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/ForeignStackTraceElementObject.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,17 @@
2222
*/
2323
package com.oracle.truffle.espresso.runtime;
2424

25+
import java.nio.file.InvalidPathException;
26+
import java.nio.file.Path;
27+
28+
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
2529
import com.oracle.truffle.api.interop.InteropLibrary;
2630
import com.oracle.truffle.api.interop.TruffleObject;
2731
import com.oracle.truffle.api.interop.UnsupportedMessageException;
2832
import com.oracle.truffle.api.library.ExportLibrary;
2933
import com.oracle.truffle.api.library.ExportMessage;
3034
import com.oracle.truffle.api.source.SourceSection;
35+
import com.oracle.truffle.espresso.EspressoLanguage;
3136
import com.oracle.truffle.espresso.impl.Method;
3237

3338
@ExportLibrary(InteropLibrary.class)
@@ -76,4 +81,43 @@ boolean hasDeclaringMetaObject() {
7681
Object getDeclaringMetaObject() {
7782
return method.getDeclaringKlass().mirror();
7883
}
84+
85+
@ExportMessage
86+
@SuppressWarnings("static-method")
87+
boolean hasLanguageId() {
88+
return true;
89+
}
90+
91+
@ExportMessage
92+
@SuppressWarnings("static-method")
93+
String getLanguageId() {
94+
return EspressoLanguage.ID;
95+
}
96+
97+
@ExportMessage
98+
@TruffleBoundary
99+
Object toDisplayString(@SuppressWarnings("unused") boolean allowSideEffects) {
100+
StringBuilder sb = new StringBuilder();
101+
sb.append('<').append(getLanguageId()).append("> ");
102+
sb.append(method.getDeclaringKlass().getExternalName()).append('.').append(method.getName()).append('(');
103+
if (sourceSection != null) {
104+
String fileName = sourceSection.getSource().getPath();
105+
try {
106+
Path fileNamePath = Path.of(fileName).getFileName();
107+
if (fileNamePath != null && !fileNamePath.toString().isEmpty()) {
108+
fileName = fileNamePath.toString();
109+
}
110+
} catch (InvalidPathException e) {
111+
// ignore
112+
}
113+
sb.append(fileName);
114+
if (sourceSection.hasLines()) {
115+
sb.append(':').append(sourceSection.getStartLine());
116+
}
117+
} else {
118+
sb.append("Unknown Source");
119+
}
120+
sb.append(')');
121+
return sb.toString();
122+
}
79123
}

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/dispatch/staticobject/ThrowableInterop.java

Lines changed: 26 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,6 @@
2727
import com.oracle.truffle.api.interop.UnsupportedMessageException;
2828
import com.oracle.truffle.api.library.ExportLibrary;
2929
import com.oracle.truffle.api.library.ExportMessage;
30-
import com.oracle.truffle.espresso.descriptors.EspressoSymbols.Names;
31-
import com.oracle.truffle.espresso.descriptors.EspressoSymbols.Signatures;
32-
import com.oracle.truffle.espresso.impl.Method;
3330
import com.oracle.truffle.espresso.meta.Meta;
3431
import com.oracle.truffle.espresso.runtime.dispatch.messages.GenerateInteropNodes;
3532
import com.oracle.truffle.espresso.runtime.dispatch.messages.Shareable;
@@ -62,59 +59,51 @@ public static RuntimeException throwException(StaticObject object) {
6259
public static boolean hasExceptionCause(StaticObject object) {
6360
object.checkNotForeign();
6461
Meta meta = object.getKlass().getMeta();
65-
Method resolvedMessageMethod = object.getKlass().lookupMethod(Names.getCause, Signatures.Throwable);
66-
if (resolvedMessageMethod == meta.java_lang_Throwable_getCause) {
67-
// not overridden, then we can trust the field value
68-
StaticObject guestCause = meta.java_lang_Throwable_cause.getObject(object);
69-
return StaticObject.notNull(guestCause) && guestCause != object;
70-
} else if (resolvedMessageMethod.isInlinableGetter()) {
71-
// only call the method for a 'has' interop message if it's simple
72-
StaticObject guestCause = (StaticObject) resolvedMessageMethod.invokeDirect(object);
73-
return StaticObject.notNull(guestCause) && guestCause != object;
74-
} else {
75-
/*
76-
* not a simple method, so we might end up returning guest null for
77-
* 'getExceptionMessage' which is OK in this case
78-
*/
79-
return true;
80-
}
62+
/*
63+
* Note that this might cause side effects, contradicting the contract of
64+
* `hasExceptionCause`. However, there's no way to both satisfy this and still ensure that
65+
* `hasExceptionCause` is true iff `getExceptionCause` returns a proper exception object. We
66+
* could be pessimistic and only return causes for exceptions which don't override
67+
* `getCause` but that causes usability issues.
68+
*/
69+
StaticObject cause = (StaticObject) meta.java_lang_Throwable_getCause.invokeDirectVirtual(object);
70+
return StaticObject.notNull(cause);
8171
}
8272

8373
@ExportMessage
8474
public static Object getExceptionCause(StaticObject object) throws UnsupportedMessageException {
8575
object.checkNotForeign();
86-
if (!hasExceptionCause(object)) {
76+
Meta meta = object.getKlass().getMeta();
77+
StaticObject cause = (StaticObject) meta.java_lang_Throwable_getCause.invokeDirectVirtual(object);
78+
if (StaticObject.isNull(cause)) {
8779
throw UnsupportedMessageException.create();
8880
}
89-
return object.getKlass().lookupMethod(Names.getCause, Signatures.Throwable).invokeDirect(object);
81+
return cause;
9082
}
9183

9284
@ExportMessage
9385
public static boolean hasExceptionMessage(StaticObject object) {
9486
object.checkNotForeign();
9587
Meta meta = object.getKlass().getMeta();
96-
Method resolvedMessageMethod = object.getKlass().lookupMethod(Names.getMessage, Signatures.String);
97-
if (resolvedMessageMethod == meta.java_lang_Throwable_getMessage) {
98-
// not overridden, then we can trust the field value
99-
return StaticObject.notNull(meta.java_lang_Throwable_detailMessage.getObject(object));
100-
} else if (resolvedMessageMethod.isInlinableGetter()) {
101-
// only call the method for a 'has' interop message if it's simple
102-
return StaticObject.notNull((StaticObject) resolvedMessageMethod.invokeDirect(object));
103-
} else {
104-
/*
105-
* not a simple method, so we might end up returning guest null for
106-
* 'getExceptionMessage' which is OK in this case
107-
*/
108-
return true;
109-
}
88+
/*
89+
* Note that this might cause side effects, contradicting the contract of
90+
* `hasExceptionMessage`. However, there's no way to both satisfy this and still ensure that
91+
* `hasExceptionMessage` is true iff `getExceptionMessage` returns a proper exception
92+
* object. We could be pessimistic and only return messages for exceptions which don't
93+
* override `getMessage` but that causes usability issues.
94+
*/
95+
StaticObject cause = (StaticObject) meta.java_lang_Throwable_getMessage.invokeDirectVirtual(object);
96+
return StaticObject.notNull(cause);
11097
}
11198

11299
@ExportMessage
113100
public static Object getExceptionMessage(StaticObject object) throws UnsupportedMessageException {
114101
object.checkNotForeign();
115-
if (!hasExceptionMessage(object)) {
102+
Meta meta = object.getKlass().getMeta();
103+
StaticObject message = (StaticObject) meta.java_lang_Throwable_getMessage.invokeDirectVirtual(object);
104+
if (StaticObject.isNull(message)) {
116105
throw UnsupportedMessageException.create();
117106
}
118-
return object.getKlass().lookupMethod(Names.getMessage, Signatures.String).invokeDirect(object);
107+
return message;
119108
}
120109
}

truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotExceptionImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ private void printStackTrace(PrintStreamOrWriter s) {
339339
int languageIdLength = 0; // java
340340
for (Object traceElement : getPolyglotStackTrace()) {
341341
PolyglotExceptionFrame frame = (PolyglotExceptionFrame) polyglot.getAPIAccess().getStackFrameReceiver(traceElement);
342-
if (!frame.isHostFrame()) {
342+
if (!frame.isHostFrame() && frame.languageId != null) {
343343
languageIdLength = Math.max(languageIdLength, frame.languageId.length());
344344
}
345345
}

truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/hotspot/HotSpotTruffleRuntime.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,7 @@ public boolean bypassedReservedOop() {
489489
* initialized. If bypassedReservedOop was called this also means that we skipped the
490490
* installed code for the JVMCI reserved oop accessor. This can happen if the debugger steps
491491
* over the code and invalidates any installed Java code stub, the HotSpot code cache
492-
* decides to clean up the the stub for the accessor method or this happened due to an
492+
* decides to clean up the stub for the accessor method or this happened due to an
493493
* initialization race condition. In all three cases the best we can do is to try to install
494494
* the stub code again even if this means repeated compilation and installation of this
495495
* method during debug-stepping. Unfortunately there is no known way to detect invalidation

0 commit comments

Comments
 (0)