diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java index ba76e4063f61dd90997916bd5526997cb46a21fd..08cbe18cd4f7f2278adb4d28be3c838f9d233733 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java @@ -59,6 +59,7 @@ import com.oracle.truffle.r.runtime.RArguments; import com.oracle.truffle.r.runtime.RCaller; import com.oracle.truffle.r.runtime.RDeparse; import com.oracle.truffle.r.runtime.RError; +import com.oracle.truffle.r.runtime.RError.ShowCallerOf; import com.oracle.truffle.r.runtime.RRuntimeASTAccess; import com.oracle.truffle.r.runtime.RootWithBody; import com.oracle.truffle.r.runtime.Utils; @@ -79,7 +80,9 @@ import com.oracle.truffle.r.runtime.nodes.InternalRSyntaxNodeChildren; import com.oracle.truffle.r.runtime.nodes.RBaseNode; import com.oracle.truffle.r.runtime.nodes.RInstrumentableNode; import com.oracle.truffle.r.runtime.nodes.RNode; +import com.oracle.truffle.r.runtime.nodes.RSyntaxCall; import com.oracle.truffle.r.runtime.nodes.RSyntaxElement; +import com.oracle.truffle.r.runtime.nodes.RSyntaxLookup; import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; /** @@ -189,6 +192,32 @@ class RRuntimeASTAccessImpl implements RRuntimeASTAccess { RCaller parent = RArguments.getCall(frame); frame = Utils.getCallerFrame(parent, FrameAccess.READ_ONLY); } + return findCallerFromFrame(frame); + } else if (call instanceof ShowCallerOf) { + Frame frame = Utils.getActualCurrentFrame(); + + boolean match = false; + String name = ((ShowCallerOf) call).getCallerOf(); + Frame f = frame; + while (f != null && RArguments.isRFrame(f)) { + RCaller parent = RArguments.getCall(f); + if (parent.isValidCaller()) { + RSyntaxElement syntaxNode = parent.getSyntaxNode(); + if (syntaxNode instanceof RSyntaxCall) { + RSyntaxElement syntaxElement = ((RSyntaxCall) syntaxNode).getSyntaxLHS(); + if (syntaxElement instanceof RSyntaxLookup) { + if (match) { + return findCallerFromFrame(f); + } + if (name.equals(((RSyntaxLookup) syntaxElement).getIdentifier())) { + match = true; + } + } + } + } + f = Utils.getCallerFrame(parent, FrameAccess.READ_ONLY); + } + return findCallerFromFrame(frame); } else { RBaseNode originalCall = checkBuiltin(call); 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 f4147ee060e9e18ad5a43a21f381ddf23955f4c7..c89561bf38828d6f30571f0cd98efd44e3cbef83 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 @@ -108,10 +108,12 @@ import com.oracle.truffle.r.nodes.builtin.fastr.FastRIdentityNodeGen; import com.oracle.truffle.r.nodes.builtin.fastr.FastRInspect; import com.oracle.truffle.r.nodes.builtin.fastr.FastRInspectNodeGen; import com.oracle.truffle.r.nodes.builtin.fastr.FastRInterop; +import com.oracle.truffle.r.nodes.builtin.fastr.FastRInterop.FastRInteropCheckException; import com.oracle.truffle.r.nodes.builtin.fastr.FastRInterop.FastRInteropClearException; import com.oracle.truffle.r.nodes.builtin.fastr.FastRInterop.FastRInteropGetException; import com.oracle.truffle.r.nodes.builtin.fastr.FastRInterop.FastRInteropTry; import com.oracle.truffle.r.nodes.builtin.fastr.FastRInteropFactory; +import com.oracle.truffle.r.nodes.builtin.fastr.FastRInteropFactory.FastRInteropCheckExceptionNodeGen; import com.oracle.truffle.r.nodes.builtin.fastr.FastRInteropFactory.FastRInteropClearExceptionNodeGen; import com.oracle.truffle.r.nodes.builtin.fastr.FastRInteropFactory.FastRInteropGetExceptionNodeGen; import com.oracle.truffle.r.nodes.builtin.fastr.FastRInteropFactory.FastRInteropTryNodeGen; @@ -449,6 +451,7 @@ public class BasePackage extends RBuiltinPackage { add(FastROptionBuiltin.class, FastROptionBuiltin::create); add(FastRTestsTry.class, FastRTestsTryNodeGen::create); add(FastRInteropTry.class, FastRInteropTryNodeGen::create); + add(FastRInteropCheckException.class, FastRInteropCheckExceptionNodeGen::create); add(FastRInteropGetException.class, FastRInteropGetExceptionNodeGen::create); add(FastRInteropClearException.class, FastRInteropClearExceptionNodeGen::create); add(FastRInspect.class, FastRInspectNodeGen::create); @@ -461,8 +464,14 @@ public class BasePackage extends RBuiltinPackage { add(FastRInterop.DoCallExternal.class, FastRInteropFactory.DoCallExternalNodeGen::create); add(FastRInterop.IsExternal.class, FastRInteropFactory.IsExternalNodeGen::create); add(FastRInterop.JavaClass.class, FastRInteropFactory.JavaClassNodeGen::create); + add(FastRInterop.GetJavaClass.class, FastRInteropFactory.GetJavaClassNodeGen::create); add(FastRInterop.JavaClassName.class, FastRInteropFactory.JavaClassNameNodeGen::create); add(FastRInterop.JavaAddToClasspath.class, FastRInteropFactory.JavaAddToClasspathNodeGen::create); + add(FastRInterop.JavaClasspath.class, FastRInteropFactory.JavaClasspathNodeGen::create); + add(FastRInterop.JavaIsIdentical.class, FastRInteropFactory.JavaIsIdenticalNodeGen::create); + add(FastRInterop.JavaIsAssignableFrom.class, FastRInteropFactory.JavaIsAssignableFromNodeGen::create); + add(FastRInterop.JavaIsInstance.class, FastRInteropFactory.JavaIsInstanceNodeGen::create); + add(FastRInterop.JavaAsTruffleObject.class, FastRInteropFactory.JavaAsTruffleObjectNodeGen::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 adc76dc605830af8f794d21d417b2936c10c2aa2..06f77fcdc7b2bc20cd6ea85ce106a5ee3938a822 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 @@ -51,6 +51,7 @@ import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.interop.ArityException; import com.oracle.truffle.api.interop.ForeignAccess; +import com.oracle.truffle.api.interop.InteropException; import com.oracle.truffle.api.interop.Message; import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.interop.UnknownIdentifierException; @@ -59,6 +60,7 @@ import com.oracle.truffle.api.interop.UnsupportedTypeException; 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.BranchProfile; import com.oracle.truffle.api.profiles.ConditionProfile; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.source.Source.Builder; @@ -478,6 +480,8 @@ public class FastRInterop { @RBuiltin(name = "new.java.class", visibility = ON, kind = PRIMITIVE, parameterNames = {"class", "silent"}, behavior = COMPLEX) public abstract static class JavaClass extends RBuiltinNode.Arg2 { + private static final Object accessError = new Object(); + static { Casts casts = new Casts(JavaClass.class); casts.arg("class").mustBe(stringValue()).asStringVector().mustBe(Predef.singleElement()).findFirst(); @@ -487,14 +491,81 @@ public class FastRInterop { @Specialization @TruffleBoundary - public TruffleObject javaClass(String clazz, boolean silent) { + public TruffleObject javaClass(TruffleObject obj, boolean silent) { + if (JavaInterop.isJavaObject(obj)) { + return JavaInterop.toJavaClass(obj); + } + throw error(RError.Message.GENERIC, "unsupported type " + obj.getClass().getName()); + } + + protected boolean isClass(Object obj) { + return obj != null && obj instanceof Class; + } + + @Specialization(guards = {"isClass(clazz)", "clazz.equals(cachedClazz)"}, limit = "10") + public TruffleObject javaClassCached(String clazz, boolean silent, + @Cached("clazz") String cachedClazz, + @Cached("getJavaClass(clazz, silent)") Object result, + @Cached("create()") BranchProfile interopExceptionProfile) { + return javaClassToTruffleObject(clazz, result, interopExceptionProfile); + } + + @Specialization(replaces = "javaClassCached") + public TruffleObject javaClass(String clazz, boolean silent, + @Cached("create()") BranchProfile interopExceptionProfile) { + Object result = getJavaClass(clazz, silent); + return javaClassToTruffleObject(clazz, result, interopExceptionProfile); + } + + @TruffleBoundary + private TruffleObject javaClassToTruffleObject(String clazz, Object result, BranchProfile interopExceptionProfile) { + if (result == RNull.instance) { + return RNull.instance; + } + if (result instanceof Class<?>) { + return JavaInterop.asTruffleObject(result); + } else if (result == accessError) { + CompilerDirectives.transferToInterpreter(); + throw error(RError.Message.GENERIC, "error while accessing Java class: " + clazz); + } else { + interopExceptionProfile.enter(); + if (result instanceof RuntimeException) { + throw RError.handleInteropException(this, (RuntimeException) result); + } else { + assert result instanceof Throwable; + throw RError.handleInteropException(this, new RuntimeException((Throwable) result)); + } + } + } + + protected static Object getJavaClass(String className, boolean silent) { + Class<?> clazz = getPrimitiveClass(className); + if (clazz != null) { + return clazz; + } + return loadClass(className, silent); + } + + @TruffleBoundary + private static Object loadClass(String clazz, boolean silent) { try { - return JavaInterop.asTruffleObject(RContext.getInstance().loadClass(clazz.replaceAll("/", "."))); - } catch (ClassNotFoundException | SecurityException | IllegalArgumentException e) { + Class<?> result = RContext.getInstance().loadClass(clazz); + if (result == null) { + // not found + if (silent) { + return RNull.instance; + } else { + return new ClassNotFoundException(clazz + " not found"); + } + } + return result; + } catch (SecurityException | IllegalArgumentException e) { + return accessError; + } catch (RuntimeException e) { if (silent) { return RNull.instance; } - throw error(RError.Message.GENERIC, "error while accessing Java class: " + e.getMessage()); + return e; } } } @@ -529,31 +600,57 @@ public class FastRInterop { } } + @RBuiltin(name = "java.classpath", visibility = ON, kind = PRIMITIVE, parameterNames = {}, behavior = COMPLEX) + public abstract static class JavaClasspath extends RBuiltinNode.Arg0 { + + static { + Casts.noCasts(JavaClasspath.class); + } + + @Specialization + @TruffleBoundary + public RAbstractStringVector getEntries() { + RContext ctx = RContext.getInstance(); + String[] paths = ctx.getInteropClasspathEntries(); + return RDataFactory.createStringVector(paths, true); + } + } + @ImportStatic({RRuntime.class}) - @RBuiltin(name = "java.class", visibility = ON, kind = PRIMITIVE, parameterNames = {"class"}, behavior = COMPLEX) - public abstract static class JavaClassName extends RBuiltinNode.Arg1 { + @RBuiltin(name = "java.class", visibility = ON, kind = PRIMITIVE, parameterNames = {"obj", "getClassName"}, behavior = COMPLEX) + public abstract static class JavaClassName extends RBuiltinNode.Arg2 { static { - Casts.noCasts(JavaClassName.class); + Casts casts = new Casts(JavaClassName.class); + casts.arg("getClassName").mapMissing(Predef.constant(RRuntime.LOGICAL_FALSE)).mustBe(logicalValue().or(Predef.nullValue())).asLogicalVector().mustBe(singleElement()).findFirst().mustBe( + notLogicalNA()).map(Predef.toBoolean()); } @Specialization(guards = {"isJavaObject(obj)"}) @TruffleBoundary - public Object javaClassName(TruffleObject obj) { - Object o = JavaInterop.asJavaObject(Object.class, obj); - if (o == null) { - return RNull.instance; + public Object javaClassName(Object obj, boolean getClassName) { + if (isJavaObject(obj)) { + Object o = JavaInterop.asJavaObject(Object.class, (TruffleObject) obj); + if (o == null) { + return RNull.instance; + } + if (getClassName && o instanceof Class) { + return ((Class<?>) o).getName(); + } + return o.getClass().getName(); + } else { + throw error(RError.Message.GENERIC, "unsupported type " + obj.getClass().getName()); } - return o.getClass().getName(); } - protected boolean isJavaObject(TruffleObject obj) { - return JavaInterop.isJavaObject(obj); + protected boolean isJavaObject(Object obj) { + return RRuntime.isForeignObject(obj) && JavaInterop.isJavaObject(obj); } @Fallback - public String javaClassName(@SuppressWarnings("unused") Object obj) { - throw error(RError.Message.GENERIC, "unsupported type"); + public String javaClassName(@SuppressWarnings("unused") Object obj, + @SuppressWarnings("unused") Object getClassName) { + throw error(RError.Message.GENERIC, "unsupported type " + obj.getClass().getName()); } } @@ -566,7 +663,6 @@ public class FastRInterop { } @Specialization(guards = {"isForeignObject(obj)"}) - @TruffleBoundary public byte isArray(TruffleObject obj, @Cached("HAS_SIZE.createNode()") Node hasSize) { return RRuntime.asLogical(ForeignAccess.sendHasSize(hasSize, obj)); @@ -578,6 +674,194 @@ public class FastRInterop { } } + @RBuiltin(name = ".fastr.interop.getJavaClass", visibility = ON, kind = PRIMITIVE, parameterNames = {"obj"}, behavior = COMPLEX) + public abstract static class GetJavaClass extends RBuiltinNode.Arg1 { + + static { + Casts.noCasts(GetJavaClass.class); + } + + @Specialization + @TruffleBoundary + public TruffleObject javaClass(TruffleObject obj) { + if (JavaInterop.isJavaObject(obj)) { + return JavaInterop.toJavaClass(obj); + } + throw error(RError.Message.GENERIC, "unsupported type " + obj.getClass().getName()); + } + } + + @ImportStatic({Message.class, RRuntime.class}) + @RBuiltin(name = ".fastr.interop.isIdentical", visibility = ON, kind = PRIMITIVE, parameterNames = {"x1", "x2"}, behavior = COMPLEX) + public abstract static class JavaIsIdentical extends RBuiltinNode.Arg2 { + + static { + Casts.noCasts(JavaIsIdentical.class); + } + + @Specialization(guards = {"isJavaObject(x1)", "isJavaObject(x2)"}) + public byte isIdentical(TruffleObject x1, TruffleObject x2) { + return RRuntime.asLogical(JavaInterop.asJavaObject(Object.class, x1) == JavaInterop.asJavaObject(Object.class, x2)); + } + + @Fallback + @TruffleBoundary + public byte isIdentical(@SuppressWarnings("unused") Object x1, @SuppressWarnings("unused") Object x2) { + throw error(RError.Message.GENERIC, String.format("unsupported types: %s, %s", x1.getClass().getName(), x2.getClass().getName())); + } + + protected boolean isJavaObject(TruffleObject obj) { + return JavaInterop.isJavaObject(obj); + } + } + + @ImportStatic({Message.class, RRuntime.class}) + @RBuiltin(name = ".fastr.interop.asJavaTruffleObject", visibility = ON, kind = PRIMITIVE, parameterNames = {"x"}, behavior = COMPLEX) + public abstract static class JavaAsTruffleObject extends RBuiltinNode.Arg1 { + + static { + Casts.noCasts(JavaAsTruffleObject.class); + } + + @Specialization + public TruffleObject asTruffleObject(byte b) { + return JavaInterop.asTruffleObject(RRuntime.fromLogical(b)); + } + + @Specialization + public TruffleObject asTruffleObject(int i) { + return JavaInterop.asTruffleObject(i); + } + + @Specialization + public TruffleObject asTruffleObject(double d) { + return JavaInterop.asTruffleObject(d); + } + + @Specialization + public TruffleObject asTruffleObject(String s) { + return JavaInterop.asTruffleObject(s); + } + + @Specialization + public TruffleObject asTruffleObject(RInteropByte b) { + return JavaInterop.asTruffleObject(b.getValue()); + } + + @Specialization + public TruffleObject asTruffleObject(RInteropChar c) { + return JavaInterop.asTruffleObject(c.getValue()); + } + + @Specialization + public TruffleObject asTruffleObject(RInteropFloat f) { + return JavaInterop.asTruffleObject(f.getValue()); + } + + @Specialization + public TruffleObject asTruffleObject(RInteropLong l) { + return JavaInterop.asTruffleObject(l.getValue()); + } + + @Specialization + public TruffleObject asTruffleObject(RInteropShort s) { + return JavaInterop.asTruffleObject(s.getValue()); + } + + @Fallback + @TruffleBoundary + public byte asTruffleObject(@SuppressWarnings("unused") Object x) { + throw error(RError.Message.GENERIC, String.format("unsupported type: %s", x.getClass().getName())); + } + } + + @ImportStatic({Message.class, RRuntime.class}) + @RBuiltin(name = ".fastr.interop.isAssignableFrom", visibility = ON, kind = PRIMITIVE, parameterNames = {"x1", "x2"}, behavior = COMPLEX) + public abstract static class JavaIsAssignableFrom extends RBuiltinNode.Arg2 { + + static { + Casts.noCasts(JavaIsAssignableFrom.class); + } + + @Specialization(guards = {"isJavaObject(x1)", "isJavaObject(x2)"}) + public byte isAssignable(TruffleObject x1, TruffleObject x2) { + Object jo1 = JavaInterop.asJavaObject(Object.class, x1); + Class<?> cl1 = (jo1 instanceof Class) ? (Class<?>) jo1 : jo1.getClass(); + Object jo2 = JavaInterop.asJavaObject(Object.class, x2); + Class<?> cl2 = (jo2 instanceof Class) ? (Class<?>) jo2 : jo2.getClass(); + return RRuntime.asLogical(cl2.isAssignableFrom(cl1)); + } + + @Fallback + @TruffleBoundary + public byte isAssignable(@SuppressWarnings("unused") Object x1, @SuppressWarnings("unused") Object x2) { + throw error(RError.Message.GENERIC, String.format("unsupported types: %s, %s", x1.getClass().getName(), x2.getClass().getName())); + } + + protected boolean isJavaObject(TruffleObject obj) { + return JavaInterop.isJavaObject(obj); + } + } + + @ImportStatic({Message.class, RRuntime.class}) + @RBuiltin(name = ".fastr.interop.isInstance", visibility = ON, kind = PRIMITIVE, parameterNames = {"x1", "x2"}, behavior = COMPLEX) + public abstract static class JavaIsInstance extends RBuiltinNode.Arg2 { + + static { + Casts.noCasts(JavaIsInstance.class); + } + + @Specialization(guards = {"isJavaObject(x1)", "isJavaObject(x2)"}) + public byte isInstance(TruffleObject x1, TruffleObject x2) { + Object jo1 = JavaInterop.asJavaObject(Object.class, x1); + Object jo2 = JavaInterop.asJavaObject(Object.class, x2); + if (jo1 instanceof Class) { + Class<?> cl1 = (Class<?>) jo1; + return RRuntime.asLogical(cl1.isInstance(jo2)); + } + return RRuntime.asLogical(jo1.getClass().isInstance(jo2)); + } + + @Specialization(guards = {"isJavaObject(x1)"}) + public byte isInstance(TruffleObject x1, RInteropScalar x2, + @Cached("createR2Foreign()") R2Foreign r2Foreign) { + Object jo1 = JavaInterop.asJavaObject(Object.class, x1); + if (jo1 instanceof Class) { + Class<?> cl1 = (Class<?>) jo1; + return RRuntime.asLogical(cl1.isInstance(r2Foreign.execute(x2))); + } + return RRuntime.asLogical(jo1.getClass().isInstance(x2)); + } + + @Specialization(guards = {"isJavaObject(x1)", "!isJavaObject(x2)", "!isInterop(x2)"}) + public byte isInstance(TruffleObject x1, Object x2) { + Object jo1 = JavaInterop.asJavaObject(Object.class, x1); + if (jo1 instanceof Class) { + Class<?> cl1 = (Class<?>) jo1; + return RRuntime.asLogical(cl1.isInstance(x2)); + } + return RRuntime.asLogical(jo1.getClass().isInstance(x2)); + } + + @Fallback + @TruffleBoundary + public byte isInstance(@SuppressWarnings("unused") Object x1, @SuppressWarnings("unused") Object x2) { + throw error(RError.Message.GENERIC, String.format("unsupported types: %s, %s", x1.getClass().getName(), x2.getClass().getName())); + } + + protected boolean isJavaObject(Object obj) { + return RRuntime.isForeignObject(obj) && JavaInterop.isJavaObject((TruffleObject) obj); + } + + protected boolean isInterop(Object obj) { + return obj instanceof RInteropScalar; + } + + protected R2Foreign createR2Foreign() { + return R2ForeignNodeGen.create(); + } + } + @RBuiltin(name = "new.java.array", visibility = ON, kind = PRIMITIVE, parameterNames = {"class", "dim"}, behavior = COMPLEX) public abstract static class NewJavaArray extends RBuiltinNode.Arg2 { @@ -604,11 +888,11 @@ public class FastRInterop { } private Class<?> getClazz(String className) throws RError { - try { - return classForName(className); - } catch (ClassNotFoundException e) { - throw error(RError.Message.GENERIC, "error while accessing Java class: " + e.getMessage()); + Class<?> result = classForName(className); + if (result == null) { + throw error(RError.Message.GENERIC, "cannot access Java class %s", className); } + return result; } } @@ -648,7 +932,23 @@ public class FastRInterop { @Specialization @TruffleBoundary public Object toArray(RAbstractIntVector vec, String className, boolean flat) { - return toArray(vec, flat, getClazz(className), (array, i) -> Array.set(array, i, vec.getDataAt(i))); + return toArray(vec, flat, getClazz(className), (array, i) -> { + if (Byte.TYPE.getName().equals(className)) { + Array.set(array, i, (byte) vec.getDataAt(i)); + } else if (Character.TYPE.getName().equals(className)) { + Array.set(array, i, (char) vec.getDataAt(i)); + } else if (Double.TYPE.getName().equals(className)) { + Array.set(array, i, (double) vec.getDataAt(i)); + } else if (Float.TYPE.getName().equals(className)) { + Array.set(array, i, (float) vec.getDataAt(i)); + } else if (Long.TYPE.getName().equals(className)) { + Array.set(array, i, (long) vec.getDataAt(i)); + } else if (Short.TYPE.getName().equals(className)) { + Array.set(array, i, (short) vec.getDataAt(i)); + } else { + Array.set(array, i, vec.getDataAt(i)); + } + }); } @Specialization @@ -660,7 +960,23 @@ public class FastRInterop { @Specialization @TruffleBoundary public Object toArray(RAbstractDoubleVector vec, String className, boolean flat) { - return toArray(vec, flat, getClazz(className), (array, i) -> Array.set(array, i, vec.getDataAt(i))); + return toArray(vec, flat, getClazz(className), (array, i) -> { + if (Byte.TYPE.getName().equals(className)) { + Array.set(array, i, (byte) vec.getDataAt(i)); + } else if (Character.TYPE.getName().equals(className)) { + Array.set(array, i, (char) vec.getDataAt(i)); + } else if (Float.TYPE.getName().equals(className)) { + Array.set(array, i, (float) vec.getDataAt(i)); + } else if (Integer.TYPE.getName().equals(className)) { + Array.set(array, i, (int) vec.getDataAt(i)); + } else if (Long.TYPE.getName().equals(className)) { + Array.set(array, i, (long) vec.getDataAt(i)); + } else if (Short.TYPE.getName().equals(className)) { + Array.set(array, i, (short) vec.getDataAt(i)); + } else { + Array.set(array, i, vec.getDataAt(i)); + } + }); } @Specialization @@ -691,14 +1007,14 @@ public class FastRInterop { @TruffleBoundary public Object toArray(RAbstractVector vec, @SuppressWarnings("unused") RMissing className, boolean flat, @Cached("createR2Foreign()") R2Foreign r2Foreign) { - return toArray(vec, flat, Object.class, (array, i) -> Array.set(array, i, r2Foreign.execute(vec.getDataAtAsObject(i)))); + return toArray(vec, flat, Object.class, r2Foreign); } @Specialization(guards = "!isJavaLikeVector(vec)") @TruffleBoundary public Object toArray(RAbstractVector vec, String className, boolean flat, @Cached("createR2Foreign()") R2Foreign r2Foreign) { - return toArray(vec, flat, getClazz(className), (array, i) -> Array.set(array, i, r2Foreign.execute(vec.getDataAtAsObject(i)))); + return toArray(vec, flat, getClazz(className), r2Foreign); } @Specialization @@ -747,6 +1063,26 @@ public class FastRInterop { return JavaInterop.asTruffleObject(array); } + private Object toArray(RAbstractVector vec, boolean flat, Class<?> clazz, R2Foreign r2Foreign) throws IllegalArgumentException, ArrayIndexOutOfBoundsException { + int[] dims = getDim(flat, vec); + final Object array = Array.newInstance(clazz, dims); + TruffleObject truffleArray = JavaInterop.asTruffleObject(array); + + for (int d = 0; d < dims.length; d++) { + int dim = dims[d]; + // TODO works only for flat + for (int i = 0; i < dim; i++) { + try { + Object value = r2Foreign.execute(vec.getDataAtAsObject(i)); + ForeignAccess.sendWrite(Message.WRITE.createNode(), truffleArray, i, value); + } catch (InteropException ex) { + throw error(RError.Message.GENERIC, ex.getMessage()); + } + } + } + return truffleArray; + } + private interface VecElementToArray { void toArray(Object array, Integer i); } @@ -781,11 +1117,11 @@ public class FastRInterop { } private Class<?> getClazz(String className) throws RError { - try { - return classForName(className); - } catch (ClassNotFoundException e) { - throw error(RError.Message.GENERIC, "error while accessing Java class: " + e.getMessage()); + Class<?> result = classForName(className); + if (result == null) { + throw error(RError.Message.GENERIC, "cannot access Java class %s", className); } + return result; } protected boolean isJavaObject(TruffleObject obj) { @@ -802,30 +1138,32 @@ public class FastRInterop { } @ImportStatic({Message.class, RRuntime.class}) - @RBuiltin(name = ".fastr.interop.fromArray", visibility = ON, kind = PRIMITIVE, parameterNames = {"array"}, behavior = COMPLEX) - public abstract static class FromForeignArray extends RBuiltinNode.Arg1 { + @RBuiltin(name = ".fastr.interop.fromArray", visibility = ON, kind = PRIMITIVE, parameterNames = {"array", "recursive"}, behavior = COMPLEX) + public abstract static class FromForeignArray extends RBuiltinNode.Arg2 { static { Casts casts = new Casts(FromForeignArray.class); casts.arg("array").castForeignObjects(false).mustNotBeMissing(); + casts.arg("recursive").mapMissing(Predef.constant(RRuntime.LOGICAL_FALSE)).mustBe(logicalValue().or(Predef.nullValue())).asLogicalVector().mustBe(singleElement()).findFirst().mustBe( + notLogicalNA()).map(Predef.toBoolean()); } private final ConditionProfile isArrayProfile = ConditionProfile.createBinaryProfile(); @Specialization(guards = {"isForeignObject(obj)"}) @TruffleBoundary - public Object fromArray(TruffleObject obj, + public Object fromArray(TruffleObject obj, boolean recursive, @Cached("HAS_SIZE.createNode()") Node hasSize, @Cached("create()") ForeignArray2R array2R) { if (isArrayProfile.profile(ForeignAccess.sendHasSize(hasSize, obj))) { - return array2R.convert(obj); + return array2R.convert(obj, recursive); } else { throw error(RError.Message.GENERIC, "not a java array"); } } @Fallback - public Object fromObject(@SuppressWarnings("unused") Object obj) { + public Object fromObject(@SuppressWarnings("unused") Object obj, @SuppressWarnings("unused") Object recursive) { throw error(RError.Message.GENERIC, "not a java array"); } } @@ -884,32 +1222,33 @@ public class FastRInterop { } } - private static Class<?> classForName(String className) throws ClassNotFoundException { - if (className.equals(Byte.TYPE.getName())) { - return Byte.TYPE; + private static Class<?> classForName(String className) { + Class<?> clazz = getPrimitiveClass(className); + if (clazz != null) { + return clazz; } - if (className.equals(Boolean.TYPE.getName())) { + return RContext.getInstance().loadClass(className); + } + + private static Class<?> getPrimitiveClass(String className) { + if (Boolean.TYPE.getName().equals(className)) { return Boolean.TYPE; - } - if (className.equals(Character.TYPE.getName())) { + } else if (Byte.TYPE.getName().equals(className)) { + return Byte.TYPE; + } else if (Character.TYPE.getName().equals(className)) { return Character.TYPE; - } - if (className.equals(Double.TYPE.getName())) { + } else if (Double.TYPE.getName().equals(className)) { return Double.TYPE; - } - if (className.equals(Float.TYPE.getName())) { + } else if (Float.TYPE.getName().equals(className)) { return Float.TYPE; - } - if (className.equals(Integer.TYPE.getName())) { + } else if (Integer.TYPE.getName().equals(className)) { return Integer.TYPE; - } - if (className.equals(Long.TYPE.getName())) { + } else if (Long.TYPE.getName().equals(className)) { return Long.TYPE; - } - if (className.equals(Short.TYPE.getName())) { + } else if (Short.TYPE.getName().equals(className)) { return Short.TYPE; } - return Class.forName(className); + return null; } @RBuiltin(name = "do.call.external", visibility = ON, kind = PRIMITIVE, parameterNames = {"receiver", "what", "args"}, behavior = COMPLEX) @@ -970,16 +1309,15 @@ public class FastRInterop { @Specialization public Object tryFunc(VirtualFrame frame, RFunction function, byte check) { - boolean isCheck = RRuntime.fromLogical(check); getInteropTryState().stepIn(); try { return call.call(frame, function, RArgsValuesAndNames.EMPTY); } catch (FastRInteropTryException e) { - Throwable cause = e.getCause(); CompilerDirectives.transferToInterpreter(); - if (cause instanceof TruffleException) { + Throwable cause = e.getCause(); + if (cause instanceof TruffleException || cause.getCause() instanceof ClassNotFoundException) { cause = cause.getCause(); - if (isCheck) { + if (RRuntime.fromLogical(check)) { String causeName = cause.getClass().getName(); String msg = cause.getMessage(); msg = msg != null ? String.format("%s: %s", causeName, msg) : causeName; @@ -998,6 +1336,40 @@ public class FastRInterop { } + @RBuiltin(name = ".fastr.interop.checkException", kind = PRIMITIVE, parameterNames = {"silent", "showCallerOf"}, behavior = COMPLEX) + public abstract static class FastRInteropCheckException extends RBuiltinNode.Arg2 { + static { + Casts casts = new Casts(FastRInteropCheckException.class); + casts.arg("silent").mustBe(logicalValue()).asLogicalVector().mustBe(singleElement()).findFirst(); + casts.arg("showCallerOf").allowMissing().mustBe(stringValue()).asStringVector().mustBe(singleElement()).findFirst(); + } + + @Specialization + public Object getException(VirtualFrame frame, byte silent, RMissing callerFor) { + return getException(frame, silent, (String) null); + } + + @Specialization + public Object getException(VirtualFrame frame, byte silent, String showCallerOf) { + Throwable t = getInteropTryState().lastException; + if (t != null) { + CompilerDirectives.transferToInterpreter(); + getInteropTryState().lastException = null; + if (!RRuntime.fromLogical(silent)) { + String causeName = t.getClass().getName(); + String msg = t.getMessage(); + msg = msg != null ? String.format("%s: %s", causeName, msg) : causeName; + if (showCallerOf == null) { + throw RError.error(RError.SHOW_CALLER, RError.Message.GENERIC, msg); + } else { + throw RError.error(new RError.ShowCallerOf(showCallerOf), RError.Message.GENERIC, msg); + } + } + } + return RNull.instance; + } + } + @RBuiltin(name = ".fastr.interop.getTryException", kind = PRIMITIVE, parameterNames = {"clear"}, behavior = COMPLEX) public abstract static class FastRInteropGetException extends RBuiltinNode.Arg1 { static { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java index a4cc7250eb6f3bc3bf9fd94fa9592e862351bd29..9dc2b82baa6afd0b96a0aa0030aa38e4efce9cd1 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java @@ -123,6 +123,23 @@ public final class RError extends RuntimeException implements TruffleException { */ public static final ErrorContext SHOW_CALLER = new ErrorContextImpl(); + /** + * This flags a call to {@code error} or {@code warning} where the error message should show the + * caller provided function if such is available. Otherwise the caller of the current function + * will be shown. + */ + public static final class ShowCallerOf extends ErrorContext { + private final String function; + + public ShowCallerOf(String function) { + this.function = function; + } + + public String getCallerOf() { + return function; + } + } + /** * A very special case that ensures that no caller is output in the error/warning message. This * is needed where, even if there is a caller, GnuR does not show it. @@ -193,7 +210,7 @@ public final class RError extends RuntimeException implements TruffleException { @TruffleBoundary public static RError handleInteropException(Node node, RuntimeException e) { - if (e instanceof TruffleException) { + if (e instanceof TruffleException || e.getCause() instanceof ClassNotFoundException) { if (RContext.getInstance().stateInteropTry.isInTry()) { // will be catched and handled in .fastr.interop.try builtin throw new FastRInteropTryException(e); 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 14cbbbc8216169c4ca2e8770410c69787fec6a70..bd968fce28658fa6a8960386888877b6d8a88099 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 @@ -828,11 +828,29 @@ public final class RContext { } }; - public Class<?> loadClass(String className) throws ClassNotFoundException { - if (FastRConfig.InternalGridAwtSupport) { - return interopClassLoader.loadClass(className); + private final HashMap<String, Class<?>> classCache = new HashMap<>(); + + /** + * This function also takes class names with '/' instead of '.'. A null return value means that + * the class was not found. + */ + public Class<?> loadClass(String className) { + if (classCache.containsKey(className)) { + return classCache.get(className); } - return Class.forName(className); + String demangled = className.replaceAll("/", "."); + Class<?> result; + try { + if (FastRConfig.InternalGridAwtSupport) { + result = interopClassLoader.loadClass(demangled); + } else { + result = Class.forName(demangled); + } + } catch (ClassNotFoundException e) { + result = null; + } + classCache.put(className, result); + return result; } /** @@ -849,6 +867,29 @@ public final class RContext { urls[i] = Paths.get(entries[i]).toUri().toURL(); } interopClassLoader = URLClassLoader.newInstance(urls, interopClassLoader); + classCache.clear(); + } + + /** + * Returns all entries in the Java interop class loader. + * + * @return the CP entries + */ + public String[] getInteropClasspathEntries() { + if (!FastRConfig.InternalGridAwtSupport) { + throw RError.error(RError.NO_CALLER, RError.Message.GENERIC, "Custom class loading not supported."); + } + if (interopClassLoader instanceof URLClassLoader) { + URL[] urls = ((URLClassLoader) interopClassLoader).getURLs(); + if (urls != null) { + String[] ret = new String[urls.length]; + for (int i = 0; i < urls.length; i++) { + ret[i] = urls[i].getPath(); + } + return ret; + } + } + return new String[0]; } public final class ConsoleIO { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/interop/ForeignArray2R.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/interop/ForeignArray2R.java index 2cd826b8689a3a24251aa967c053f58ca022fa80..e3ca6bcac927d0cf283afb8b0fb05dc11fd8f33f 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/interop/ForeignArray2R.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/interop/ForeignArray2R.java @@ -267,7 +267,6 @@ public abstract class ForeignArray2R extends RBaseNode { int size = arrayData.elements.size(); int[] dims = arrayData.dims != null && arrayData.dims.size() > 1 ? arrayData.dims.stream().mapToInt((i) -> i.intValue()).toArray() : null; - assert dims == null || sizeByDims(dims) == size : sizeByDims(dims) + " " + size; switch (type) { case NONE: diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test index 4c72153998acf3103c52b7ba99ba17c9eaba3104..5c8a452f4616911141ce1e0750cf3d2921841353 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test @@ -148283,16 +148283,18 @@ NULL [1] "com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass" ##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testGetClass# -#if (!any(R.version$engine == "FastR")) { cat('Error in java.class(1) : unsupported type', '<<<NEWLINE>>>') } else { java.class(1) } -Error in java.class(1) : unsupported type +#if (!any(R.version$engine == "FastR")) { cat('Error in java.class(1) : unsupported type java.lang.Double', '<<<NEWLINE>>>') } else { java.class(1) } +Error in java.class(1) : unsupported type java.lang.Double ##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testGetClass# -#if (!any(R.version$engine == "FastR")) { cat('Error in java.class(NULL) : unsupported type', '<<<NEWLINE>>>') } else { java.class(NULL) } -Error in java.class(NULL) : unsupported type +#if (!any(R.version$engine == "FastR")) { cat('Error in java.class(NULL) :', '<<<NEWLINE>>>', ' unsupported type com.oracle.truffle.r.runtime.data.RNull', '<<<NEWLINE>>>') } else { java.class(NULL) } +Error in java.class(NULL) : + unsupported type com.oracle.truffle.r.runtime.data.RNull ##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testGetClass# -#if (!any(R.version$engine == "FastR")) { cat('Error in java.class(to$methodReturnsNull()) : unsupported type', '<<<NEWLINE>>>') } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass'));java.class(to$methodReturnsNull()) } -Error in java.class(to$methodReturnsNull()) : unsupported type +#if (!any(R.version$engine == "FastR")) { cat('Error in java.class(to$methodReturnsNull()) :', '<<<NEWLINE>>>', ' unsupported type com.oracle.truffle.r.runtime.data.RNull', '<<<NEWLINE>>>') } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass'));java.class(to$methodReturnsNull()) } +Error in java.class(to$methodReturnsNull()) : + unsupported type com.oracle.truffle.r.runtime.data.RNull ##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testIdentical# #if (!any(R.version$engine == "FastR")) { FALSE } else { b1 <- as.external.byte(1); b2 <- as.external.byte(1); identical(b1, b2) } 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 a48f99ef1321e44a58d58491625b9420d9f1818e..c9436739bc2e8270d04065925f01259a5a223d19 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 @@ -247,9 +247,9 @@ public class TestJavaInterop extends TestBase { public void testGetClass() { assertEvalFastR(CREATE_TRUFFLE_OBJECT + "java.class(to)", "'com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass'"); - assertEvalFastR(CREATE_TRUFFLE_OBJECT + "java.class(to$methodReturnsNull())", errorIn("java.class(to$methodReturnsNull())", "unsupported type")); - assertEvalFastR("java.class(NULL)", errorIn("java.class(NULL)", "unsupported type")); - assertEvalFastR("java.class(1)", errorIn("java.class(1)", "unsupported type")); + assertEvalFastR(CREATE_TRUFFLE_OBJECT + "java.class(to$methodReturnsNull())", errorIn("java.class(to$methodReturnsNull())", "unsupported type com.oracle.truffle.r.runtime.data.RNull")); + assertEvalFastR("java.class(NULL)", errorIn("java.class(NULL)", "unsupported type com.oracle.truffle.r.runtime.data.RNull")); + assertEvalFastR("java.class(1)", errorIn("java.class(1)", "unsupported type java.lang.Double")); } @Test