From c78617db61a4857e34c867732d145ff88a6654b5 Mon Sep 17 00:00:00 2001 From: Tomas Stupka <tomas.stupka@oracle.com> Date: Fri, 2 Jun 2017 17:34:19 +0200 Subject: [PATCH] extend new function to also create foreign java objects --- .../r/nodes/builtin/base/BasePackage.java | 1 + .../r/nodes/builtin/fastr/FastRInterop.java | 48 +++++++++++++++---- .../builtin/methods/R/methods_overrides.R | 16 +++++++ .../r/test/library/fastr/TestJavaInterop.java | 24 +++++++++- 4 files changed, 80 insertions(+), 9 deletions(-) diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java index 2030c3ecb7..9d55e2b44f 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java @@ -376,6 +376,7 @@ public class BasePackage extends RBuiltinPackage { add(FastRInterop.InteropNew.class, FastRInteropFactory.InteropNewNodeGen::create); add(FastRInterop.IsNull.class, FastRInteropFactory.IsNullNodeGen::create); add(FastRInterop.IsExecutable.class, FastRInteropFactory.IsExecutableNodeGen::create); + add(FastRInterop.IsExternal.class, FastRInteropFactory.IsExternalNodeGen::create); add(FastRInterop.JavaClass.class, FastRInteropFactory.JavaClassNodeGen::create); add(FastRInterop.IsJavaArray.class, FastRInteropFactory.IsJavaArrayNodeGen::create); add(FastRInterop.NewJavaArray.class, FastRInteropFactory.NewJavaArrayNodeGen::create); diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java index 0857f0b531..4c2f44c654 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java @@ -53,6 +53,7 @@ import com.oracle.truffle.api.interop.java.JavaInterop; import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.profiles.ConditionProfile; +import com.oracle.truffle.api.profiles.ValueProfile; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.source.Source.Builder; import com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef; @@ -92,6 +93,12 @@ import java.util.logging.Logger; public class FastRInterop { + private static boolean isTesting = false; + + public static void testingMode() { + isTesting = true; + } + @RBuiltin(name = ".fastr.interop.eval", visibility = OFF, kind = PRIMITIVE, parameterNames = {"mimeType", "source"}, behavior = COMPLEX) public abstract static class Eval extends RBuiltinNode.Arg2 { @@ -420,26 +427,31 @@ public class FastRInterop { } } - @RBuiltin(name = ".fastr.java.class", visibility = ON, kind = PRIMITIVE, parameterNames = {"class"}, behavior = COMPLEX) - public abstract static class JavaClass extends RBuiltinNode.Arg1 { + @RBuiltin(name = ".fastr.java.class", visibility = ON, kind = PRIMITIVE, parameterNames = {"class", "silent"}, behavior = COMPLEX) + public abstract static class JavaClass extends RBuiltinNode.Arg2 { static { Casts casts = new Casts(JavaClass.class); casts.arg("class").mustBe(stringValue()).asStringVector().mustBe(Predef.singleElement()).findFirst(); + casts.arg("silent").mapMissing(Predef.constant(RRuntime.LOGICAL_FALSE)).mustBe(logicalValue().or(Predef.nullValue())).asLogicalVector().mustBe(singleElement()).findFirst().mustBe( + notLogicalNA()).map(Predef.toBoolean()); } @Specialization @TruffleBoundary - public TruffleObject javaClass(String clazz) { + public TruffleObject javaClass(String clazz, boolean silent) { try { - return JavaInterop.asTruffleObject(Class.forName(clazz)); + return JavaInterop.asTruffleObject(Class.forName(clazz.replaceAll("/", "."))); } catch (ClassNotFoundException | SecurityException | IllegalArgumentException e) { + if (silent) { + return RNull.instance; + } throw error(RError.Message.GENERIC, "error while accessing Java class: " + e.getMessage()); } } } - @ImportStatic({Message.class, RRuntime.class}) + @ImportStatic({RRuntime.class}) @RBuiltin(name = ".fastr.java.isArray", visibility = ON, kind = PRIMITIVE, parameterNames = {"obj"}, behavior = COMPLEX) public abstract static class IsJavaArray extends RBuiltinNode.Arg1 { @@ -506,7 +518,7 @@ public class FastRInterop { Casts casts = new Casts(ToJavaArray.class); casts.arg("x").mustNotBeMissing(); casts.arg("className").allowMissing().mustBe(stringValue()).asStringVector().mustBe(Predef.singleElement()).findFirst(); - casts.arg("flat").mapMissing(Predef.constant(RRuntime.asLogical(true))).mustBe(logicalValue().or(Predef.nullValue())).asLogicalVector().mustBe(singleElement()).findFirst().mustBe( + casts.arg("flat").mapMissing(Predef.constant(RRuntime.LOGICAL_TRUE)).mustBe(logicalValue().or(Predef.nullValue())).asLogicalVector().mustBe(singleElement()).findFirst().mustBe( notLogicalNA()).map(Predef.toBoolean()); } @@ -796,8 +808,9 @@ public class FastRInterop { } Object result = ForeignAccess.sendNew(sendNew, clazz, argValues); return RRuntime.java2R(result); - } catch (SecurityException | IllegalArgumentException | UnsupportedTypeException | ArityException | UnsupportedMessageException e) { - throw error(RError.Message.GENERIC, "error during Java object instantiation: " + e.getMessage()); + } catch (IllegalStateException | SecurityException | IllegalArgumentException | UnsupportedTypeException | ArityException | UnsupportedMessageException e) { + String msg = isTesting ? "error during Java object instantiation" : "error during Java object instantiation: " + e.getMessage(); + throw error(RError.Message.GENERIC, msg); } } @@ -806,4 +819,23 @@ public class FastRInterop { throw error(RError.Message.GENERIC, "interop object needed as receiver of NEW message"); } } + + @ImportStatic(RRuntime.class) + @RBuiltin(name = ".fastr.interop.isExternal", visibility = ON, kind = PRIMITIVE, parameterNames = {"obj"}, behavior = COMPLEX) + public abstract static class IsExternal extends RBuiltinNode.Arg1 { + + static { + Casts.noCasts(IsExternal.class); + } + + @Specialization(guards = {"isForeignObject(obj)"}) + public byte isExternal(TruffleObject obj) { + return RRuntime.LOGICAL_TRUE; + } + + @Fallback + public byte isExternal(Object obj) { + return RRuntime.LOGICAL_FALSE; + } + } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/methods/R/methods_overrides.R b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/methods/R/methods_overrides.R index 99c947a62d..698ae85bc7 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/methods/R/methods_overrides.R +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/methods/R/methods_overrides.R @@ -23,4 +23,20 @@ eval(expression({ # this function is replaced with a primitive because it is expected to # modify its argument in-place, which can clash with argument refcount handling `slot<-` <- .fastr.methods.slotassign + +new <- function (Class, ...) { + if(is.character(Class)) { + javaClass <- .fastr.java.class(Class, silent=TRUE) + if(!is.null(javaClass)) { + Class <- javaClass + } + } + if(.fastr.interop.isExternal(Class)) { + .fastr.interop.new(Class, ...) + } else { + ClassDef <- getClass(Class, where = topenv(parent.frame())) + value <- .Call(C_new_object, ClassDef) + initialize(value, ...) + } +} }), asNamespace("methods")) diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/fastr/TestJavaInterop.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/fastr/TestJavaInterop.java index dd2d584e5b..557545ba6e 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/fastr/TestJavaInterop.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/fastr/TestJavaInterop.java @@ -22,6 +22,7 @@ */ package com.oracle.truffle.r.test.library.fastr; +import com.oracle.truffle.r.nodes.builtin.fastr.FastRInterop; import com.oracle.truffle.r.runtime.RType; import org.junit.Test; @@ -33,11 +34,17 @@ import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import org.junit.Assert; +import org.junit.Before; public class TestJavaInterop extends TestBase { private static final String TEST_CLASS = TestClass.class.getName(); + @Before + public void testInit() { + FastRInterop.testingMode(); + } + @Test public void testToByte() { assertEvalFastR("v <- .fastr.interop.toByte(1L); v;", "1"); @@ -191,8 +198,9 @@ public class TestJavaInterop extends TestBase { } @Test - public void testNew() { + public void testInteroptNew() { assertEvalFastR("tc <- .fastr.java.class('" + Boolean.class.getName() + "'); t <- .fastr.interop.new(tc, TRUE); t", "TRUE"); + assertEvalFastR("tc <- .fastr.java.class('java/lang/Boolean'); t <- new(tc, TRUE); t", "TRUE"); assertEvalFastR("tc <- .fastr.java.class('" + Byte.class.getName() + "'); t <- .fastr.interop.new(tc, .fastr.interop.toByte(1)); t", "1"); assertEvalFastR("tc <- .fastr.java.class('" + Character.class.getName() + "'); t <- .fastr.interop.new(tc, .fastr.interop.toChar(97)); t", "'a'"); assertEvalFastR("tc <- .fastr.java.class('" + Double.class.getName() + "'); t <- .fastr.interop.new(tc, 1.1); t", "1.1"); @@ -204,6 +212,20 @@ public class TestJavaInterop extends TestBase { assertEvalFastR("tc <- .fastr.java.class('" + TestNullClass.class.getName() + "'); t <- .fastr.interop.new(tc, NULL); class(t)", "'" + RType.TruffleObject.getName() + "'"); } + @Test + public void testNewWithJavaClass() { + assertEvalFastR("tc <- .fastr.java.class('" + Boolean.class.getName() + "'); to <- new(tc, TRUE); to", "TRUE"); + assertEvalFastR("tc <- .fastr.java.class('" + TEST_CLASS + "'); to <- new(tc); to$fieldInteger", getRValue(Integer.MAX_VALUE)); + + assertEvalFastR("to <- new('" + Boolean.class.getName() + "', TRUE); to", "TRUE"); + assertEvalFastR("to <- new('java/lang/Boolean', TRUE); to", "TRUE"); + assertEvalFastR("to <- new('" + TEST_CLASS + "'); to$fieldStaticInteger", getRValue(Integer.MAX_VALUE)); + + assertEvalFastR("to <- new('" + TEST_CLASS + "'); new(to)", "cat('Error in .fastr.interop.new(Class, ...) : ', '\n', ' error during Java object instantiation\n', sep='')"); + + assertEvalFastR("to <- new('__bogus_class_name__');", "cat('Error in getClass(Class, where = topenv(parent.frame())) : ', '\n', ' “__bogus_class_name__†is not a defined class\n', sep='')"); + } + @Test public void testCombineInteropTypes() { assertEvalFastR("class(c(.fastr.interop.toByte(123)))", "'interopt.byte'"); -- GitLab