diff --git a/com.oracle.truffle.r.native/fficall/src/jni/Rinternals.c b/com.oracle.truffle.r.native/fficall/src/jni/Rinternals.c
index 27531805427a24841acc3bd6dfa20d5e5cb46178..00d867ef647012ad9d64979ae8439eee20f83783 100644
--- a/com.oracle.truffle.r.native/fficall/src/jni/Rinternals.c
+++ b/com.oracle.truffle.r.native/fficall/src/jni/Rinternals.c
@@ -52,6 +52,7 @@ static jmethodID Rf_getAttribMethodID;
 static jmethodID Rf_setAttribMethodID;
 static jmethodID Rf_isStringMethodID;
 static jmethodID Rf_isNullMethodID;
+static jmethodID Rf_installCharMethodID;
 static jmethodID Rf_installMethodID;
 static jmethodID Rf_warningcallMethodID;
 static jmethodID Rf_warningMethodID;
@@ -158,6 +159,7 @@ void init_internals(JNIEnv *env) {
 	Rf_isStringMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_isString", "(Ljava/lang/Object;)I", 0);
 	Rf_isNullMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_isNull", "(Ljava/lang/Object;)I", 0);
 	Rf_installMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_install", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	Rf_installCharMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_installChar", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
 	Rf_warningMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_warning", "(Ljava/lang/Object;)V", 0);
 	Rf_warningcallMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_warningcall", "(Ljava/lang/Object;Ljava/lang/Object;)V", 0);
 	Rf_errorMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_error", "(Ljava/lang/Object;)V", 0);
@@ -166,7 +168,7 @@ void init_internals(JNIEnv *env) {
 	Rf_allocateArrayMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_allocateArray", "(ILjava/lang/Object;)Ljava/lang/Object;", 0);
 	Rf_duplicateMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_duplicate", "(Ljava/lang/Object;I)Ljava/lang/Object;", 0);
 	Rf_anyDuplicatedMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_anyDuplicated", "(Ljava/lang/Object;I)I", 0);
-	R_NewHashedEnvMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_NewHashedEnv", "(Lcom/oracle/truffle/r/runtime/env/REnvironment;I)Lcom/oracle/truffle/r/runtime/env/REnvironment;", 0);
+	R_NewHashedEnvMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_NewHashedEnv", "(Lcom/oracle/truffle/r/runtime/env/REnvironment;Ljava/lang/Object;)Lcom/oracle/truffle/r/runtime/env/REnvironment;", 0);
 	Rf_classgetsMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_classgets", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 0);
 	RprintfMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rprintf", "(Ljava/lang/Object;)V", 0);
 	R_do_MAKE_CLASS_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_do_MAKE_CLASS", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
@@ -497,8 +499,7 @@ SEXP Rf_install(const char *name) {
 SEXP Rf_installChar(SEXP charsxp) {
 	TRACE(TARGp, charsxp);
 	JNIEnv *thisenv = getEnv();
-	jstring string = stringFromCharSXP(thisenv, charsxp);
-	SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, Rf_installMethodID, string);
+	SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, Rf_installCharMethodID, charsxp);
 	return checkRef(thisenv, result);
 }
 
diff --git a/com.oracle.truffle.r.native/version.source b/com.oracle.truffle.r.native/version.source
index 78c24caea0d6432c6894a971ae42532768cc80d4..60d3b2f4a4cd5f1637eba020358bfe5ecb5edcf2 100644
--- a/com.oracle.truffle.r.native/version.source
+++ b/com.oracle.truffle.r.native/version.source
@@ -1 +1 @@
-2/10/17
+15
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/JavaUpCallsRFFIImpl.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/JavaUpCallsRFFIImpl.java
index a12d923fd9d1eac96a6a53f5fe799a458d3abb74..87aa0607ff6c2d7ff6b0499cc3015c63b38d9642 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/JavaUpCallsRFFIImpl.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/JavaUpCallsRFFIImpl.java
@@ -315,6 +315,12 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
         return RDataFactory.createSymbolInterned((String) name);
     }
 
