Skip to content

Commit d92bcc1

Browse files
committed
[GR-75874] Fix incorrect OtherContextException wrapper when exception returns to creator context.
PullRequest: graal/24167
2 parents 3b5da51 + 00d564f commit d92bcc1

4 files changed

Lines changed: 79 additions & 8 deletions

File tree

truffle/src/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/test/TruffleContextTest.java

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -614,6 +614,31 @@ public void testEvalInnerContextError() throws InteropException {
614614
innerContext.close();
615615
}
616616

617+
@Test
618+
public void testInnerContextRethrowsOuterContextError() throws InteropException {
619+
EvalContextTestException outerException = new EvalContextTestException();
620+
EvalContextRethrowingObject innerObject = new EvalContextRethrowingObject(outerException);
621+
AtomicReference<AbstractTruffleException> innerCaughtException = new AtomicReference<>();
622+
setupLanguageThatReturns(() -> innerObject);
623+
624+
TruffleContext innerContext = languageEnv.newInnerContextBuilder().initializeCreatorContext(true).build();
625+
outerException.expectedContext = languageEnv.getContext();
626+
innerObject.expectedContext = innerContext;
627+
innerObject.innerCaughtException = innerCaughtException;
628+
629+
Object result = innerContext.evalInternal(null, newTruffleSource());
630+
assertNotEquals("must be wrapped", innerObject, result);
631+
try {
632+
InteropLibrary.getUncached().execute(result, outerException);
633+
fail();
634+
} catch (AbstractTruffleException e) {
635+
assertSame(outerException, e);
636+
}
637+
assertNotNull(innerCaughtException.get());
638+
assertEquals(1, innerObject.executeCount);
639+
innerContext.close();
640+
}
641+
617642
@SuppressWarnings("serial")
618643
@ExportLibrary(InteropLibrary.class)
619644
static class EvalContextTestException extends AbstractTruffleException {
@@ -658,6 +683,45 @@ final Object execute(Object[] args) {
658683
}
659684
}
660685

686+
@ExportLibrary(InteropLibrary.class)
687+
static class EvalContextRethrowingObject implements TruffleObject {
688+
689+
final EvalContextTestException outerException;
690+
TruffleContext expectedContext;
691+
AtomicReference<AbstractTruffleException> innerCaughtException;
692+
int executeCount;
693+
694+
EvalContextRethrowingObject(EvalContextTestException outerException) {
695+
this.outerException = outerException;
696+
}
697+
698+
@ExportMessage
699+
@TruffleBoundary
700+
final boolean isExecutable() {
701+
assertTrue(expectedContext.isEntered());
702+
return true;
703+
}
704+
705+
@ExportMessage
706+
@TruffleBoundary
707+
final Object execute(Object[] args) {
708+
assertTrue(expectedContext.isEntered());
709+
assertEquals(1, args.length);
710+
executeCount++;
711+
try {
712+
InteropLibrary.getUncached().throwException(args[0]);
713+
fail();
714+
} catch (AbstractTruffleException e) {
715+
Assert.assertNotSame(outerException, e);
716+
innerCaughtException.set(e);
717+
throw e;
718+
} catch (InteropException e) {
719+
throw CompilerDirectives.shouldNotReachHere(e);
720+
}
721+
return null;
722+
}
723+
}
724+
661725
@Test
662726
public void testPublicEvalInnerContext() {
663727
// test that primitive values can just be passed through

truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleContext.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -1274,7 +1274,7 @@ public Builder onExited(Consumer<Integer> r) {
12741274

12751275
/**
12761276
* Specifies a {@link Runnable} that will be executed when some operation on the new context
1277-
* attenmpted while the context is already {@link TruffleContext#close()} closed}. However,
1277+
* attempted while the context is already {@link TruffleContext#close() closed}. However,
12781278
* the runnable will only be executed if the outer context is not closed.
12791279
*
12801280
* The purpose of the runnable is to allow throwing a custom guest exception before the

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

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -181,10 +181,16 @@ static Object sendImpl(Node node, PolyglotSharingLayer layer, Object receiver, M
181181
@TruffleBoundary
182182
static <T extends Throwable> RuntimeException migrateException(PolyglotContextImpl receiverContext, Throwable e, PolyglotContextImpl valueContext) throws T {
183183
if (e instanceof OtherContextException) {
184+
// Same logic as in migrateValue()
184185
OtherContextException other = (OtherContextException) e;
185186
if (other.receiverContext == receiverContext && other.delegateContext == valueContext) {
187+
// reuse wrapper it is already wrapped
186188
throw other;
189+
} else if (other.receiverContext == valueContext && other.delegateContext == receiverContext) {
190+
// unpack foreign value it belongs to that context
191+
throw (T) other.delegate;
187192
} else {
193+
// Preserve original context of the delegate when forwarding through third context
188194
throw new OtherContextException(receiverContext, other.delegate, other.delegateContext);
189195
}
190196
} else if (InteropLibrary.getUncached().isException(e)) {
@@ -338,13 +344,13 @@ static class OtherContextException extends AbstractTruffleException {
338344
}
339345

340346
@TruffleBoundary
341-
OtherContextException(PolyglotContextImpl thisContext, Exception delegate, PolyglotContextImpl delegateContext) {
347+
OtherContextException(PolyglotContextImpl receiverContext, Exception delegate, PolyglotContextImpl delegateContext) {
342348
super(delegate.getMessage());
343349
assert !(delegate instanceof OtherContextException) : "recursive host foreign value found";
344-
assert thisContext != null && delegateContext != null : "Must have associated contexts.";
345-
assert thisContext != delegateContext : "no need for foreign value if contexts match";
350+
assert receiverContext != null && delegateContext != null : "Must have associated contexts.";
351+
assert receiverContext != delegateContext : "no need for foreign value if contexts match";
346352
this.delegate = delegate;
347-
this.receiverContext = thisContext;
353+
this.receiverContext = receiverContext;
348354
this.delegateContext = delegateContext;
349355
}
350356

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1720,6 +1720,7 @@ Object migrateValue(Object value, PolyglotContextImpl valueContext) {
17201720
// guaranteed by migrateValue
17211721
assert value instanceof TruffleObject;
17221722
if (value instanceof OtherContextGuestObject) {
1723+
// Same logic as in migrateException()
17231724
OtherContextGuestObject otherValue = (OtherContextGuestObject) value;
17241725
if (otherValue.receiverContext == this && otherValue.delegateContext == valueContext) {
17251726
// reuse wrapper it is already wrapped

0 commit comments

Comments
 (0)