From ab008d05e862238394b971639488380410017fae Mon Sep 17 00:00:00 2001
From: Mick Jordan <mick.jordan@oracle.com>
Date: Thu, 8 Dec 2016 15:34:43 -0800
Subject: [PATCH] more efficient implementation of RForeignAccessfactory

---
 .../r/engine/interop/DLLDotSymbolMR.java      |  42 +++
 .../truffle/r/engine/interop/DLLInfoMR.java   |  42 +++
 .../r/engine/interop/RExternalPtrMR.java      |  42 +++
 .../interop/RForeignAccessFactoryImpl.java    | 243 ++++++++++++++----
 .../truffle/r/engine/interop/RSymbolMR.java   |  42 +++
 .../truffle/r/runtime/context/RContext.java   |   8 +-
 .../context/RForeignAccessFactory.java        |   8 +-
 7 files changed, 379 insertions(+), 48 deletions(-)
 create mode 100644 com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/DLLDotSymbolMR.java
 create mode 100644 com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/DLLInfoMR.java
 create mode 100644 com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RExternalPtrMR.java
 create mode 100644 com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RSymbolMR.java

diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/DLLDotSymbolMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/DLLDotSymbolMR.java
new file mode 100644
index 0000000000..3342f4a737
--- /dev/null
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/DLLDotSymbolMR.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.engine.interop;
+
+import com.oracle.truffle.api.interop.CanResolve;
+import com.oracle.truffle.api.interop.MessageResolution;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.engine.TruffleRLanguage;
+import com.oracle.truffle.r.runtime.ffi.DLL;
+
+@MessageResolution(receiverType = DLL.DotSymbol.class, language = TruffleRLanguage.class)
+public class DLLDotSymbolMR {
+    @CanResolve
+    public abstract static class DotSymbolCheck extends Node {
+
+        protected static boolean test(TruffleObject receiver) {
+            return receiver instanceof DLL.DotSymbol;
+        }
+    }
+
+}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/DLLInfoMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/DLLInfoMR.java
new file mode 100644
index 0000000000..92a4118c05
--- /dev/null
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/DLLInfoMR.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.engine.interop;
+
+import com.oracle.truffle.api.interop.CanResolve;
+import com.oracle.truffle.api.interop.MessageResolution;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.engine.TruffleRLanguage;
+import com.oracle.truffle.r.runtime.ffi.DLL;
+
+@MessageResolution(receiverType = DLL.DLLInfo.class, language = TruffleRLanguage.class)
+public class DLLInfoMR {
+    @CanResolve
+    public abstract static class DLLInfolCheck extends Node {
+
+        protected static boolean test(TruffleObject receiver) {
+            return receiver instanceof DLL.DLLInfo;
+        }
+    }
+
+}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RExternalPtrMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RExternalPtrMR.java
new file mode 100644
index 0000000000..3d21bccdf2
--- /dev/null
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RExternalPtrMR.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2016, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.engine.interop;
+
+import com.oracle.truffle.api.interop.CanResolve;
+import com.oracle.truffle.api.interop.MessageResolution;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.engine.TruffleRLanguage;
+import com.oracle.truffle.r.runtime.data.RExternalPtr;
+
+@MessageResolution(receiverType = RExternalPtr.class, language = TruffleRLanguage.class)
+public class RExternalPtrMR {
+    @CanResolve
+    public abstract static class RExternalPtrCheck extends Node {
+
+        protected static boolean test(TruffleObject receiver) {
+            return receiver instanceof RExternalPtr;
+        }
+    }
+
+}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RForeignAccessFactoryImpl.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RForeignAccessFactoryImpl.java
index 03f73455c6..539084c106 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RForeignAccessFactoryImpl.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RForeignAccessFactoryImpl.java
@@ -22,88 +22,243 @@
  */
 package com.oracle.truffle.r.engine.interop;
 
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.Assumption;
+import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.TruffleLanguage;
 import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.MessageResolution;
+import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.vm.PolyglotEngine;
 import com.oracle.truffle.r.engine.TruffleRLanguage;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.context.RForeignAccessFactory;
+import com.oracle.truffle.r.runtime.data.RComplex;
+import com.oracle.truffle.r.runtime.data.RComplexVector;
+import com.oracle.truffle.r.runtime.data.RDoubleSequence;
+import com.oracle.truffle.r.runtime.data.RDoubleVector;
+import com.oracle.truffle.r.runtime.data.RExternalPtr;
 import com.oracle.truffle.r.runtime.data.RFunction;
+import com.oracle.truffle.r.runtime.data.RIntSequence;
+import com.oracle.truffle.r.runtime.data.RIntVector;
 import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RLogicalVector;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RPairList;