+    @Override
+    public Object Rf_installChar(Object name) {
+        CharSXPWrapper charSXP = guaranteeInstanceOf(name, CharSXPWrapper.class);
+        return RDataFactory.createSymbolInterned(charSXP.getContents());
+    }
+
     @Override
     public Object Rf_lengthgets(Object x, int newSize) {
         RAbstractVector vec = (RAbstractVector) RRuntime.asAbstractVector(x);
@@ -1146,8 +1152,9 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
     }
 
     @Override
-    public REnvironment R_NewHashedEnv(REnvironment parent, int initialSize) {
-        REnvironment env = RDataFactory.createNewEnv(REnvironment.UNNAMED, true, initialSize);
+    public REnvironment R_NewHashedEnv(REnvironment parent, Object initialSize) {
+        // We know this is an RIntVector from use site in gramRd.c
+        REnvironment env = RDataFactory.createNewEnv(REnvironment.UNNAMED, true, ((RIntVector) initialSize).getDataAt(0));
         RArguments.initializeEnclosingFrame(env.getFrame(), parent.getFrame());
         return env;
     }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/RFFIUpCallMethod.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/RFFIUpCallMethod.java
index a998220115fb7fa4aec65f56d40e8c6087f704ce..d475cc1f9e2aff4845c5d0f9a7a576b581042186 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/RFFIUpCallMethod.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/RFFIUpCallMethod.java
@@ -69,7 +69,7 @@ public enum RFFIUpCallMethod {
     R_Interactive("() : sint32"),
     R_MakeExternalPtr("(object, object, object) : object"),
     R_NamespaceRegistry("() : object"),
-    R_NewHashedEnv("(object, sint32) : object"),
+    R_NewHashedEnv("(object, object) : object"),
     R_ParseVector("(object, sint32, object) : object"),
     R_PromiseExpr("(object) : object"),
     R_SetExternalPtrAddr("(object, object) : void"),
@@ -77,7 +77,7 @@ public enum RFFIUpCallMethod {
     R_SetExternalPtrTag("(object, object) : void"),
     R_ToplevelExec("() : object"),
     R_computeIdentical("(object, object, sint32) : sint32"),
-    R_do_MAKE_CLASS("(pointer) : object"),
+    R_do_MAKE_CLASS("(string) : object"),
     R_getContextCall("(object) : object"),
     R_getContextEnv("(object) : object"),
     R_getContextFun("(object) : object"),
@@ -110,7 +110,7 @@ public enum RFFIUpCallMethod {
     Rf_copyMatrix("(object, object, sint32) : void"),
     Rf_defineVar("(object, object, object) : void"),
     Rf_duplicate("(object, sint32) : object"),
-    Rf_error("(pointer) : void"),
+    Rf_error("(string) : void"),
     Rf_eval("(object, object) : object"),
     Rf_findVar("(object, object) : object"),
     Rf_findVarInFrame("(object, object) : object"),
@@ -118,8 +118,9 @@ public enum RFFIUpCallMethod {
     Rf_findfun("(object, object) : object"),
     Rf_getAttrib("(object, object) : object"),
     Rf_gsetVar("(object, object, object) : void"),
-    Rf_inherits("(pointer, object) : sint32"),
-    Rf_install("(pointer) : object"),
+    Rf_inherits("(string, object) : sint32"),
+    Rf_install("(string) : object"),
+    Rf_installChar("(object) : object"),
     Rf_isNull("(object) : sint32"),
     Rf_isString("(object) : sint32"),
     Rf_lengthgets("(object, sint32) : object"),
@@ -127,9 +128,9 @@ public enum RFFIUpCallMethod {
     Rf_ncols("(object) : sint32"),
     Rf_nrows("(object) : sint32"),
     Rf_setAttrib("(object, object, object) : void"),
-    Rf_warning("(pointer) : void"),
-    Rf_warningcall("(object, pointer) : void"),
-    Rprintf("(pointer) : void"),
+    Rf_warning("(string) : void"),
+    Rf_warningcall("(object, string) : void"),
+    Rprintf("(string) : void"),
     SETCADR("(object, object) : object"),
     SETCAR("(object, object) : object"),
     SETCDR("(object, object) : object"),
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/TracingUpCallsRFFIImpl.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/TracingUpCallsRFFIImpl.java
index fd78becb64849ad60a065a0da30db451b4d4ad15..2943d37a769df0466e31d754da376a8dbd37b543 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/TracingUpCallsRFFIImpl.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/TracingUpCallsRFFIImpl.java
@@ -159,6 +159,12 @@ final class TracingUpCallsRFFIImpl implements UpCallsRFFI {
         return delegate.Rf_install(name);
     }
 
+    @Override
+    public Object Rf_installChar(Object name) {
+        RFFIUtils.traceUpCall("Rf_installChar", name);
+        return delegate.Rf_installChar(name);
+    }
+
     @Override
     public Object Rf_lengthgets(Object x, int newSize) {
         RFFIUtils.traceUpCall("Rf_lengthgets", x, newSize);
@@ -700,7 +706,7 @@ final class TracingUpCallsRFFIImpl implements UpCallsRFFI {
     }
 
     @Override
-    public REnvironment R_NewHashedEnv(REnvironment parent, int initialSize) {
+    public REnvironment R_NewHashedEnv(REnvironment parent, Object initialSize) {
         RFFIUtils.traceUpCall("R_NewHashedEnv", parent, initialSize);
         return delegate.R_NewHashedEnv(parent, initialSize);
     }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFICstring.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFICstring.java
index c8dbee39e12f83ea246b81d120268560bb150ae7..5d50ed6f2cca867aa9ec80826bf7746e350cf17c 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFICstring.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFICstring.java
@@ -28,10 +28,11 @@ import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
 /**
- * Tags an upcall argument as being (on the native side) a C string.
+ * Tags an upcall argument as being (on the native side) a C string. By default the C string is
+ * converted to a {@link String}, which requires it to be null-terminated.
  */
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.PARAMETER)
 public @interface RFFICstring {
-
+    boolean convert() default true;
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java
index 35fdd63dac7b346cfd6cdcb1f1b73c76c890b90c..963d98cc59c51098f153ed6e59bfb49bed90735b 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java
@@ -33,8 +33,22 @@ import com.oracle.truffle.r.runtime.context.RContext.ContextState;
  * must support the {@link #newContextState()} method.
  */
 public abstract class RFFIFactory {
-    private static final String FACTORY_CLASS_PROPERTY = "fastr.ffi.factory.class";
-    private static final String DEFAULT_FACTORY_CLASS = "com.oracle.truffle.r.runtime.ffi.jni.JNI_RFFIFactory";
+    private enum Factory {
+        JNI("com.oracle.truffle.r.runtime.ffi.jni.JNI_RFFIFactory"),
+        LLVM("com.oracle.truffle.r.engine.interop.ffi.llvm.TruffleLLVM_RFFIFactory"),
+        NFI("com.oracle.truffle.r.engine.interop.ffi.nfi.TruffleNFI_RFFIFactory");
+
+        private final String klassName;
+
+        Factory(String klassName) {
+            this.klassName = klassName;
+        }
+    }
+
+    private static final String FACTORY_CLASS_PROPERTY = "fastr.rffi.factory.class";
+    private static final String FACTORY_CLASS_NAME_PROPERTY = "fastr.rffi.factory";
+    private static final String FACTORY_CLASS_ENV = "FASTR_RFFI_FACTORY";
+    private static final Factory DEFAULT_FACTORY = Factory.JNI;
 
     /**
      * Singleton instance of the factory.
@@ -45,20 +59,43 @@ public abstract class RFFIFactory {
 
     public static RFFIFactory initialize() {
         if (instance == null) {
-            String prop = System.getProperty(FACTORY_CLASS_PROPERTY);
+            String klassName = getFactoryClassName();
             try {
-                if (prop == null) {
-                    prop = DEFAULT_FACTORY_CLASS;
-                }
-                instance = (RFFIFactory) Class.forName(prop).newInstance();
+                instance = (RFFIFactory) Class.forName(klassName).newInstance();
                 theRFFI = instance.createRFFI();
             } catch (Exception ex) {
-                throw Utils.rSuicide("Failed to instantiate class: " + prop + ": " + ex);
+                throw Utils.rSuicide("Failed to instantiate class: " + klassName + ": " + ex);
             }
         }
         return instance;
     }
 
+    private static String getFactoryClassName() {
+        String prop = System.getProperty(FACTORY_CLASS_PROPERTY);
+        if (prop != null) {
+            return prop;
+        }
+        prop = System.getProperty(FACTORY_CLASS_NAME_PROPERTY);
+        if (prop != null) {
+            return checkFactoryName(prop);
+        }
+        prop = System.getenv(FACTORY_CLASS_ENV);
+        if (prop != null) {
+            return checkFactoryName(prop);
+        }
+        return DEFAULT_FACTORY.klassName;
+    }
+
+    private static String checkFactoryName(String prop) {
+        try {
+            Factory factory = Factory.valueOf(prop.toUpperCase());
+            return factory.klassName;
+        } catch (IllegalArgumentException ex) {
+            throw Utils.rSuicide("No RFFI factory: " + prop);
+        }
+
+    }
+
     public static RFFIFactory getInstance() {
         assert instance != null;
         return instance;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/StdUpCallsRFFI.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/StdUpCallsRFFI.java
index 3d96124a1cae1cfce8e8af31608e92dd60bbc9a6..1bd2e8efe24c900f096dc0cb84e43f412e0fee51 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/StdUpCallsRFFI.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/StdUpCallsRFFI.java
@@ -65,7 +65,7 @@ public interface StdUpCallsRFFI {
 
     Object Rf_coerceVector(Object x, int mode);
 
-    Object Rf_mkCharLenCE(@RFFICstring Object bytes, int len, int encoding);
+    Object Rf_mkCharLenCE(@RFFICstring(convert = false) Object bytes, int len, int encoding);
 
     Object Rf_cons(Object car, Object cdr);
 
@@ -90,6 +90,8 @@ public interface StdUpCallsRFFI {
 
     Object Rf_install(@RFFICstring Object name);
 
+    Object Rf_installChar(Object name);
+
     Object Rf_lengthgets(Object x, int newSize);
 
     int Rf_isString(Object x);
@@ -240,7 +242,7 @@ public interface StdUpCallsRFFI {
 
     void R_CleanUp(int sa, int status, int runlast);
 
-    REnvironment R_NewHashedEnv(REnvironment parent, int initialSize);
+    REnvironment R_NewHashedEnv(REnvironment parent, Object initialSize);
 
     int PRSEEN(Object x);
 
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tools/RFFIUpCallMethodGenerate.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tools/RFFIUpCallMethodGenerate.java
index 85f600102edbe908fc920d4cf39986245b44a250..b538c5c44d84568c7d05d22e628a71157a386667 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tools/RFFIUpCallMethodGenerate.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tools/RFFIUpCallMethodGenerate.java
@@ -28,6 +28,7 @@ import java.util.Arrays;
 import java.util.Comparator;
 
 import com.oracle.truffle.r.nodes.ffi.RFFIUpCallMethod;
+import com.oracle.truffle.r.runtime.ffi.RFFICstring;
 import com.oracle.truffle.r.runtime.ffi.UpCallsRFFI;
 
 /**
@@ -73,12 +74,24 @@ public class RFFIUpCallMethodGenerate {
         return sb.toString();
     }
 
-    static String nfiParamName(Class<?> paramType, Annotation[] annotation) {
+    static String nfiParamName(Class<?> paramType, Annotation[] annotations) {
         String paramName = paramType.getSimpleName();
-        Class<?> klass = annotation.length == 0 ? null : annotation[0].annotationType();
+        RFFICstring rffiCstring = null;
+        if (annotations.length > 0) {
+            for (Annotation annotation : annotations) {
+                if (annotation instanceof RFFICstring) {
+                    rffiCstring = (RFFICstring) annotation;
+                    break;
+                }
+            }
+        }
         switch (paramName) {
             case "Object":
-                return klass == null ? "object" : "pointer";
+                if (rffiCstring == null) {
+                    return "object";
+                } else {
+                    return rffiCstring.convert() ? "string" : "pointer";
+                }
             case "int":
                 return "sint32";
             case "double":