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 2030c3ecb77f0d70debd5bc68b326daa7d802d3c..9d55e2b44f1b2c1458e0db9300e163bfe05c36f5 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 0857f0b53170e67e2b6042ce574718bad030cbff..4c2f44c65435784378c9ac91b3fb8e3f55df718b 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 99c947a62dbc8ce4e07359f7873abf9baab63558..698ae85bc701c39fe45b7d6ae8f35fcd75ae7345 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 dd2d584e5b43bf6515adab3e789d6798ae427c13..557545ba6e3be4cf0ae958db0bcbc3b97d03b1f3 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'");