+import com.oracle.truffle.r.runtime.data.RRaw;
+import com.oracle.truffle.r.runtime.data.RRawVector;
+import com.oracle.truffle.r.runtime.data.RStringVector;
+import com.oracle.truffle.r.runtime.data.RSymbol;
 import com.oracle.truffle.r.runtime.data.RTruffleObject;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
+import com.oracle.truffle.r.runtime.ffi.DLL;
+import com.oracle.truffle.r.runtime.ffi.DLL.DLLInfo;
+import com.oracle.truffle.r.runtime.ffi.DLL.DotSymbol;
 
+/**
+ * A {@link ForeignAccess} instance captures the {@link Thread} that creates it and all uses are
+ * checked against the current thread. Therefore, in a world with multiple {@link PolyglotEngine}s,
+ * aka multiple {@link Thread} and {@link RContext} instances, it is not possible to use a simple
+ * global constant value for the {@link ForeignAccess} instance that could be associated directly
+ * with the {@link TruffleObject} class.
+ *
+ * This factory provides a generic solution for all FastR types (all of which are
+ * {@link TruffleObject}s), at some cost in performance.
+ *
+ * The REALLY bad news is that we cannot use {@link RContext} to store the state because, although
+ * that should be possible, at the time the call to {@link #getForeignAccess(RTruffleObject)}
+ * happens the {@link RContext} may not have been associated with a thread, so
+ * {@link RContext#getInstance()} will fail. In short the mapping has to be established using
+ * {@link Thread} not {@link RContext} and there is no call that informs us ahead of a call to
+ * {@link #getForeignAccess} that a new thread is in play. We use a Truffle {@link Assumption} to
+ * provide a fast path in the normal, single-threaded, case.
+ *
+ * For most types we use the {@link MessageResolution} facility to automatically generate the
+ * factory for creating the {@link ForeignAccess} instance. The exceptions are the (many) subclasses
+ * of {@link RAbstractVector} as these have the same handling but the generator cannot handle
+ * abstract classes.
+ *
+ */
 public final class RForeignAccessFactoryImpl implements RForeignAccessFactory {
+    /**
+     * The subset of the full set of types that are supported for foreign access. N.B. This list
+     * includes types that are not expected to travel between high-level languages, but may travel
+     * into the native world (when implemented in Truffle).
+     */
+    private static final Class<?>[] FOREIGN_CLASSES = new Class<?>[]{
+                    RRaw.class, RComplex.class, RIntSequence.class,
+                    RDoubleSequence.class, RIntVector.class, RDoubleVector.class,
+                    RRawVector.class, RComplexVector.class, RStringVector.class, RLogicalVector.class,
+                    RFunction.class, RNull.class, REnvironment.class,
+                    RList.class, RSymbol.class,
+                    RPairList.class, RExternalPtr.class,
+                    DLLInfo.class, DotSymbol.class};
+
+    private static final class ForeignAccessState {
+
+        private static final class TableEntry {
+            private final Class<? extends RTruffleObject> clazz; // for sanity check
+            private final ForeignAccess foreignAccess;
+
+            private TableEntry(Class<? extends RTruffleObject> clazz, ForeignAccess foreignAccess) {
+                this.clazz = clazz;
+                this.foreignAccess = foreignAccess;
+            }
+        }
 
-    private static final class TableEntry {
-        private final Class<? extends RTruffleObject> clazz;
-        private final ForeignAccess foreignAccess;
         /**
-         * {@link PolyglotEngine} checks the thread on a {@link ForeignAccess}.
+         * Table with a unique index for each class in {@link #FOREIGN_CLASSES}.
+         */
+        private static Class<?>[] classTable;
+        /**
+         * Isomorphic to {@link #classTable} but contains the {@link ForeignAccess} instance.
+         */
+        private final TableEntry[] table;
+        /**
+         * Mask to efficiently compute the table index.
+         */
+        @CompilationFinal private static int tableMask;
+        /**
+         * The thread that this state is generated for.
          */
         private final Thread thread;
 
-        private TableEntry(Class<? extends RTruffleObject> clazz, ForeignAccess foreignAccess) {
-            this.clazz = clazz;
+        private ForeignAccessState() {
             this.thread = Thread.currentThread();
-            this.foreignAccess = foreignAccess;
+            table = new TableEntry[classTable.length];
+            for (int i = 0; i < table.length; i++) {
+                @SuppressWarnings("unchecked")
+                Class<? extends RTruffleObject> checkedClass = (Class<? extends RTruffleObject>) classTable[i];
+                if (checkedClass != null) {
+                    table[i] = new TableEntry(checkedClass, createForeignAccess(checkedClass));
+                }
+            }
         }
-    }
 
-    TableEntry[] table = new TableEntry[32];
-    int tableIndex;
+        private ForeignAccess get(RTruffleObject obj) {
+            Class<? extends RTruffleObject> clazz = obj.getClass();
+            return get(clazz);
+        }
 
-    @Override
-    public ForeignAccess getForeignAccess(RTruffleObject obj) {
-        return get(obj);
-    }
+        private ForeignAccess get(Class<? extends RTruffleObject> clazz) {
+            int index = System.identityHashCode(clazz) & tableMask;
+            assert table[index].clazz == clazz;
+            return table[index].foreignAccess;
+        }
+
+        static {
+            generatePrototypeTable();
+        }
 
-    private synchronized ForeignAccess get(RTruffleObject obj) {
-        Class<? extends RTruffleObject> objclazz = obj.getClass();
-        Thread thread = Thread.currentThread();
-        for (int i = 0; i < tableIndex; i++) {
-            TableEntry te = table[i];
-            if (te.clazz == objclazz && te.thread == thread) {
-                return te.foreignAccess;
+        /**
+         * Create a table that has a unique index for every member of {@link #FOREIGN_CLASSES} for
+         * efficient access. Since {@link System#identityHashCode(Object)} can vary from run to run,
+         * the size of the table may vary, but is typically in the range 64-4096.
+         */
+        private static void generatePrototypeTable() {
+            int ts = 32;
+            while (true) {
+                classTable = new Class<?>[ts];
+                boolean collision = false;
+                for (int i = 0; i < FOREIGN_CLASSES.length; i++) {
+                    Class<?> clazz = FOREIGN_CLASSES[i];
+                    int h = System.identityHashCode(clazz);
+                    int hc = h % ts;
+                    if (classTable[hc] == null) {
+                        classTable[hc] = clazz;
+                    } else {
+                        collision = true;
+                        break;
+                    }
+                }
+                if (!collision) {
+                    break;
+                } else {
+                    ts = ts * 2;
+                }
             }
+            tableMask = ts - 1;
         }
-        return createForeignAccess(objclazz);
-    }
 
-    @TruffleBoundary
-    private ForeignAccess createForeignAccess(Class<? extends RTruffleObject> clazz) {
-        ForeignAccess foreignAccess = null;
-        String name = clazz.getSimpleName();
-        if (RNull.class.isAssignableFrom(clazz)) {
-            foreignAccess = RNullMRForeign.createAccess();
-        } else if (RList.class.isAssignableFrom(clazz)) {
-            foreignAccess = RListMRForeign.createAccess();
-        } else if (REnvironment.class.isAssignableFrom(clazz)) {
-            foreignAccess = REnvironmentMRForeign.createAccess();
-        } else if (RPairList.class.isAssignableFrom(clazz)) {
-            foreignAccess = RPairListMRForeign.createAccess();
-        } else if (RFunction.class.isAssignableFrom(clazz)) {
-            foreignAccess = RFunctionMRForeign.createAccess();
-        } else {
-            if (RAbstractVector.class.isAssignableFrom(clazz)) {
-                foreignAccess = ForeignAccess.create(RAbstractVector.class, new RAbstractVectorAccessFactory());
+        @TruffleBoundary
+        private static ForeignAccess createForeignAccess(Class<? extends RTruffleObject> clazz) {
+            ForeignAccess foreignAccess = null;
+            String name = clazz.getSimpleName();
+            if (RNull.class.isAssignableFrom(clazz)) {
+                foreignAccess = RNullMRForeign.createAccess();
+            } else if (RList.class.isAssignableFrom(clazz)) {
+                foreignAccess = RListMRForeign.createAccess();
+            } else if (REnvironment.class.isAssignableFrom(clazz)) {
+                foreignAccess = REnvironmentMRForeign.createAccess();
+            } else if (RPairList.class.isAssignableFrom(clazz)) {
+                foreignAccess = RPairListMRForeign.createAccess();
+            } else if (RFunction.class.isAssignableFrom(clazz)) {
+                foreignAccess = RFunctionMRForeign.createAccess();
+            } else if (DLL.DLLInfo.class.isAssignableFrom(clazz)) {
+                foreignAccess = DLLInfoMRForeign.createAccess();
+            } else if (DLL.DotSymbol.class.isAssignableFrom(clazz)) {
+                foreignAccess = DLLDotSymbolMRForeign.createAccess();
+            } else if (RSymbol.class.isAssignableFrom(clazz)) {
+                foreignAccess = RSymbolMRForeign.createAccess();
+            } else if (RExternalPtr.class.isAssignableFrom(clazz)) {
+                foreignAccess = RExternalPtrMRForeign.createAccess();
             } else {
-                throw RInternalError.unimplemented("foreignAccess: " + name);
+                if (RAbstractVector.class.isAssignableFrom(clazz)) {
+                    foreignAccess = ForeignAccess.create(RAbstractVector.class, new RAbstractVectorAccessFactory());
+                } else {
+                    throw RInternalError.unimplemented("foreignAccess: " + name);
+                }
             }
+            return foreignAccess;
         }
-        TableEntry te = new TableEntry(clazz, foreignAccess);
-        table[tableIndex++] = te;
-        return te.foreignAccess;
 
     }
 
+    /**
+     * In normal execution, there is only one thread.
+     */
+    private static final Assumption singleStateAssumption = Truffle.getRuntime().createAssumption("single ForeignAccessState");
+    /**
+     * In case of multiple threads, the per-thread state.
+     */
+    private static final ThreadLocal<ForeignAccessState> threadLocalState = new ThreadLocal<>();
+    /**
+     * In single thread mode, a fast path to the state. In multi-thread mode set to {@code null}.
+     */
+    @CompilationFinal private static ForeignAccessState singleForeignAccessState;
+
+    @Override
+    public ForeignAccess getForeignAccess(RTruffleObject obj) {
+        ForeignAccessState foreignAccessState;
+        if (singleStateAssumption.isValid()) {
+            foreignAccessState = singleForeignAccessState;
+            if (foreignAccessState == null) {
+                // very first call
+                foreignAccessState = new ForeignAccessState();
+                singleForeignAccessState = foreignAccessState;
+                threadLocalState.set(foreignAccessState);
+            } else {
+                // check thread
+                if (Thread.currentThread() != foreignAccessState.thread) {
+                    singleStateAssumption.invalidate();
+                    singleForeignAccessState = null;
+                    foreignAccessState = new ForeignAccessState();
+                }
+            }
+        } else {
+            // use the threadLocal
+            foreignAccessState = threadLocalState.get();
+        }
+        ForeignAccess result = foreignAccessState.get(obj);
+        return result;
+    }
+
     @Override
     public Class<? extends TruffleLanguage<RContext>> getTruffleLanguage() {
         return TruffleRLanguage.class;
     }
+
 }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RSymbolMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RSymbolMR.java
new file mode 100644
index 0000000000..2eb3cb9661
--- /dev/null
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RSymbolMR.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2016, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.engine.interop;
+
+import com.oracle.truffle.api.interop.CanResolve;
+import com.oracle.truffle.api.interop.MessageResolution;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.engine.TruffleRLanguage;
+import com.oracle.truffle.r.runtime.data.RSymbol;
+
+@MessageResolution(receiverType = RSymbol.class, language = TruffleRLanguage.class)
+public class RSymbolMR {
+    @CanResolve
+    public abstract static class RSymbolCheck extends Node {
+
+        protected static boolean test(TruffleObject receiver) {
+            return receiver instanceof RSymbol;
+        }
+    }
+
+}
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 63b0cf0575..91dcaa8c70 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
@@ -465,7 +465,6 @@ public final class RContext extends ExecutionContext implements TruffleObject {
         this.stateInstrumentation = InstrumentationState.newContextState(instrumenter);
         this.stateInternalCode = ContextStateImpl.newContextState();
         this.engine = RContext.getRRuntimeASTAccess().createEngine(this);
-
         state.add(State.CONSTRUCTED);
     }
 
@@ -523,8 +522,6 @@ public final class RContext extends ExecutionContext implements TruffleObject {
         stateStdConnections.initialize(this);
         stateRNG.initialize(this);
         this.stateRFFI = RFFIContextStateFactory.newContextState().initialize(this);
-
-        stateRFFI.initialize(this);
         stateRSerialize.initialize(this);
         stateLazyDBCache.initialize(this);
         stateInstrumentation.initialize(this);
@@ -787,6 +784,11 @@ public final class RContext extends ExecutionContext implements TruffleObject {
         return RContext.getInstance().engine;
     }
 
+    public ContextState getStateRFFI() {
+        assert stateRFFI != null;
+        return stateRFFI;
+    }
+
     public PolyglotEngine getVM() {
         return info.getVM();
     }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RForeignAccessFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RForeignAccessFactory.java
index d05f96d5ec..d5732acff8 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RForeignAccessFactory.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RForeignAccessFactory.java
@@ -28,7 +28,13 @@ import com.oracle.truffle.r.runtime.data.RTruffleObject;
 
 public interface RForeignAccessFactory {
 
-    ForeignAccess getForeignAccess(RTruffleObject value);
+    /**
+     * Return the appropriate {@link ForeignAccess} instance for {@code obj}.
+     */
+    ForeignAccess getForeignAccess(RTruffleObject obj);
 
+    /**
+     * Return the {@link TruffleLanguage} instance for R. (Project circularity workaround).
+     */
     Class<? extends TruffleLanguage<RContext>> getTruffleLanguage();
 }
-- 
GitLab