From a786ad2d0adb2b3eab1ac405a2937f85c219a40f Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Wed, 24 Apr 2024 16:50:41 +0200 Subject: [PATCH] Use InteropLibrary when accepting String values (#9773) While investigating #9749 a JavaScript call to `Polyglot.eval("enso", ....).eval_expression("id")` was made. It crashed as JavaScript isn't using `String` but `TruffleString` to represent strings. --- .../test/ForeignMethodInvokeTest.java | 29 +++++++++++-- .../org/enso/interpreter/runtime/Module.java | 41 +++++++++++++++---- 2 files changed, 58 insertions(+), 12 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/ForeignMethodInvokeTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/ForeignMethodInvokeTest.java index 91d9cee49f49..2d47b64b78bf 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/ForeignMethodInvokeTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/ForeignMethodInvokeTest.java @@ -3,6 +3,10 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.TruffleObject; +import com.oracle.truffle.api.library.ExportLibrary; +import com.oracle.truffle.api.library.ExportMessage; import java.util.concurrent.Executors; import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Value; @@ -44,7 +48,7 @@ public void testForeignFunctionParseFailure() { assertTrue("Invoking non-installed foreign function should recover", res.isException()); try { throw res.throwException(); - } catch (Exception e) { + } catch (RuntimeException e) { assertTrue( "Wrong error message", e.getMessage() @@ -65,7 +69,7 @@ public void testInteropWithJavaScript() throws Exception { """; var module = ctx.eval("enso", source); - var third = module.invokeMember("eval_expression", "third"); + var third = module.invokeMember("eval_expression", new AsString("third")); var res = third.execute(13); assertTrue("It is an array", res.hasArrayElements()); assertEquals(3, res.getArraySize()); @@ -102,7 +106,7 @@ public void testParallelInteropWithJavaScript() throws Exception { """; var module = ctx.eval("enso", source); - var third = module.invokeMember("eval_expression", "third"); + var third = module.invokeMember("eval_expression", new AsString("third")); var future = Executors.newSingleThreadExecutor() @@ -122,4 +126,23 @@ public void testParallelInteropWithJavaScript() throws Exception { assertTrue("It is an array2", res2.hasArrayElements()); assertEquals(12, res2.getArrayElement(2).asInt()); } + + @ExportLibrary(InteropLibrary.class) + static class AsString implements TruffleObject { + private final String value; + + private AsString(String value) { + this.value = value; + } + + @ExportMessage + boolean isString() { + return true; + } + + @ExportMessage + String asString() { + return value; + } + } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/Module.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/Module.java index 8d3bbd6ee31a..cf4781920489 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/Module.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/Module.java @@ -8,6 +8,7 @@ import com.oracle.truffle.api.interop.ArityException; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.UnknownIdentifierException; +import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.interop.UnsupportedTypeException; import com.oracle.truffle.api.library.ExportLibrary; import com.oracle.truffle.api.library.ExportMessage; @@ -567,8 +568,12 @@ private static Function getMethod(ModuleScope scope, Object[] args) } private static Type getType(ModuleScope scope, Object[] args) - throws ArityException, UnsupportedTypeException { - String name = Types.extractArguments(args, String.class); + throws ArityException, UnsupportedTypeException, UnsupportedMessageException { + var iop = InteropLibrary.getUncached(); + if (!iop.isString(args[0])) { + throw UnsupportedTypeException.create(args, "First argument must be a string"); + } + String name = iop.asString(args[0]); return scope.getTypes().get(name); } @@ -586,15 +591,23 @@ private static Module reparse(Module module, Object[] args, EnsoContext context) } private static Module setSource(Module module, Object[] args, EnsoContext context) - throws ArityException, UnsupportedTypeException { - String source = Types.extractArguments(args, String.class); + throws ArityException, UnsupportedTypeException, UnsupportedMessageException { + var iop = InteropLibrary.getUncached(); + if (!iop.isString(args[0])) { + throw UnsupportedTypeException.create(args, "First argument must be a string"); + } + String source = iop.asString(args[0]); module.setLiteralSource(source); return module; } private static Module setSourceFile(Module module, Object[] args, EnsoContext context) - throws ArityException, UnsupportedTypeException { - String file = Types.extractArguments(args, String.class); + throws ArityException, UnsupportedTypeException, UnsupportedMessageException { + var iop = InteropLibrary.getUncached(); + if (!iop.isString(args[0])) { + throw UnsupportedTypeException.create(args, "First argument must be a string"); + } + String file = iop.asString(args[0]); module.setSourceFile(context.getTruffleFile(new File(file))); return module; } @@ -606,8 +619,15 @@ private static Type getAssociatedType(ModuleScope scope, Object[] args) throws A private static Object evalExpression( ModuleScope scope, Object[] args, EnsoContext context, CallOptimiserNode callOptimiserNode) - throws ArityException, UnsupportedTypeException { - String expr = Types.extractArguments(args, String.class); + throws ArityException, UnsupportedTypeException, UnsupportedMessageException { + if (args.length != 1) { + throw ArityException.create(1, 1, args.length); + } + var iop = InteropLibrary.getUncached(); + if (!iop.isString(args[0])) { + throw UnsupportedTypeException.create(args, "First argument must be a string"); + } + String expr = iop.asString(args[0]); Builtins builtins = context.getBuiltins(); BuiltinFunction eval = builtins @@ -641,7 +661,10 @@ static Object doInvoke( String member, Object[] arguments, @Cached LoopingCallOptimiserNode callOptimiserNode) - throws UnknownIdentifierException, ArityException, UnsupportedTypeException { + throws UnknownIdentifierException, + ArityException, + UnsupportedTypeException, + UnsupportedMessageException { EnsoContext context = EnsoContext.get(null); ModuleScope scope; switch (member) {