From a8659ed6bb5b1f30ab3d6ffa1581f09207abf0e1 Mon Sep 17 00:00:00 2001
From: Florian Angerer <florian.angerer@oracle.com>
Date: Tue, 27 Jun 2017 17:36:31 +0200
Subject: [PATCH] Added builtin for adding classpath entries for Java interop.

---
 .../r/nodes/builtin/base/BasePackage.java     |  1 +
 .../r/nodes/builtin/fastr/FastRInterop.java   | 30 ++++++++++++++++++-
 .../truffle/r/runtime/context/RContext.java   | 19 ++++++++++++
 3 files changed, 49 insertions(+), 1 deletion(-)

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 6720e2f634..ff95db1dd7 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
@@ -420,6 +420,7 @@ public class BasePackage extends RBuiltinPackage {
         add(FastRInterop.IsExternal.class, FastRInteropFactory.IsExternalNodeGen::create);
         add(FastRInterop.JavaClass.class, FastRInteropFactory.JavaClassNodeGen::create);
         add(FastRInterop.JavaClassName.class, FastRInteropFactory.JavaClassNameNodeGen::create);
+        add(FastRInterop.JavaAddClasspathEntry.class, FastRInteropFactory.JavaAddClasspathEntryNodeGen::create);
         add(FastRInterop.IsForeignArray.class, FastRInteropFactory.IsForeignArrayNodeGen::create);
         add(FastRInterop.NewJavaArray.class, FastRInteropFactory.NewJavaArrayNodeGen::create);
         add(FastRInterop.ToJavaArray.class, FastRInteropFactory.ToJavaArrayNodeGen::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 dfd0f56b4b..4aeffb07da 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
@@ -39,6 +39,7 @@ import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 import java.io.File;
 import java.io.IOException;
 import java.lang.reflect.Array;
+import java.net.MalformedURLException;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -440,7 +441,8 @@ public class FastRInterop {
         @TruffleBoundary
         public TruffleObject javaClass(String clazz, boolean silent) {
             try {
-                return JavaInterop.asTruffleObject(Class.forName(clazz.replaceAll("/", ".")));
+                ClassLoader interopClassLoader = RContext.getInstance().getInteropClassLoader();
+                return JavaInterop.asTruffleObject(interopClassLoader.loadClass(clazz.replaceAll("/", ".")));
             } catch (ClassNotFoundException | SecurityException | IllegalArgumentException e) {
                 if (silent) {
                     return RNull.instance;
@@ -450,6 +452,32 @@ public class FastRInterop {
         }
     }
 
+    @RBuiltin(name = ".fastr.java.addClasspathEntry", visibility = OFF, kind = PRIMITIVE, parameterNames = {"entry", "silent"}, behavior = COMPLEX)
+    public abstract static class JavaAddClasspathEntry extends RBuiltinNode.Arg2 {
+
+        static {
+            Casts casts = new Casts(JavaAddClasspathEntry.class);
+            casts.arg("entry").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 entry, boolean silent) {
+            try {
+                RContext ctx = RContext.getInstance();
+                ctx.addInteropClasspathEntry(entry);
+                return RNull.instance;
+            } catch (MalformedURLException e) {
+                if (silent) {
+                    return RNull.instance;
+                }
+                throw error(RError.Message.GENERIC, "error while adding classpath entry: " + e.getMessage());
+            }
+        }
+    }
+
     @ImportStatic({RRuntime.class})
     @RBuiltin(name = "java.class", visibility = ON, kind = PRIMITIVE, parameterNames = {"class"}, behavior = COMPLEX)
     public abstract static class JavaClassName extends RBuiltinNode.Arg1 {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java
index b8f37f4b22..801a51bd97 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java
@@ -25,7 +25,11 @@ package com.oracle.truffle.r.runtime.context;
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
 import java.io.Closeable;
+import java.io.File;
 import java.lang.ref.WeakReference;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
 import java.nio.file.Path;
 import java.util.EnumSet;
 import java.util.HashMap;
@@ -248,6 +252,9 @@ public final class RContext implements RTruffleObject {
 
     private PrimitiveMethodsInfo primitiveMethodsInfo;
 
+    /** Class loader for Java interop. */
+    private ClassLoader interopClassLoader = getClass().getClassLoader();
+
     /**
      * Set to {@code true} when in embedded mode to allow other parts of the system to determine
      * whether embedded mode is in effect, <b>before</b> the initial context is created.
@@ -809,4 +816,16 @@ public final class RContext implements RTruffleObject {
         }
     };
 
+    public ClassLoader getInteropClassLoader() {
+        return interopClassLoader;
+    }
+
+    /**
+     * Adds an entry to the Java interop class loader. This will effectively create a new class
+     * loader with the previous one as parent.
+     */
+    public void addInteropClasspathEntry(String entry) throws MalformedURLException {
+        URL url = new File(entry).toURI().toURL();
+        interopClassLoader = URLClassLoader.newInstance(new URL[]{url}, interopClassLoader);
+    }
 }
-- 
GitLab