Skip to content

Commit 965acfb

Browse files
Enforcing consistency of cached and uncached version of Atom.invokeMember message (#13492)
1 parent 062d798 commit 965acfb

File tree

4 files changed

+106
-7
lines changed

4 files changed

+106
-7
lines changed

engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/AtomConstructorTest.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import static org.junit.Assert.assertTrue;
77
import static org.junit.Assert.fail;
88

9+
import com.oracle.truffle.api.interop.InteropLibrary;
10+
import com.oracle.truffle.api.interop.UnknownIdentifierException;
911
import java.util.function.Function;
1012
import org.enso.common.MethodNames;
1113
import org.enso.interpreter.runtime.data.atom.Atom;
@@ -14,6 +16,7 @@
1416
import org.enso.interpreter.runtime.data.atom.StructsLibrary;
1517
import org.enso.interpreter.runtime.error.PanicException;
1618
import org.enso.test.utils.ContextUtils;
19+
import org.enso.test.utils.TestRootNode;
1720
import org.junit.ClassRule;
1821
import org.junit.Test;
1922

@@ -64,6 +67,30 @@ public void testAtomNewInstanceNode() {
6467

6568
assertAtomFactory("getUncached() with priming", uncachedFactory);
6669
assertLessArguments("getUncached() with priming", uncachedFactory);
70+
71+
var atom = uncachedFactory.apply(new Object[] {1, 2, 3});
72+
{
73+
var trn = new TestRootNode();
74+
var n = InteropLibrary.getFactory().createDispatched(10);
75+
n = trn.insert(n);
76+
assertInteropInvoke("Cached invokeMember node yields UnknownIdentifierException", atom, n);
77+
}
78+
{
79+
var n = InteropLibrary.getUncached();
80+
assertInteropInvoke("Uncached invokeMember node yields UnknownIdentifierException", atom, n);
81+
}
82+
}
83+
84+
private static void assertInteropInvoke(String msg, Atom atom, InteropLibrary node) {
85+
try {
86+
var res = node.invokeMember(atom, "toString");
87+
fail("Expecting exception, now a result: " + res);
88+
} catch (UnknownIdentifierException good) {
89+
assertEquals(
90+
"UnknownIdentifierException is expected", "toString", good.getUnknownIdentifier());
91+
} catch (Exception ex) {
92+
throw new AssertionError(msg + " got " + ex.getClass().getName(), ex);
93+
}
6794
}
6895

6996
private static void assertAtomFactory(String msg, Function<java.lang.Object[], Atom> factory) {
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package org.enso.interpreter.test.interop;
2+
3+
import static org.junit.Assert.assertEquals;
4+
import static org.junit.Assert.fail;
5+
6+
import com.oracle.truffle.api.interop.InteropLibrary;
7+
import com.oracle.truffle.api.interop.TruffleObject;
8+
import com.oracle.truffle.api.interop.UnknownIdentifierException;
9+
import com.oracle.truffle.api.interop.UnsupportedMessageException;
10+
import java.util.ArrayList;
11+
import org.enso.interpreter.test.ValuesGenerator;
12+
import org.enso.test.utils.ContextUtils;
13+
import org.enso.test.utils.TestRootNode;
14+
import org.junit.ClassRule;
15+
import org.junit.Test;
16+
import org.junit.runner.RunWith;
17+
import org.junit.runners.Parameterized;
18+
19+
@RunWith(Parameterized.class)
20+
public class InvokeMemberConsistencyTest {
21+
@ClassRule public static final ContextUtils CTX = ContextUtils.createDefault();
22+
23+
@Parameterized.Parameter(0)
24+
public Object raw;
25+
26+
@Parameterized.Parameters
27+
public static Object[][] allPossibleEnsoInterpreterValues() throws Exception {
28+
var g = ValuesGenerator.create(CTX);
29+
var data = new ArrayList<Object[]>();
30+
for (var value : g.allValues()) {
31+
var raw = CTX.unwrapValue(value);
32+
if (raw instanceof TruffleObject) {
33+
data.add(new Object[] {raw});
34+
}
35+
}
36+
return data.toArray(new Object[0][]);
37+
}
38+
39+
@Test
40+
public void unknownIdentifierUncached() {
41+
assertInteropInvoke("Uncached version", raw, InteropLibrary.getUncached());
42+
}
43+
44+
@Test
45+
public void unknownIdentifierCached() {
46+
47+
var trn = new TestRootNode();
48+
var n = InteropLibrary.getFactory().createDispatched(10);
49+
n = trn.insert(n);
50+
51+
assertInteropInvoke("Cached version", raw, n);
52+
}
53+
54+
private static void assertInteropInvoke(String msg, Object raw, InteropLibrary node) {
55+
try {
56+
var res = node.invokeMember(raw, "unknownIdentifier");
57+
fail("Expecting exception, now a result: " + res);
58+
} catch (UnknownIdentifierException good) {
59+
assertEquals(
60+
"UnknownIdentifierException is expected",
61+
"unknownIdentifier",
62+
good.getUnknownIdentifier());
63+
} catch (UnsupportedMessageException ex) {
64+
// that's OK
65+
} catch (Exception ex) {
66+
throw new AssertionError(msg + " got " + ex.getClass().getName() + " for " + raw, ex);
67+
}
68+
}
69+
}

engine/runtime/src/main/java/org/enso/interpreter/node/callable/IndirectInvokeMethodNode.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,10 @@ Object doFunctionalDispatch(
7474
@Shared("typesLib") @CachedLibrary(limit = "10") TypesLibrary dispatch,
7575
@Shared("methodResolverNode") @Cached MethodResolverNode methodResolverNode,
7676
@Shared("indirectInvokeFunctionNode") @Cached IndirectInvokeFunctionNode invokeFunctionNode) {
77-
Function function = methodResolverNode.expectNonNull(self, dispatch.getType(self), symbol);
77+
var function = methodResolverNode.executeResolution(dispatch.getType(self), symbol);
78+
if (function == null) {
79+
throw InvokeMethodNode.methodNotFound(this, true, symbol, self);
80+
}
7881
return invokeFunctionNode.execute(
7982
function,
8083
frame,

engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodNode.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ Object doFunctionalDispatchUncachedSymbol(
244244
if (imported != null) {
245245
return imported;
246246
}
247-
throw methodNotFound(symbol, self);
247+
throw methodNotFound(this, onBoundary, symbol, self);
248248
}
249249
CallArgumentInfo[] invokeFuncSchema = invokeFunctionNode.getSchema();
250250
var shouldPrependSyntheticSelfArg =
@@ -297,12 +297,12 @@ Object doFunctionalDispatchUncachedSymbol(
297297
return invokeFunctionNode.execute(function, frame, state, arguments);
298298
}
299299

300-
private PanicException methodNotFound(UnresolvedSymbol symbol, Object self)
301-
throws PanicException {
300+
static PanicException methodNotFound(
301+
Node where, boolean onBoundary, UnresolvedSymbol symbol, Object self) throws PanicException {
302302
var cause = onBoundary ? UnknownIdentifierException.create(symbol.getName()) : null;
303-
var ctx = EnsoContext.get(this);
303+
var ctx = EnsoContext.get(where);
304304
var payload = ctx.getBuiltins().error().makeNoSuchMethod(self, symbol);
305-
throw new PanicException(ctx, payload, cause, this);
305+
throw new PanicException(ctx, payload, cause, where);
306306
}
307307

308308
@Specialization
@@ -326,7 +326,7 @@ Object doMultiValue(
326326
}
327327
return invokeFunctionNode.execute(fnAndType.getLeft(), frame, state, arguments);
328328
}
329-
throw methodNotFound(symbol, self);
329+
throw methodNotFound(this, onBoundary, symbol, self);
330330
}
331331

332332
@Specialization

0 commit comments

Comments
 (0)