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/base/Paste.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Paste.java index ce22e93b4542dbf95490f7ca5945c6ca68d4de3c..837bd534627de17d70d12b97ec38c6f6e873ad64 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Paste.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Paste.java @@ -271,7 +271,7 @@ public abstract class Paste extends RBuiltinNode.Arg3 { private CastNode getAsCharacterNode() { if (asCharacterNode == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); - asCharacterNode = insert(newCastBuilder().returnIf(nullValue(), emptyStringVector()).asStringVector().buildCastNode()); + asCharacterNode = insert(newCastBuilder().castForeignObjects(true).returnIf(nullValue(), emptyStringVector()).asStringVector().buildCastNode()); } return asCharacterNode; } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ExternalPtrPrinter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ExternalPtrPrinter.java index d70a8830550f69490e871ec0f20bfa822cbf3080..ab7318661930b15223156c2d244c968e6a3fed8d 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ExternalPtrPrinter.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ExternalPtrPrinter.java @@ -38,7 +38,7 @@ final class ExternalPtrPrinter extends AbstractValuePrinter<RExternalPtr> { @Override @TruffleBoundary protected void printValue(RExternalPtr value, PrintContext printCtx) throws IOException { - // like in RDeparse + // same like in RDeparse if (value.getAddr().isLong()) { printCtx.output().print(String.format("<pointer: %s>", Double.toHexString(value.getAddr().asAddress()))); } else { 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.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/CastForeignNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/CastForeignNode.java index d52436f13ffcd2a8cfbc91a5a16d5ed68538cb99..687c4fce806f0fd1f3ea91763bb59686e0962052 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/CastForeignNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/CastForeignNode.java @@ -24,6 +24,7 @@ package com.oracle.truffle.r.nodes.builtin.casts; import com.oracle.truffle.api.profiles.ConditionProfile; import com.oracle.truffle.r.nodes.unary.CastNode; +import com.oracle.truffle.r.runtime.data.RInteropScalar; import com.oracle.truffle.r.runtime.interop.ForeignArray2R; public final class CastForeignNode extends CastNode { @@ -31,13 +32,20 @@ public final class CastForeignNode extends CastNode { @Child private ForeignArray2R foreignArray2R = ForeignArray2R.create(); private final ConditionProfile isForeign = ConditionProfile.createBinaryProfile(); + private final ConditionProfile isInteropScalar = ConditionProfile.createBinaryProfile(); @Override protected Object execute(Object obj) { if (isForeign.profile(foreignArray2R.isForeignVector(obj))) { return foreignArray2R.convert(obj); + } else if (isInteropScalar.profile(isInteropScalar(obj))) { + return ((RInteropScalar) obj).getRValue(); } else { return obj; } } + + protected boolean isInteropScalar(Object obj) { + return obj instanceof RInteropScalar; + } } diff --git a/com.oracle.truffle.r.pkgs/rJava/DESCRIPTION b/com.oracle.truffle.r.pkgs/rJava/DESCRIPTION index 22b6b722cc2bf4bf2c7ee8830d129ea8a50f8272..0b4abc2eee6b3735f3d1183742ac54753fbfc852 100644 --- a/com.oracle.truffle.r.pkgs/rJava/DESCRIPTION +++ b/com.oracle.truffle.r.pkgs/rJava/DESCRIPTION @@ -6,6 +6,4 @@ Date: 2017-05-18 Author: Tomas Stupka Maintainer: Tomas Stupka <tomas.stupka@oracle.com> Description: Provides rJava R interface backed by FastR interoperability builtins. -License: GPL-2 -Suggests: testthat -RoxygenNote: 6.0.1 +License: GPL-2 \ No newline at end of file diff --git a/com.oracle.truffle.r.pkgs/rJava/NAMESPACE b/com.oracle.truffle.r.pkgs/rJava/NAMESPACE index b3ee338a3a3c9097a7fd84c642938fbe94438e60..1ff4c9f1c729643a9258e633e40dccda29fd070f 100644 --- a/com.oracle.truffle.r.pkgs/rJava/NAMESPACE +++ b/com.oracle.truffle.r.pkgs/rJava/NAMESPACE @@ -1,27 +1,49 @@ -# Generated by roxygen2: do not edit by hand - -export(.jaddClassPath) -export(.jaddLibrary) -export(.jarray) -export(.jbyte) -export(.jcall) -export(.jcast) -export(.jchar) -export(.jcheck) -export(.jclear) -export(.jevalArray) -export(.jfield) -export(.jfindClass) -export(.jfloat) -export(.jgetEx) -export(.jinit) -export(.jlong) -export(.jnew) -export(.jnull) -export(.jpackage) -export(.jshort) -export(.jsimplify) -export(.jstrVal) -export(.jthrow) -export(J) -export(is.jnull) +exportPattern("^\\.j") +export( "J" ) +export( "%instanceof%" ) + +export( clone ) +S3method( clone, default ) +export(is.jnull, .rJava.base.path) +exportClasses(jobjRef, jarrayRef, jrectRef, jfloat, jlong, jbyte, jchar, jclassName) +exportMethods(show, "$", "$<-", + "==", "!=", "<", ">", "<=", ">=", + names, new, as.character, length, head, tail, + "[", "[[", "[[<-", str, "dim<-", + unique, duplicated, anyDuplicated, + sort, rev, + min, max, range, + rep, + clone ) +import(methods) +importFrom(utils,head) +importFrom(utils,tail) +importFrom(utils,str) +importFrom(utils, assignInNamespace) + +S3method(with, jobjRef) +S3method(with, jarrayRef) +S3method(with, jclassName) + +S3method(within, jobjRef) +S3method(within, jarrayRef) +S3method(within, jclassName) + +# within requires that with.jobjRef is visible outside +export(with.jobjRef) + +if( exists( ".DollarNames", asNamespace("utils") ) ) importFrom( utils, .DollarNames ) +S3method(.DollarNames, jobjRef) +S3method(.DollarNames, jarrayRef) +S3method(.DollarNames, jrectRef) +S3method(.DollarNames, jclassName) + +S3method( as.list, jobjRef ) +S3method( as.list, jarrayRef ) +S3method( as.list, jrectRef ) + +S3method( "$", "Throwable" ) +S3method( "$<-", "Throwable" ) + +export( javaImport ) + diff --git a/com.oracle.truffle.r.pkgs/rJava/R/0classes.R b/com.oracle.truffle.r.pkgs/rJava/R/0classes.R new file mode 100644 index 0000000000000000000000000000000000000000..1a877b5fa014b4688a3a5c70d20adf2cff0ee106 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/R/0classes.R @@ -0,0 +1,62 @@ +## + # This material is distributed under the GNU General Public License + # Version 2. You may review the terms of this license at + # http://www.gnu.org/licenses/gpl-2.0.html + # + # Copyright (c) 2006 Simon Urbanek <simon.urbanek@r-project.org> + # Copyright (c) 2018 Oracle and/or its affiliates + # + # All rights reserved. +## + +## S4 classes (jobjRef is re-defined in .First.lib to contain valid jobj) +#' java object reference +setClass("jobjRef", representation(jobj="externalptr", jclass="character"), + prototype=list(jobj=NULL, jclass="java/lang/Object")) + +#' rugged arrays +setClass("jarrayRef", representation("jobjRef", jsig="character")) + +#' rectangular java arrays double[][] d = new double[m][n] +setClass("jrectRef", + representation("jarrayRef", dimension="integer" ) ) + + + +# we extend array here so that we can keep dimensions +# in the helper functions below, the storage mode is +# set when the objects are built +# TODO: maybe an initialize method is needed here +# TODO: maybe a validate method is needed here as well +setClass("jfloat", representation("array" ) ) +setClass("jlong", representation("array" ) ) +setClass("jbyte", representation("array" ) ) +setClass("jshort", representation("array" ) ) +setClass("jchar", representation("array" ) ) + +# there is no way to distinguish between double and float in R, so we need to mark floats specifically +.jfloat <- function(x) { + storage.mode( x ) <- "double" + new("jfloat", x ) +} +# the same applies to long +.jlong <- function(x) { + storage.mode( x ) <- "double" + new("jlong", x) +} +# and byte +.jbyte <- function(x) { + storage.mode( x ) <- "integer" + new("jbyte", x) +} +# and short +.jshort <- function(x){ + storage.mode( x ) <- "integer" + new("jshort", x) +} +# and char (experimental) +.jchar <- function(x){ + storage.mode( x ) <- "integer" + new("jchar", as.integer(x)) +} + diff --git a/com.oracle.truffle.r.pkgs/rJava/R/J.R b/com.oracle.truffle.r.pkgs/rJava/R/J.R new file mode 100644 index 0000000000000000000000000000000000000000..59cb21061c77ce7e8f5dd8c2dc9e50979ec3297f --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/R/J.R @@ -0,0 +1,52 @@ +## + # This material is distributed under the GNU General Public License + # Version 2. You may review the terms of this license at + # http://www.gnu.org/licenses/gpl-2.0.html + # + # Copyright (c) 2006 Simon Urbanek <simon.urbanek@r-project.org> + # Copyright (c) 2018, Oracle and/or its affiliates + # + # All rights reserved. +## + +setClass("jclassName", representation(name="character", jobj="jobjRef")) +jclassName <- function(class){ + if( is( class, "jobjRef" ) && .jinherits(class, "java/lang/Class" ) ){ + jobj <- class + name <- .jcall( class, "Ljava/lang/String;", "getName", evalString = TRUE ) + } else{ + name <- gsub("/",".",as.character(class)) + jobj <- .jfindClass(as.character(class)) + } + new("jclassName", name=name, jobj=jobj) +} + +setGeneric("new") +setMethod("new", signature(Class="jclassName"), function(Class, ...) .J(Class@name, ...)) + +setMethod("$", c(x="jclassName"), function(x, name) { + if( name == "class" ){ + x@jobj + } else if (classHasField(x@jobj, name, TRUE)){ + .jfield(x@name, , name) + } else if (classHasMethod(x@jobj, name, TRUE)){ + function(...) .jrcall(x@name, name, ...) + } else if( classHasClass(x@jobj, name, FALSE) ){ + inner.cl <- .jcall( "RJavaTools", "Ljava/lang/Class;", "getClass", x@jobj, name, FALSE ) + new("jclassName", name=.jcall(inner.cl, "S", "getName"), jobj=inner.cl) + } else { + stop("no static field, method or inner class called `", name, "' in `", x@name, "'") + } +}) +setMethod("$<-", c(x="jclassName"), function(x, name, value) { + .jfield(x@jobj, name) <- value + # FASTR <<<<< + # Fix: return x, otherwise LHS of $<- is overriden with + # the result of .jfield(x@jobj, name) <- value which is the field value + x +}) +setMethod("show", c(object="jclassName"), function(object) invisible(show(paste("Java-Class-Name:",object@name)))) +setMethod("as.character", c(x="jclassName"), function(x, ...) x@name) + +## the magic `J' +J<-function(class, method, ...) if (nargs() == 1L && missing(method)) jclassName(class) else .jrcall(class, method, ...) diff --git a/com.oracle.truffle.r.pkgs/rJava/R/arrays.R b/com.oracle.truffle.r.pkgs/rJava/R/arrays.R new file mode 100644 index 0000000000000000000000000000000000000000..2d4b1f6108a4d73a9bfc6ba93c5c7abc6bf05269 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/R/arrays.R @@ -0,0 +1,736 @@ +## + # This material is distributed under the GNU General Public License + # Version 2. You may review the terms of this license at + # http://www.gnu.org/licenses/gpl-2.0.html + # + # Copyright (c) 2006 Simon Urbanek <simon.urbanek@r-project.org> + # Copyright (c) 2018, Oracle and/or its affiliates + # + # All rights reserved. +## + +# :tabSize=4:indentSize=4:noTabs=false:folding=explicit:collapseFolds=1: + +# {{{ utilities to deal with arrays +#' Indicates if a object refers to a java array +#' +#' @param o object +#' @return TRUE if the object is a java array, FALSE if not +#' (including when the object is not even a java reference) +isJavaArray <- function( o ){ + if( ( is( o, "jobjRef" ) || is( o, "jarrayRef") || is( o, "jrectRef") ) && !is.jnull(o) ){ + .jcall( "RJavaArrayTools", "Z", "isArray", .jcast(o) ) + } else FALSE +} +._must_be_java_array <- function( o, message = "object is not a java array" ){ + if( !isJavaArray(o ) ){ + stop( message ) + } +} +isJavaArraySignature <- function( sig ){ + identical( substr( sig, 1, 1 ), '[' ) +} + +#' get the component type of a java array +getComponentType <- function( o, check = TRUE ){ + if( check ) ._must_be_java_array( o ) + .jcall( .jcall( o, "Ljava/lang/Class;", "getClass" ), "Ljava/lang/Class;", "getComponentType" ) +} + +._jarray_simplify <- function( x ){ + ._must_be_java_array( x ) + clname <- .jclass(x, true = TRUE ) + + Array <- "java/lang/reflect/Array" + obj <- switch( clname, + # deal with array of primitive first + "[I" = .Call(RgetIntArrayCont , x@jobj), + "[J" = .Call(RgetLongArrayCont , x@jobj), + "[Z" = .Call(RgetBoolArrayCont , x@jobj) , + "[B" = .Call(RgetByteArrayCont , x@jobj) , + "[D" = .Call(RgetDoubleArrayCont, x@jobj) , + "[S" = .Call(RgetShortArrayCont , x@jobj) , + "[C" = .Call(RgetCharArrayCont , x@jobj) , + "[F" = .Call(RgetFloatArrayCont , x@jobj) , + "[Ljava.lang.String;" = .Call(RgetStringArrayCont, x@jobj), + + # otherwise, just get the object + x ) + obj +} +# }}} + +# {{{ length +#' get the length of the array +._length_java_array <- function(x){ + if( isJavaArray( x ) ){ + .jcall( "java/lang/reflect/Array", "I", "getLength", .jcast( x, check = FALSE, convert.array = FALSE) ) + } else{ + stop( "the supplied object is not a java array" ) + } +} + +setMethod( "length", "jarrayRef", ._length_java_array ) +setMethod( "length", "jrectRef", ._length_java_array ) + +setGeneric( "str" ) +setMethod("str", "jarrayRef", function(object, ...){ + txt <- sprintf( "Formal class 'jarrayRef' [package \"rJava\"] with 2 slots + ..@ jobj :<externalptr> + ..@ jclass: chr \"%s\" + ..@ jsig : chr \"%s\" +", object@jclass, object@jsig ) + cat( txt ) +} ) +setMethod("str", "jrectRef", function(object, ...){ + dim <- object@dimension + dim.txt <- if( length( dim ) == 1L ){ + sprintf( "int %d", dim ) + } else { + sprintf( "int[1:%d] %s", length(dim), paste( if( length(dim) > 6 ) c( dim[1:6], "...") else dim, collapse = " ") ) + } + txt <- sprintf( "Formal class 'jrectRef' [package \"rJava\"] with 2 slots + ..@ jobj :<externalptr> + ..@ jclass : chr \"%s\" + ..@ jsig : chr \"%s\" + ..@ dimension: %s +", object@jclass, object@jsig, dim.txt ) + cat( txt ) +} ) +# }}} + +# {{{ single bracket indexing : [ + +# indexing of .jarrayRef +# is is not quite clear what the proper result should be, because technically +# [ should always return a jarrayRef, but it's not the most useful thing to do. +# the code below (ab)uses drop to try to deal with that, but it's not optimal ... + +# ._jctype <- function(x) if (is.jnull(x)) NA else if(is(x, "jarrayRef")) x@jsig else paste("L", x@jclass, ";", sep='') + +# #' index a java array +# #' +# #' @param x a reference to a java array +# #' @param i indexer (only 1D indexing supported so far) +# #' @param drop if the result if of length 1, just return the java object instead of an array of length one +# #' @param simplify further simplify the result +# ._java_array_single_indexer <- function( x, i, j, drop, simplify = FALSE, silent = FALSE, ... ){ +# # arrays only +# +# if( !silent ){ +# if( ! missing( j ) ){ +# warning( "only one dimensional indexing is currently supported in i, ignoring j argument" ) +# } +# dots <- list( ... ) +# if( length(dots) ){ +# unnamed.dots <- dots[ names(dots) == "" ] +# if( length( unnamed.dots ) ){ +# warning( "only one dimensional indexing is currently supported in [, ignoring ... arguments" ) +# } +# } +# } +# +# # the component type of the array - maybe used to make +# # arrays with the same component type, but of length 0 +# component.type <- getComponentType( x, check = FALSE ) +# +# # 'eval' the array +# ja <- .jevalArray( x ) +# +# # native type - use R subsetting and maybe remap to java +# if (!is.list(ja)) { +# # perform the subset +# o <- ja[i] +# +# # return native type if simplify +# if( simplify ){ +# return(o) +# } +# +# if( length(o) == 0L) { +# # return an array of the same component type as the original array +# # but of length 0 +# return( .jcall( "java/lang/reflect/Array", "Ljava/lang/Object;", "newInstance", component.type, 0L ) ) +# } else { +# # drop makes no sense here +# return( .jarray( o ) ) +# } +# } +# +# # the result an array of java objects +# sl <- ja[i] +# +# if( length( sl ) == 0L ){ +# # TODO: make simplify influencial here +# # for example if x is int[] then we want to get integer(0) +# return( .jcall( "java/lang/reflect/Array", "Ljava/lang/Object;", "newInstance", component.type, 0L ) ) +# } else{ +# # just return the array +# return( .jarray( sl ) ) +# } +# } + +# ## this is all weird - we need to distinguish between x[i] and x[i,] yet S4 fails to do so ... +setMethod( "[", signature( x = "jarrayRef" ), + function(x, i, j, ..., drop = FALSE){ + # the code above is not good enough + .NotYetImplemented() + } ) +# }}} + +# {{{ double bracket indexing : [[ +._collectIndex <- function( i, j, ...){ + dots <- list( ... ) + unnamed.dots <- if( length( dots ) ){ + dots[ names(dots) == "" ] + } + + firstInteger <- function(.) as.integer(.)[1] + firstIntegerOfEach <- function(.) sapply( ., firstInteger ) + + index <- c( + if( !missing(i) ) firstInteger(i), + if( !missing(j) ) firstInteger(j), + if( !is.null(unnamed.dots) && length(unnamed.dots) ) firstIntegerOfEach( unnamed.dots ) + ) +} + +# R version of RJavaArrayTools#getDimensionLength +# it only works on the signature so should be used with caution +getDimensionLength <- function( x, true.class = TRUE ){ + nchar( sub( "[^[]+", "", .jclass(x, true = true.class) ) ) +} + +# R version of RJavaArrayTools#getObjectTypeName +getObjectTypeName <- function( x, true.class=TRUE){ + sub( "^[[]*(.*);?$", "\\1", .jclass(x, true = true.class) ) +} + +._java_array_double_indexer <- function( x, i, j, ..., evalArray = FALSE, evalString = FALSE ){ + # initial checks + ._must_be_java_array( x ) + index <- ._collectIndex( i, j, ... ) + + if( !length(index) || is.null(index) ){ + # return the full object + x + } else{ + + # shift one left (java style indexing starts from 0 ) + index <- index - 1L + depth <- getDimensionLength( x ) + typename <- getObjectTypeName( x ) + + if( length( index) == depth ){ + # we need to dispatch primitive + if( isPrimitiveTypeName( typename ) ){ + res <- switch( typename, + # deal with array of primitive first + "I" = .jcall( "RJavaArrayTools", "I", "getInt" , .jcast(x), index ) , + "J" = .jcall( "RJavaArrayTools", "J", "getLong" , .jcast(x), index ) , + "Z" = .jcall( "RJavaArrayTools", "Z", "getBoolean", .jcast(x), index ) , + "B" = .jcall( "RJavaArrayTools", "B", "getByte" , .jcast(x), index ) , + "D" = .jcall( "RJavaArrayTools", "D", "getDouble" , .jcast(x), index ) , + "S" = .jcall( "RJavaArrayTools", "S", "getShort" , .jcast(x), index ) , + "C" = .jcall( "RJavaArrayTools", "C", "getChar" , .jcast(x), index ) , + "F" = .jcall( "RJavaArrayTools", "F", "getFloat" , .jcast(x), index ), + stop( "wrong primitive" ) # should never happen + ) + return( res ) + } + + } + + # otherwise use the Object version + .jcall( "RJavaArrayTools", "Ljava/lang/Object;", "get", .jcast(x), index, + evalArray = evalArray, evalString = evalString ) + } + +} + +# this is the only case that makes sense: i is an integer or a numeric of length one +# we cannot use logical indexing or indexing by name because there is no such thing in java +setMethod( "[[", signature( x = "jarrayRef" ), + function(x, i, j, ...){ + ._java_array_double_indexer( x, i, j, ... ) + } ) + +._java_array_double_replacer <- function( x, i, j, ..., value ){ + # initial checks + ._must_be_java_array( x ) + + index <- ._collectIndex( i, j, ... ) + + if( !length(index) || is.null(index) ){ + # allow for x[[]] <- value + newArray( value , simplify = FALSE ) + } else{ + jvalue <- ._java_valid_object( value ) + if( ._isPrimitiveReference( value ) ){ + # then use a primitive version + .jcall( "RJavaArrayTools", "V", "set", .jcast(x), + index - 1L, value ) + } else{ + # use the Object version + .jcall( "RJavaArrayTools", "V", "set", .jcast(x), + index - 1L, .jcast( jvalue ) ) + if( isJavaArray( jvalue ) ){ + # rectangularity might have changed + # we have no choice but to reset the array + x <- newArray( jobj = x@jobj, signature = x@jsig ) + } + } + x + } + +} + +setReplaceMethod( "[[", signature( x = "jarrayRef" ), +function(x, i, j, ..., value ){ + ._java_array_double_replacer( x, i, j, ..., value = value) +} ) +# }}} + +# {{{ head and tail +setGeneric( "head" ) +setMethod("head", signature( x = "jarrayRef" ), function(x, n = 6L, ... ){ + if( !isJavaArray( x ) ){ + stop( "not a java array" ) + } + # FIXME : this only makes sense for 1d arays + n_objs <- length(x) + if( abs( n ) >= n_objs ){ + return( x ) + } + len <- if( n > 0L ) n else n_objs + n + x[seq_len(n), ... ] +} ) + +setGeneric( "tail" ) +setMethod("tail", signature( x = "jarrayRef" ), function(x, n = 6L, ... ){ + if( !isJavaArray( x ) ){ + stop( "not a java array" ) + } + # FIXME : this only makes sense for 1d arays + n_objs <- length(x) + if( abs( n ) >= n_objs ) return(x) + if( n < 0L){ + n <- n_objs + n + } + return( x[ seq.int( n_objs-n+1, n_objs ) , ... ] ) +} ) +# }}} + +# {{{ newArray - dispatch to jarrayRef or jrectRef +#' creates a new jarrayRef or jrectRef depending on the rectangularity +#' of the array +#' +#' @param o a jobjRef object +#' @param simplify if TRUE and the result is a rectangular array +#' of primitives, simplify it to an R object +newArray <- function( o, simplify = TRUE, jobj, signature ){ + if( !missing(jobj) ){ + o <- new("jobjRef", jobj = jobj, jclass = signature) + } + if( !isJavaArray( o ) ){ + stop( "o does not refer to a java array" ) + } + if( inherits( o, "jrectRef" ) ){ + # no need to go further + return(o) + } + + clazz <- tojni( .jclass( o, true = TRUE ) ) + wrapper <- .jnew("ArrayWrapper", .jcast(o) ) + isRect <- .jcall( wrapper, "Z", "isRectangular" ) + if( isRect ){ + dims <- .jcall( wrapper, "[I", "getDimensions" ) + + if( !simplify ){ + # no need to go further down, return a reference + return( new( "jrectRef", jobj = o@jobj, jsig = clazz, jclass = clazz, + dimension = dims ) ) + } + + isprim <- .jcall( wrapper, "Z", "isPrimitive" ) + typename <- .jcall( wrapper, "Ljava/lang/String;", "getObjectTypeName" ) + isstrings <- identical( typename, "java.lang.String" ) + + if( !isprim && !isstrings ){ + # cannot simplify, return a reference + return( new( "jrectRef", jobj = o@jobj, jsig = clazz, jclass = clazz, + dimension = dims ) ) + } + + if( isprim || isstrings ){ + # array of java primitives, we can translate this to R array + out <- structure( switch( typename , + "I" = .jcall( wrapper, "[I" , "flat_int" ), + "Z" = .jcall( wrapper, "[Z" , "flat_boolean" ), + "B" = .jcall( wrapper, "[B" , "flat_byte" ), + "J" = .jlong( .jcall( wrapper, "[J" , "flat_long" ) ), + "S" = .jshort( .jcall( wrapper, "[T" , "flat_short" ) ), # [T is remapped to [S in .jcall + "D" = .jcall( wrapper, "[D" , "flat_double" ), + "C" = .jchar( .jcall( wrapper, "[C" , "flat_char" ) ), + "F" = .jfloat( .jcall( wrapper, "[F" , "flat_float" ) ), + "java.lang.String" = .jcall( wrapper, "[Ljava/lang/String;", "flat_String" ), + stop( sprintf("cannot simplify type : ", typename) ) # this should not happen + ), dim = dims ) + return( out ) + } + + } else { + # not a rectangular array -> jarrayRef + new( "jarrayRef", jobj = o@jobj, jsig = clazz, jclass = clazz ) + } +} +# }}} + +# {{{ [ indexing of rectangular arrays +setMethod( "[", signature( x = "jrectRef" ), + function(x, i, j, ..., simplify = FALSE, drop = TRUE ){ + + # first we extract th data as a flat (one dimensional) R array + # called 'flat' + + dim <- x@dimension + wrapper <- .jnew( "ArrayWrapper", .jcast(x) ) + + typename <- .jcall( wrapper, "Ljava/lang/String;", "getObjectTypeName" ) + isprim <- .jcall( wrapper, "Z", "isPrimitive" ) + + flat <- switch( typename, + "I" = .jcall( wrapper, "[I" , "flat_int" , evalArray = TRUE ), + "Z" = .jcall( wrapper, "[Z" , "flat_boolean" , evalArray = TRUE ), + "B" = .jcall( wrapper, "[B" , "flat_byte" , evalArray = TRUE ), + "J" = .jcall( wrapper, "[J" , "flat_long" , evalArray = TRUE ), + "S" = .jcall( wrapper, "[T" , "flat_short" , evalArray = TRUE ), # [T is remapped to [S in .jcall + "D" = .jcall( wrapper, "[D" , "flat_double" , evalArray = TRUE ), + "C" = .jcall( wrapper, "[C" , "flat_char" , evalArray = TRUE ) , + "F" = .jcall( wrapper, "[F" , "flat_float" , evalArray = TRUE ), + "java.lang.String" = .jcall( wrapper, "[Ljava/lang/String;" , "flat_String" , evalArray = TRUE ), + .jcall( wrapper, "[Ljava/lang/Object;" , "flat_Object" , evalArray = TRUE ) ) + + # then we give to flat the correct dimensions + if( length(dim) != 1L ){ + dim( flat ) <- dim + } + + # now we construct the call to '[' on flat. + # this call uses all the regular R indexing + call <- match.call( call = sys.call(sys.parent()) ) + n.args <- nargs( ) + + e <- as.list( call )[ -(1:2) ] + names.e <- names(e) + if( any( have.name <- (names.e != "") ) ){ + # we need to extract drop and simplify + nam <- names.e[ have.name ] + if( !all( nam %in% c("simplify", "drop", "i", "j" ) ) ){ + stop( "only 'drop' and 'simplify' are allowed as named arguments, they need to be written exactly" ) + } + } + + if( missing(i) && missing(j) && all( names.e != "" ) ){ + # special case with no indexing at all + actual.call <- sprintf( "flat[ , drop = %s ]", as.character(drop) ) + } else if( !missing(i) && missing(j) && all( names.e != "" ) ){ + # special case where there is only one index + actual.call <- sprintf( "flat[ %s , drop = %s ]", deparse(i), as.character(drop) ) + } else{ + # we need to be careful about the missing's + # we cannot just do things like list(...) because with missings + # it just does not work + actual.call <- "flat[" + + itoken <- if( missing(i ) ) " " else deparse(i) + jtoken <- if( missing(j ) ) " " else deparse(j) + + actual.call <- sprintf( "flat[ %s , %s", itoken, jtoken ) + + iii <- 1L + for( a in e ){ + if( missing(a) ){ + actual.call <- sprintf( "%s , ", actual.call ) + } else if( have.name[iii] ) { + # we put both at the end + } else { + # not missing, not named + actual.call <- sprintf( "%s, %s", actual.call, deparse(a) ) + } + iii <- iii + 1L + } + actual.call <- sprintf( "%s, drop = %s ]", actual.call, as.character(drop) ) + } + + # now we eval the call + subs <- eval( parse( text = actual.call ) ) + + # now if we need and can simplify it, we return the subsetted array as is + # otherwise, we rewrap it to java + if( simplify && (typename == "java.lang.String" || isprim ) ) subs else .jarray( subs, dispatch = TRUE ) + + } ) +# }}} + +# {{{ dim.jrectRef +setMethod( "dim", signature( x = "jrectRef" ), function(x) x@dimension ) +setReplaceMethod( "dim", signature( x = "jrectRef" ), function(x, value){ + + expected_prod <- prod( x@dimension ) + + if( is.null( value ) ){ + value <- expected_prod + } else{ + received_prod <- prod(value) + if( received_prod != expected_prod ){ + stop( sprintf("dims [product %d] do not match the length of object [%d]", received_prod, expected_prod ) ) + } + } + dim <- x@dimension + wrapper <- .jnew( "ArrayWrapper", .jcast(x) ) + + typename <- .jcall( wrapper, "Ljava/lang/String;", "getObjectTypeName" ) + + flat <- structure( + switch( typename, + "I" = .jcall( wrapper, "[I" , "flat_int" , evalArray = TRUE ), + "Z" = .jcall( wrapper, "[Z" , "flat_boolean" , evalArray = TRUE ), + "B" = .jcall( wrapper, "[B" , "flat_byte" , evalArray = TRUE ), + "J" = .jcall( wrapper, "[J" , "flat_long" , evalArray = TRUE ), + "S" = .jcall( wrapper, "[T" , "flat_short" , evalArray = TRUE ), # [T is remapped to [S in .jcall + "D" = .jcall( wrapper, "[D" , "flat_double" , evalArray = TRUE ), + "C" = .jcall( wrapper, "[C" , "flat_char" , evalArray = TRUE ) , + "F" = .jcall( wrapper, "[F" , "flat_float" , evalArray = TRUE ), + "java.lang.String" = .jcall( wrapper, "[Ljava/lang/String;" , "flat_String" , evalArray = TRUE ), + .jcall( wrapper, "[Ljava/lang/Object;" , "flat_Object" , evalArray = TRUE ) ) , + dim = value ) + + .jarray(flat, dispatch = TRUE) + +} ) +# }}} + +PRIMITIVE_TYPES <- c( "I", "Z", "B", "J", "S", "D", "C", "F" ) +isPrimitiveTypeName <- function( type, include.strings = TRUE ){ + type %in% PRIMITIVE_TYPES || ( include.strings && identical( type, "java.lang.String" ) ) +} +PRIMITIVE_TYPES_RX <- sprintf( "^[[]+[%s]$" , paste( PRIMITIVE_TYPES, collapse = "" ) ) +isPrimitiveArraySignature <- function( x, ... ){ + regexpr( PRIMITIVE_TYPES_RX, x, ... ) > 0 +} +isArraySignature <- function( x ){ + substr( x, 1, 1 ) == "[" +} + +# {{{ unique.jarrayRef +setGeneric( "unique" ) +._unique_jrectRef <- function( x, incomparables = FALSE, ...){ + + dim <- x@dimension + + if( length( dim ) > 1L ){ + stop( "'unique' only implemented for 1d array so far" ) + } + + typename <- .jcall( "RJavaArrayTools", "Ljava/lang/String;", + "getObjectTypeName", .jcast(x) ) + + if( isPrimitiveTypeName( typename, include.strings = TRUE ) ){ + .jarray( unique( .jevalArray( x ) ), dispatch = TRUE ) + } else{ + .jcall( "RJavaArrayTools", "[Ljava/lang/Object;", "unique", + .jcast( x, "[Ljava/lang/Object;" ), evalArray = TRUE, simplify = TRUE ) + } +} + +setMethod( "unique", "jarrayRef", function(x, incomparables = FALSE, ...){ + .NotYetImplemented() +} ) +setMethod( "unique", "jrectRef", ._unique_jrectRef ) +# }}} + +# {{{ duplicated +setGeneric( "duplicated" ) +._duplicated_jrectRef <- function( x, incomparables = FALSE, ...){ + + dim <- x@dimension + + if( length( dim ) > 1L ){ + stop( "'duplicated' only implemented for 1d array so far" ) + } + + typename <- .jcall( "RJavaArrayTools", "Ljava/lang/String;", + "getObjectTypeName", .jcast(x) ) + + if( isPrimitiveTypeName( typename, include.strings = TRUE ) ){ + duplicated( .jevalArray( x ) ) + } else{ + .jcall( "RJavaArrayTools", "[Z", "duplicated", + .jcast( x, "[Ljava/lang/Object;" ), evalArray = TRUE ) + } +} +setMethod( "duplicated", "jrectRef", ._duplicated_jrectRef ) +setMethod( "duplicated", "jarrayRef", function( x, incomparables = FALSE, ...){ + .NotYetImplemented() +}) +# }}} + +# {{{ anyDuplicated +.base.has.anyDuplicated <- exists("anyDuplicated", asNamespace("base")) +if (!.base.has.anyDuplicated) { + anyDuplicated <- function(x, incomparables = FALSE, ...) UseMethod("anyDuplicated") +} +setGeneric( "anyDuplicated" ) +._anyduplicated_jrectRef <- function( x, incomparables = FALSE, ...){ + + dim <- x@dimension + if( length( dim ) > 1L ){ + stop( "'anyDuplicated' only implemented for 1d array so far" ) + } + typename <- .jcall( "RJavaArrayTools", "Ljava/lang/String;", + "getObjectTypeName", .jcast(x) ) + if( isPrimitiveTypeName( typename, include.strings = TRUE ) ){ + anyDuplicated( .jevalArray( x ) ) + } else{ + .jcall( "RJavaArrayTools", "I", "anyDuplicated", + .jcast( x, "[Ljava/lang/Object;" ), evalArray = TRUE ) + 1L + } +} +setMethod( "anyDuplicated", "jrectRef", ._anyduplicated_jrectRef ) +setMethod( "anyDuplicated", "jarrayRef", function( x, incomparables = FALSE, ...){ + .NotYetImplemented() +}) +# }}} + +# {{{ flat +#' utility to flatten an array +flat <- function(x, simplify = FALSE){ + stop( "undefined" ) +} +setGeneric( "flat") +._flat_jrectRef <- function( x, simplify = FALSE ){ + dim <- dim(x) + if( length(dim) == 1L ) { + if( !simplify) x else x[ simplify = TRUE ] + } else { + x[ seq_len(prod(dim)), drop = TRUE, simplify = simplify ] + } +} +setMethod( "flat", "jrectRef", ._flat_jrectRef ) +setMethod( "flat", "jarrayRef", function(x, simplify=FALSE){ + .NotYetImplemented() +} ) +# }}} + +# {{{ sort +setGeneric( "sort" ) +._sort_jrectRef <- function( x, decreasing = FALSE, ...){ + + x <- flat( x ) + dim <- x@dimension + typename <- .jcall( "RJavaArrayTools", "Ljava/lang/String;", + "getObjectTypeName", .jcast(x) ) + + if( isPrimitiveTypeName( typename, include.strings = TRUE ) ){ + .jarray( sort( .jevalArray( x ), decreasing = decreasing ), dispatch = TRUE ) + } else{ + .jcall( "RJavaArrayTools", "[Ljava/lang/Object;", "sort", + .jcast( x, "[Ljava/lang/Object;" ), decreasing, evalArray = TRUE, simplify = TRUE ) + } + +} +setMethod( "sort", "jrectRef", ._sort_jrectRef ) +setMethod( "sort", "jarrayRef", function(x, decreasing=FALSE, ...){ + .NotYetImplemented() +}) +# }}} + +# {{{ rev +setGeneric( "rev" ) +setMethod( "rev", "jrectRef", function(x){ + x <- flat( x ) + dim <- x@dimension + typename <- .jcall( "RJavaArrayTools", "Ljava/lang/String;", + "getObjectTypeName", .jcast(x) ) + + if( isPrimitiveTypeName( typename, include.strings = TRUE ) ){ + .jarray( rev( .jevalArray( x ) ), dispatch = TRUE ) + } else{ + .jcall( "RJavaArrayTools", "[Ljava/lang/Object;", "rev", + .jcast( x, "[Ljava/lang/Object;" ), evalArray = TRUE, simplify = TRUE ) + } + +} ) +setMethod( "rev", "jarrayRef", function(x){ + .NotYetImplemented() +}) +# }}} + +# {{{ as.list +# S4 dispatch does not work +as.list.jarrayRef <- function(x, ... ){ + .jevalArray( x ) +} +as.list.jrectRef <- function( x, ...){ + .jevalArray( x ) +} +as.list.jobjRef <- function( x, ... ){ + if( ! .jinstanceof( x, "java.lang.Iterable" ) ){ + stop( "only objects that implements java.lang.Iterable can be converted to lists" ) + } + .jcall( "RJavaArrayTools", "[Ljava/lang/Object;", + "getIterableContent", .jcast(x, "java/lang/Iterable") , evalArray = TRUE, ... ) +} +# }}} + +# {{{ min, max, range +setMethod("min", "jrectRef", function(x, ...,na.rm=TRUE){ + + dim <- x@dimension + typename <- .jcall( "RJavaArrayTools", "Ljava/lang/String;", + "getObjectTypeName", .jcast(x) ) + if( isPrimitiveTypeName( typename, include.strings = TRUE ) ){ + min( x[simplify=TRUE], na.rm = na.rm ) + } else{ + summarizer <- .jnew( "RectangularArraySummary", .jcast(x), dim ) + .jcall( summarizer, "Ljava/lang/Object;", "min", na.rm ) + } + +} ) +setMethod("min", "jarrayRef", function(x, ...,na.rm=TRUE){ + .NotYetImplemented() +}) +setMethod("max", "jrectRef", function(x, ..., na.rm=TRUE){ + + dim <- x@dimension + typename <- .jcall( "RJavaArrayTools", "Ljava/lang/String;", + "getObjectTypeName", .jcast(x) ) + if( isPrimitiveTypeName( typename, include.strings = TRUE ) ){ + max( x[simplify=TRUE], na.rm = na.rm ) + } else{ + summarizer <- .jnew( "RectangularArraySummary", .jcast(x), dim ) + .jcall( summarizer, "Ljava/lang/Object;", "max", na.rm ) + } + +} ) +setMethod("max", "jarrayRef", function(x, ..., na.rm=TRUE){ + .NotYetImplemented() +} ) +setMethod("range", "jrectRef", function(x, ..., na.rm=TRUE){ + + dim <- x@dimension + typename <- .jcall( "RJavaArrayTools", "Ljava/lang/String;", + "getObjectTypeName", .jcast(x) ) + if( isPrimitiveTypeName( typename, include.strings = TRUE ) ){ + range( x[simplify=TRUE], na.rm = na.rm ) + } else{ + summarizer <- .jnew( "RectangularArraySummary", .jcast(x), dim ) + .jcall( summarizer, "[Ljava/lang/Object;", "range", na.rm, evalArray = TRUE, simplify = TRUE ) + } + +} ) +setMethod("range", "jarrayRef", function(x, ..., na.rm=TRUE){ + .NotYetImplemented() +}) +# }}} diff --git a/com.oracle.truffle.r.pkgs/rJava/R/call.R b/com.oracle.truffle.r.pkgs/rJava/R/call.R new file mode 100644 index 0000000000000000000000000000000000000000..8bd0cb94b188ced9dee1728735f612835879cad5 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/R/call.R @@ -0,0 +1,436 @@ +## + # This material is distributed under the GNU General Public License + # Version 2. You may review the terms of this license at + # http://www.gnu.org/licenses/gpl-2.0.html + # + # Copyright (c) 2006 Simon Urbanek <simon.urbanek@r-project.org> + # Copyright (c) 2018, Oracle and/or its affiliates + # + # All rights reserved. +## + +## This file is part of the rJava package - low-level R/Java interface +## (C)2006 Simon Urbanek <simon.urbanek@r-project.org> +## For license terms see DESCRIPTION and/or LICENSE +## +## $Id$ + +# create a new object +.jnew <- function(class, ..., check=TRUE, silent=!check, class.loader=NULL) { + class <- gsub("\\.", "/", as.character(class)) # allow non-JNI specifiation + # TODO: should this do "S" > "java/lang/String", ... like .jcall + + if (check) .jcheck(silent=TRUE) + o<-.External(RcreateObject, class, ..., silent=silent, class.loader=class.loader) + if (check) .jcheck(silent=silent) + if (is.null(o)) { + if (!silent) { + stop("Failed to create object of class `",class,"'") + } else { + o <- .jzeroRef + } + } + new("jobjRef", jobj=o, jclass=class) +} + +# create a new object reference manually (avoid! for backward compat only!) +# the problem with this is that you need a valid `jobj' which +# is implementation-dependent so it is undefined outside rJava internals +# it is now used by JRI.createRJavaRef, though +.jmkref <- function(jobj, jclass="java/lang/Object") { + new("jobjRef", jobj=jobj, jclass=gsub('\\.','/',as.character(jclass))) +} + +# evaluates an array reference. If rawJNIRefSignature is set, then obj is not assumed to be +# jarrayRef, but rather direct JNI reference with the corresponding signature +.jevalArray <- function(obj, rawJNIRefSignature=NULL, silent=FALSE, simplify=FALSE) { + jobj<-obj + sig<-rawJNIRefSignature + if (is.null(rawJNIRefSignature)) { + if(!inherits(obj,"jarrayRef")) { + if (!inherits(obj,"jobjRef")) + stop("object is not a Java object reference (jobjRef/jarrayRef).") + cl <- gsub("\\.","/",.jclass(obj)) + if (is.null(cl) || !isJavaArraySignature(cl) ) + stop("object is not a Java array.") + sig <- cl + } else sig <- obj@jsig + jobj<-obj@jobj + } else if (is(obj, "jobjRef")) jobj<-obj@jobj + if (sig=="[I") + return(.Call(RgetIntArrayCont, jobj)) + else if (sig=="[J") + return(.Call(RgetLongArrayCont, jobj)) + else if (sig=="[Z") + return(.Call(RgetBoolArrayCont, jobj)) + else if (sig=="[B") + return(.Call(RgetByteArrayCont, jobj)) + else if (sig=="[D") + return(.Call(RgetDoubleArrayCont, jobj)) + else if (sig=="[S") + return(.Call(RgetShortArrayCont, jobj)) + else if (sig=="[C") + return(.Call(RgetCharArrayCont, jobj)) + else if (sig=="[F") + return(.Call(RgetFloatArrayCont, jobj)) + else if (sig=="[Ljava/lang/String;") + return(.Call(RgetStringArrayCont, jobj)) + else if (sig=="[Ljava/lang/Double;" && simplify) { + obj@jclass <- sig; return(.jcall("RJavaArrayTools", "[D", "unboxDoubles", obj)) } + else if (sig=="[Ljava/lang/Integer;" && simplify) { + obj@jclass <- sig; return(.jcall("RJavaArrayTools", "[I", "unboxIntegers", obj)) } + else if (sig=="[Ljava/lang/Boolean;" && simplify) { + obj@jclass <- sig; return(as.logical(.jcall("RJavaArrayTools", "[I", "unboxBooleans", obj))) } + else if (substr(sig,1,2)=="[L") + return(lapply(.Call(RgetObjectArrayCont, jobj), + function(x) new("jobjRef", jobj=x, jclass=substr(sig, 3, nchar(sig)-1)) )) + else if (substr(sig,1,2)=="[[") { + if (simplify) { # try to figure out if this is a rectangular array in which case we can do better + o <- newArray(simplify=TRUE, jobj=jobj, signature=sig) + # if o is not a reference then we were able to simplify it + if (!is(o, "jobjRef")) return(o) + } + # otherwise simplify has no effect + return(lapply(.Call(RgetObjectArrayCont, jobj), + function(x) newArray(jobj=x, signature=substr(sig, 2, 999), simplify=simplify))) + } + # if we don't know how to evaluate this, issue a warning and return the jarrayRef + if (!silent) + warning(paste("I don't know how to evaluate an array with signature",sig,". Returning a reference.")) + newArray(jobj = jobj, signature = sig, simplify = FALSE) +} + +.jcall <- function(obj, returnSig="V", method, ..., evalArray=TRUE, + evalString=TRUE, check=TRUE, interface="RcallMethod", + simplify=FALSE, use.true.class = FALSE) { + if (check) .jcheck() + iaddr <- .env[[interface]] + # TODO + #interface <- if (is.null(iaddr)) getNativeSymbolInfo(interface, "rJava", TRUE, FALSE)$address else iaddr + r<-NULL + # S is a shortcut for Ljava/lang/String; + if (returnSig=="S") + returnSig<-"Ljava/lang/String;" + if (returnSig=="[S") + returnSig<-"[Ljava/lang/String;" + # original S (short) is now mapped to T so we need to re-map it (we don't really support short, though) + if (returnSig=="T") returnSig <- "S" + if (returnSig=="[T") returnSig <- "[S" + + if (inherits(obj,"jobjRef") || inherits(obj,"jarrayRef") || inherits(obj,"jrectRef") ) + r<-.External(interface, obj@jobj, returnSig, method, ...) + else + r<-.External(interface, as.character(obj), returnSig, method, ...) + + if (returnSig=="V") { + # FASTR <<<<< + # FIX check for exceptions before return; like in other cases in this function + if (check) { + .jcheck() + } + # FASTR >>>>> + return(invisible(NULL)) + } + + if( use.true.class && !is.null( r ) ){ + if( ! ( isPrimitiveTypeName(returnSig) || isArraySignature(returnSig) ) ){ + # FASTR <<<<< + # retrieve the clazzname via fastr interop + + # avoid calling .jcall since we work on external pointers directly here + #clazz <- .External(interface, r , "Ljava/lang/Class;", "getClass") + #clazzname <- .External(interface, clazz, "Ljava/lang/String;", "getName") + #clazzname <- .External("RgetStringValue", clazzname) + o <- r + if(inherits(r, "externalptr")) { + o <- attr(r, "external.object") + } + clazzname <- attr(r, "external.classname") + if(is.null(clazzname)) { + # returnSig is not primitive, but we might have an unboxed primitive value + # typicaly, this can happen when called from .jrcall (in cases like rJavaObject$someMethodReturningInt()) + # and .jrcall for now always simplyfies the return value and dumps the class name anyway + # so lets retrieve it "just in case" + if (is.character(o)) { + clazzname <- "java.lang.String" + } else if (is.integer(o)) { + clazzname <- "java.lang.Integer" + } else if (is.double(o)) { + clazzname <- "java.lang.Double" + } else if (is.logical(o)) { + clazzname <- "java.lang.Boolean" + } else { + clazzname <- java.class(o, getClassName = TRUE) + } + } + # FASTR >>>>>> + returnSig <- tojniSignature( clazzname ) + } + } + + if (isJavaArraySignature(returnSig)) { + # eval or return a reference + r <- if (evalArray) .jevalArray(r, rawJNIRefSignature=returnSig, simplify=simplify) else newArray(jobj = r, signature = returnSig, simplify = FALSE) + } else if ( substr(returnSig,1,1)=="L") { + if (is.null(r)){ + if( check ) .jcheck( silent = FALSE ) + return(r) + } + + if (returnSig=="Ljava/lang/String;" && evalString){ + if( check ) .jcheck( silent = FALSE ) + return(.External(RgetStringValue, r)) + } + r <- new("jobjRef", jobj=r, jclass=substr(returnSig,2,nchar(returnSig)-1)) + } + if (check) .jcheck() + if (.conv.in$.) .convert.in(r) else r +} + +.jstrVal <- function(obj) { + # .jstrVal(.jstrVal(...)) = .jstrVal(...) + if (is.character(obj)) + return(obj) + r<-NULL + if (!is(obj,"jobjRef")) + stop("can get value of Java objects only") + if (!is.null(obj@jclass) && obj@jclass=="lang/java/String") + r<-.External(RgetStringValue, obj@jobj) + else + r<-.External(RtoString, obj@jobj) + r +} + +#' casts java object into new.class +#' +#' @param obj a java object reference +#' @param new.class the new class (in JNI or Java) +#' @param check logical. If TRUE the cast if checked +#' @param convert.array logical. If TRUE and the new class represents an array, then a jarrayRef object is made +.jcast <- function(obj, new.class="java/lang/Object", check = FALSE, convert.array = FALSE) { + if (!is(obj,"jobjRef")) + stop("cannot cast anything but Java objects") + if( check && !.jinstanceof( obj, new.class) ){ + stop( sprintf( "cannot cast object to '%s'", new.class ) ) + } + + new.class <- gsub("\\.","/", as.character(new.class)) # allow non-JNI specifiation + if( convert.array && !is( obj, "jarrayRef" ) && isJavaArray( obj ) ){ + r <- .jcastToArray( obj, signature = new.class) + } else { + r <- obj + r@jclass <- new.class + } + r +} + +# makes sure that a given object is jarrayRef +.jcastToArray <- function(obj, signature=NULL, class="", quiet=FALSE) { + if (!is(obj, "jobjRef")) + return(.jarray(obj)) + if (is.null(signature)) { + # TODO: factor out these two calls into a separate function + cl <- .jcall(obj, "Ljava/lang/Class;", "getClass") + cn <- .jcall(cl, "Ljava/lang/String;", "getName") + if ( !isJavaArraySignature(cn) ) { + if (quiet) + return(obj) + else + stop("cannot cast to array, object signature is unknown and class name is not an array") + } + signature <- cn + } else{ + if( !isJavaArraySignature(signature) ){ + if( quiet ) { + return( obj ) + } else{ + stop( "cannot cast to array, signature is not an array signature" ) + } + } + } + signature <- gsub('\\.', '/', signature) + if (inherits(obj, "jarrayRef")) { + obj@jsig <- signature + return(obj) + } + newArray(obj, simplify=FALSE) +} + +# creates a new "null" object of the specified class +# although it sounds weird, the class is important when passed as +# a parameter (you can even cast the result) +.jnull <- function(class="java/lang/Object") { + new("jobjRef", jobj=.jzeroRef, jclass=as.character(class)) +} + +.jcheck <- function(silent=FALSE) invisible(.Call(RJavaCheckExceptions, silent)) + +.jproperty <- function(key) { + if (length(key)>1) + sapply(key, .jproperty) + else + .jcall("java/lang/System", "S", "getProperty", as.character(key)[1]) +} + +#' gets the dim of an array, or its length if it is just a vector +getDim <- function(x){ + dim <- dim(x) + if( is.null( dim ) ) dim <- length(x) + dim +} + +.jarray <- function(x, contents.class = NULL, dispatch = FALSE) { + # this already is an array, so don't bother + if( isJavaArray( x ) ) return( newArray( x, simplify = FALSE) ) + + # this is a two stage process, first we need to convert into + # a flat array using the jni code + # TODO: but this needs to move to the internal jni world to avoid + # too many copies + + # common mistake is to not specify a list but just a single Java object + # but, well, people just keep doing it so we may as well support it + dim <- if (inherits(x,"jobjRef")) { + x <- list(x) + 1L + } else getDim(x) + + # the jni call + array <- .Call(RcreateArray, x, contents.class) + + if (!dispatch) return( array ) + + if( is.list( x ) ){ + # if the input of RcreateArray was a list, we need some more care + # because we cannot be sure the array is rectangular so we have to + # check it + newArray( array, simplify = FALSE ) + } else { + + # then we transform this to a rectangular array of the proper dimensions + if( length( dim ) == 1L ) { + # single dimension array + new( "jrectRef", jobj = array@jobj, jsig = array@jsig, + jclass = array@jclass, dimension = dim ) + } else { + builder <- .jnew( "RectangularArrayBuilder", .jcast(array), dim ) + clazz <- .jcall( builder, "Ljava/lang/String;", "getArrayClassName" ) + + # we cannot use .jcall here since it will try to simplify the array + # or go back to java to calculate its dimensions, ... + r <- .External( "RcallMethod", builder@jobj, + "Ljava/lang/Object;", "getArray", PACKAGE="rJava") + + new( "jrectRef", jobj = r, dimension = dim, + jclass = clazz, jsig = tojni( clazz ) ) + } + } +} + +# works on EXTPTR or jobjRef or NULL. NULL is always silently converted to .jzeroRef +.jidenticalRef <- function(a,b) { + if (is(a,"jobjRef")) a<-a@jobj + if (is(b,"jobjRef")) b<-b@jobj + if (is.null(a)) a <- .jzeroRef + if (is.null(b)) b <- .jzeroRef + if (!inherits(a,"externalptr") || !inherits(b,"externalptr")) stop("Invalid argument to .jidenticalRef, must be a pointer or jobjRef") + .Call(RidenticalRef,a,b) +} + +# returns TRUE only for NULL or jobjRef with jobj=0x0 +is.jnull <- function(x) { + (is.null(x) || (is(x,"jobjRef") && .jidenticalRef(x@jobj,.jzeroRef))) +} + +# should we move this to C? +.jclassRef <- function(x, silent=FALSE) { + if (is.jnull(x)) { + if (silent) return(NULL) else stop("null reference has no class") + } + if (!is(x, "jobjRef")) { + if (silent) return(NULL) else stop("invalid object") + } + cl <- NULL + try(cl <- .jcall(x, "Ljava/lang/Class;", "getClass", check=FALSE)) + .jcheck(silent=TRUE) + if (is.jnull(cl) && !silent) stop("cannot get class object") + cl +} + +# return class object for a given class name; silent determines whether +# an error should be thrown on failure (FALSE) or just null reference (TRUE) +.jfindClass <- function(cl, silent=FALSE) { + # FASTR TODO check all usecases, some of them might try to do e.g. newInstance, getMethods (if methods are a problem too?) etc + if (inherits(cl, "jclassName")) return(cl@jobj) + if (!is.character(cl) || length(cl)!=1) + stop("invalid class name") + cl<-gsub("/",".",cl) + a <- NULL + if (!is.jnull(.rJava.class.loader)) + try(a <- .jcall("java/lang/Class","Ljava/lang/Class;","forName",cl,TRUE,.jcast(.rJava.class.loader,"java.lang.ClassLoader"), check=FALSE)) + else + try(a <- .jcall("java/lang/Class","Ljava/lang/Class;","forName",cl,check=FALSE)) + # this is really .jcheck but we don't want it to appear on the call stack + .Call(RJavaCheckExceptions, silent) + if (!silent && is.jnull(a)) stop("class not found") + a +} + +# Java-side inheritance check; NULL inherits from any class, because +# it can be cast to any class type; cl can be a class name or a jobjRef to a class object +.jinherits <- function(o, cl) { + if (is.jnull(o)) return(TRUE) + if (!is(o, "jobjRef")) stop("invalid object") + if (is.character(cl)) cl <- .jfindClass(cl) else if (inherits(cl, "jclassName")) cl <- cl@jobj + if (!is(cl, "jobjRef")) stop("invalid class object") + ocl <- .jclassRef(o) + .Call(RisAssignableFrom, ocl@jobj, cl@jobj) +} + +# compares two things which may be Java objects. invokes Object.equals if applicable and thus even different pointers can be equal. if one parameter is not Java object, but scalar string/int/number/boolean then a corresponding Java object is created for comparison +# strict comparison returns FALSE if Java-reference is compared with non-reference. otherwise conversion into Java scalar object is attempted +.jequals <- function(a, b, strict=FALSE) { + if (is.null(a)) a <- new("jobjRef") + if (is.null(b)) b <- new("jobjRef") + if (is(a,"jobjRef")) o <- a else + if (is(b,"jobjRef")) { o <- b; b <- a } else + return(all.equal(a,b)) + if (!is(b,"jobjRef")) { + if (strict) return(FALSE) + if (length(b)!=1) { warning("comparison of non-scalar values is always FALSE"); return(FALSE) } + if (is.character(b)) b <- .jnew("java/lang/String",b) else + if (is.integer(b)) b <- .jnew("java/lang/Integer",b) else + if (is.numeric(b)) b <- .jnew("java/lang/Double",b) else + if (is.logical(b)) b <- .jnew("java/lang/Boolean", b) else + { warning("comparison of non-trivial values to Java objects is always FALSE"); return(FALSE) } + } + if (is.jnull(a)) + is.jnull(b) + else + .jcall(o, "Z", "equals", .jcast(b, "java/lang/Object")) +} + +.jfield <- function(o, sig=NULL, name, true.class=is.null(sig), convert=TRUE) { + if (length(sig)) { + if (sig=='S') sig<-"Ljava/lang/String;" + if (sig=='T') sig<-"S" + if (sig=='[S') sig<-"[Ljava/lang/String;" + if (sig=='[T') sig<-"[S" + } + r <- .Call(RgetField, o, sig, as.character(name), as.integer(true.class)) + if (inherits(r, "jobjRef")) { + if (isJavaArraySignature(r@jclass)) { + r <- if (convert) .jevalArray(r, rawJNIRefSignature=r@jclass, simplify=TRUE) else newArray(r, simplify=FALSE) + } + if (convert && inherits(r, "jobjRef")) { + if (r@jclass == "java/lang/String") + return(.External(RgetStringValue, r@jobj)) + if (.conv.in$.) return(.convert.in(r)) + } + } + r +} + +".jfield<-" <- function(o, name, value) + .Call(RsetField, o, name, value) + diff --git a/com.oracle.truffle.r.pkgs/rJava/R/comparison.R b/com.oracle.truffle.r.pkgs/rJava/R/comparison.R new file mode 100644 index 0000000000000000000000000000000000000000..70f0447c6e68c994039aa1218eea7e6eeb11e940 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/R/comparison.R @@ -0,0 +1,59 @@ +## + # This material is distributed under the GNU General Public License + # Version 2. You may review the terms of this license at + # http://www.gnu.org/licenses/gpl-2.0.html + # + # Copyright (c) 2006 Simon Urbanek <simon.urbanek@r-project.org> + # Copyright (c) 2018, Oracle and/or its affiliates + # + # All rights reserved. +## + +#' if a and b are compatable, +#' in the sense of the java.util.Comparable interface +#' then the result of the compareTo method is returned +#' otherwise an error message is generated +.jcompare <- function(a, b) { + if (is.null(a)) a <- new("jobjRef") + if (is.null(b)) b <- new("jobjRef") + + if( isJavaArray(a) || isJavaArray(b) ){ + stop( "comparison (<,>,<=,>=) is not implemented for java arrays yet" ) + } + + if( !is(a, "jobjRef" ) ) a <- ._java_valid_object( a ) + if( !is(b, "jobjRef" ) ) b <- ._java_valid_object( b ) + + .jcall( "RJavaComparator", "I", "compare", .jcast(a), .jcast(b) ) + +} +._lower <- function(e1, e2){ + .jcompare( e1, e2 ) <= 0L +} +._greater <- function(e1, e2 ){ + .jcompare( e1, e2 ) >= 0L +} +._strictly_lower <- function(e1, e2 ){ + .jcompare( e1, e2 ) < 0L +} +._strictly_greater <- function(e1, e2 ){ + .jcompare( e1, e2 ) > 0L +} + +setMethod("<" , c(e1="jobjRef",e2="jobjRef"), ._strictly_lower ) +setMethod("<" , c(e1="jobjRef") , ._strictly_lower ) +setMethod("<" , c(e2="jobjRef") , ._strictly_lower ) + +setMethod(">" , c(e1="jobjRef",e2="jobjRef"), ._strictly_greater ) +setMethod(">" , c(e1="jobjRef") , ._strictly_greater ) +setMethod(">" , c(e2="jobjRef") , ._strictly_greater ) + +setMethod("<=", c(e1="jobjRef",e2="jobjRef"), ._lower ) +setMethod("<=", c(e1="jobjRef") , ._lower ) +setMethod("<=", c(e2="jobjRef") , ._lower ) + +setMethod(">=", c(e1="jobjRef",e2="jobjRef"), ._greater ) +setMethod(">=", c(e1="jobjRef") , ._greater ) +setMethod(">=", c(e2="jobjRef") , ._greater ) + + diff --git a/com.oracle.truffle.r.pkgs/rJava/R/completion.R b/com.oracle.truffle.r.pkgs/rJava/R/completion.R new file mode 100644 index 0000000000000000000000000000000000000000..2e3dc1fbe65b1db20e3ff718930e687df8202f6f --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/R/completion.R @@ -0,0 +1,67 @@ +## + # This material is distributed under the GNU General Public License + # Version 2. You may review the terms of this license at + # http://www.gnu.org/licenses/gpl-2.0.html + # + # Copyright (c) 2006 Simon Urbanek <simon.urbanek@r-project.org> + # Copyright (c) 2018, Oracle and/or its affiliates + # + # All rights reserved. +## + +# :tabSize=4:indentSize=4:noTabs=false:folding=explicit:collapseFolds=1: + +# S4 dispatch does not work for .DollarNames, so we'll use S3 +# {{{ bring .DollarNames from the future if necessary +if( !exists( ".DollarNames", envir = asNamespace("utils") ) ){ + .DollarNames <- function(x, pattern) + UseMethod(".DollarNames") +} +# }}} + +# {{{ support function to retrieve completion names from RJavaTools +### get completion names from RJavaTools +classNamesMethod <- function (cl, static.only = TRUE ) { + # TODO: return both from java instead of two java calls + fieldnames <- .jcall( "RJavaTools", "[Ljava/lang/String;", + "getFieldNames", cl, static.only ) + methodnames <- .jcall( "RJavaTools", "[Ljava/lang/String;", + "getMethodNames", cl, static.only ) + c(fieldnames, methodnames) +} +# }}} + +# {{{ jclassName +._names_jclassName <- function(x){ + c( "class", classNamesMethod(x@jobj, static.only = TRUE ) ) +} +.DollarNames.jclassName <- function(x, pattern = "" ){ + grep( pattern, ._names_jclassName(x), value = TRUE ) +} + +setMethod("names", c(x="jclassName"), ._names_jclassName ) +# }}} + +# {{{ jobjRef +._names_jobjRef <- function(x){ + classNamesMethod(.jcall(x, "Ljava/lang/Class;", "getClass"), static.only = FALSE ) +} +.DollarNames.jobjRef <- function(x, pattern = "" ){ + grep( pattern, ._names_jobjRef(x), value = TRUE ) +} +setMethod("names", c(x="jobjRef"), ._names_jobjRef ) +# }}} + +# {{{ jarrayRef and jrectRef +._names_jarrayRef <- function(x ){ + c("length", classNamesMethod(.jcall(x, "Ljava/lang/Class;", "getClass"), static.only = FALSE ) ) +} +.DollarNames.jarrayRef <- .DollarNames.jrectRef <- function(x, pattern = ""){ + grep( pattern, ._names_jarrayRef(x), value = TRUE ) +} + +setMethod("names", c(x="jarrayRef"), ._names_jarrayRef ) +setMethod("names", c(x="jrectRef"), ._names_jarrayRef ) + + +# }}} diff --git a/com.oracle.truffle.r.pkgs/rJava/R/converter.R b/com.oracle.truffle.r.pkgs/rJava/R/converter.R new file mode 100644 index 0000000000000000000000000000000000000000..0401920e756c53fb800e99a7dd2ececd22a487a5 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/R/converter.R @@ -0,0 +1,54 @@ +## + # This material is distributed under the GNU General Public License + # Version 2. You may review the terms of this license at + # http://www.gnu.org/licenses/gpl-2.0.html + # + # Copyright (c) 2006 Simon Urbanek <simon.urbanek@r-project.org> + # Copyright (c) 2018, Oracle and/or its affiliates + # + # All rights reserved. +## + +# in: Java -> R +.conv.in <- new.env(parent=emptyenv()) +.conv.in$. <- FALSE +# out: R -> Java +.conv.out <- new.env(parent=emptyenv()) +.conv.out$. <- FALSE + +# --- internal fns +.convert.in <- function(jobj, verify.class=TRUE) { + jcl <- if (verify.class) .jclass(jobj) else gsub("/",".",jobj@jclass) + cv <- .conv.in[[jcl]] + if (!is.null(cv)) jobj else cv$fn(jobj) +} + +.convert.out <- function(robj) { + for (cl in class(robj)) { + cv <- .conv.out[[cl]] + if (!is.null(cv)) return(cv$fn(robj)) + } + robj +} + +# external fns +.jsetJConvertor <- function(java.class, fn) { + if (is.null(fn)) { + rm(list=java.class, envir=.conv.in) + if (!length(ls(.conv.in))) .conv.in$. <- FALSE + } else { + .conv.in$. <- TRUE + .conv.in[[java.class]] <- list(fn=fn) + } +} + +.jsetRConvertor <- function(r.class, fn) { + if (is.null(fn)) { + rm(list=r.class, envir=.conv.out) + if (!length(ls(.conv.out))) .conv.out$. <- FALSE + } else { + .conv.out$. <- TRUE + .conv.out[[r.class]] <- list(fn=fn) + } +} + diff --git a/com.oracle.truffle.r.pkgs/rJava/R/exceptions.R b/com.oracle.truffle.r.pkgs/rJava/R/exceptions.R new file mode 100644 index 0000000000000000000000000000000000000000..454dffa101a8a5855ed4b6105736356eec1c7c64 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/R/exceptions.R @@ -0,0 +1,59 @@ +## + # This material is distributed under the GNU General Public License + # Version 2. You may review the terms of this license at + # http://www.gnu.org/licenses/gpl-2.0.html + # + # Copyright (c) 2006 Simon Urbanek <simon.urbanek@r-project.org> + # Copyright (c) 2018, Oracle and/or its affiliates + # + # All rights reserved. +## + +## functions for some basic exception handling + +# FIXME: should all these actually be deprecated or defunct + +## poll for an exception +.jgetEx <- function(clear=FALSE) { + exo <- .Call(RpollException) + if (is.null(exo)) return(NULL) + x <- new("jobjRef", jobj=exo, jclass="java/lang/Throwable") + if (clear) .jclear() + x +} + +## explicitly clear any pending exceptions +.jclear <- function() { + .C(RclearException) + invisible(NULL) +} + +## throw an exception +.jthrow <- function(exception, message=NULL) { + if (is.character(exception)) + exception <- .jnew(exception, as.character(message)) + if (is(exception, "jobjRef")) + .Call(RthrowException, exception) + else + stop("Invalid exception.") +} + + +"$.Throwable" <- function( x, name ){ + if( name %in% names(c(x)) ){ + c(x)[[ name ]] + } else{ + ._jobjRef_dollar( x[["jobj"]], name ) + } +} + +"$<-.Throwable" <- function( x, name, value ){ + if( name %in% names(x) ){ + x[[ name ]] <- value + } else{ + ._jobjRef_dollargets( x[["jobj"]], name, value ) + } + x + +} + diff --git a/com.oracle.truffle.r.pkgs/rJava/R/import.R b/com.oracle.truffle.r.pkgs/rJava/R/import.R new file mode 100644 index 0000000000000000000000000000000000000000..9f13d38e8fa14912cde500f2277cdf0e172af7ce --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/R/import.R @@ -0,0 +1,156 @@ +## + # This material is distributed under the GNU General Public License + # Version 2. You may review the terms of this license at + # http://www.gnu.org/licenses/gpl-2.0.html + # + # Copyright (c) 2006 Simon Urbanek <simon.urbanek@r-project.org> + # Copyright (c) 2018, Oracle and/or its affiliates + # + # All rights reserved. +## + +IMPORTER <- ".__rjava__import" + +java_class_importers <- new.env() +assign( ".namespaces", NULL, envir = java_class_importers ) + +getImporterFromNamespace <- function( nm, create = TRUE ){ + .namespaces <- get(".namespaces", envir = java_class_importers ) + if( !is.null( .namespaces ) ){ + for( item in .namespaces ){ + if( identical( item$nm, nm ) ){ + return( item$importer ) + } + } + } + if( create ){ + addImporterNamespace(nm) + } + +} +addImporterNamespace <- function( nm ){ + importer <- .jnew( "RJavaImport", .jcast( .rJava.class.loader, "java/lang/ClassLoader" ) ) + assign( ".namespaces", + append( list( list( nm = nm, importer = importer ) ), get(".namespaces", envir = java_class_importers ) ), + envir = java_class_importers ) + importer +} + +getImporterFromEnvironment <- function(env, create = TRUE){ + if( isNamespace( env ) ){ + getImporterFromNamespace( env ) + } else if( exists(IMPORTER, envir = env ) ){ + get( IMPORTER, envir = env ) + } else if( create ){ + addImporterNamespace(env) + } +} + +getImporterFromGlobalEnv <- function( ){ + if( exists( "global", envir = java_class_importers ) ){ + get( "global", envir = java_class_importers ) + } else{ + initGlobalEnvImporter() + } +} +initGlobalEnvImporter <- function(){ + importer <- .jnew( "RJavaImport", .jcast( .rJava.class.loader, "java/lang/ClassLoader" ) ) + assign( "global", importer , envir = java_class_importers ) + importer +} + +import <- function( package = "java.util", env = sys.frame(sys.parent()) ){ + + if( missing(env) ){ + caller <- sys.function(-1) + env <- environment( caller ) + if( isNamespace( env ) ){ + importer <- getImporterFromNamespace( env ) + } + } else{ + force(env) + + if( !is.environment( env ) ){ + stop( "env is not an environment" ) + } + + if( ! exists( IMPORTER, env ) || is.jnull( get( IMPORTER, envir = env ) ) ){ + importer <- .jnew( "RJavaImport", .jcast( .rJava.class.loader, "java/lang/ClassLoader" ) ) + if( isNamespace(env) ){ + unlockBinding( IMPORTER, env = env ) + assignInNamespace( IMPORTER, importer, envir = env ) + } + assign( IMPORTER, importer, envir = env ) + } else{ + importer <- get( IMPORTER, envir = env ) + } + } + mustbe.importer( importer ) + .jcall( importer, "V", "importPackage", package ) + +} + +is.importer <- function(x){ + is( x, "jobjRef" ) && .jinherits( x, "RJavaImport" ) +} +mustbe.importer <- function(x){ + if( !is.importer(x) ){ + stop( "object not a suitable java package importer" ) + } +} + +#' collect importers +getAvailableImporters <- function( frames = TRUE, namespace = TRUE, + global = TRUE, caller = sys.function(-1L) ){ + + importers <- .jnew( "java/util/HashSet" ) + + addImporter <- function( importer ){ + if( is.importer( importer ) ){ + .jcall( importers, "Z", "add", .jcast(importer) ) + } + } + if( isTRUE( global ) ){ + addImporter( getImporterFromGlobalEnv() ) + } + + if( isTRUE( frames ) ){ + frames <- sys.frames() + if( length(frames) > 1L ){ + sapply( head( frames, -1L ), function(env) { + if( !identical( env, .GlobalEnv ) ){ + addImporter( getImporterFromEnvironment( env ) ) + } + } ) + } + } + + if( isTRUE( namespace ) ){ + force(caller) + env <- environment( caller ) + if( isNamespace( env ) ){ + addImporter( getImporterFromNamespace( env ) ) + } + } + + importers +} + +#' lookup for a class name in the available importers +lookup <- function( name = "Object", ..., caller = sys.function(-1L) ){ + force(caller) + importers <- getAvailableImporters(..., caller = caller) + .jcall( "RJavaImport", "Ljava/lang/Class;", "lookup", + name, .jcast( importers, "java/util/Set" ) ) +} + + +javaImport <- function( packages = "java.lang" ){ + # FASTR TODO + stop("javaImport not yet implemented") + # importer <- .jnew( "RJavaImport", .jcast( .rJava.class.loader, "java/lang/ClassLoader" ) ) + # .jcall( importer, "V", "importPackage", packages ) + # .Call( "newRJavaLookupTable" , importer, + # PACKAGE = "rJava" ) +} + diff --git a/com.oracle.truffle.r.pkgs/rJava/R/instanceof.R b/com.oracle.truffle.r.pkgs/rJava/R/instanceof.R new file mode 100644 index 0000000000000000000000000000000000000000..81b66760317a9152dd0d5ae8b2b6cc2e84536062 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/R/instanceof.R @@ -0,0 +1,36 @@ +## + # This material is distributed under the GNU General Public License + # Version 2. You may review the terms of this license at + # http://www.gnu.org/licenses/gpl-2.0.html + # + # Copyright (c) 2006 Simon Urbanek <simon.urbanek@r-project.org> + # Copyright (c) 2018, Oracle and/or its affiliates + # + # All rights reserved. +## + +`%instanceof%` <- .jinstanceof <- function( o, cl ){ + + if( !inherits( o, "jobjRef" ) ){ + stop( "o is not a java object" ) + } + + # first get the class object that represents cl + if( inherits( cl, "jobjRef" ) ){ + if( .jclass( cl ) == "java.lang.Class" ){ + clazz <- cl + } else { + clazz <- .jcall( cl, "Ljava/lang/Class;", "getClass" ) + } + } else if( inherits( cl, "jclassName" ) ) { + clazz <- cl@jobj + } else if( inherits( cl, "character" ) ){ + clazz <- .jfindClass(cl) + } else { + return(FALSE) + } + + # then find out if o is an instance of the class + .jcall( clazz , "Z", "isInstance", .jcast(o, "java/lang/Object" ) ) +} + diff --git a/com.oracle.truffle.r.pkgs/rJava/R/jfirst.R b/com.oracle.truffle.r.pkgs/rJava/R/jfirst.R new file mode 100644 index 0000000000000000000000000000000000000000..a43a273fb9394c62ec5597ae0b40ae508b910fbf --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/R/jfirst.R @@ -0,0 +1,749 @@ +## + # This material is distributed under the GNU General Public License + # Version 2. You may review the terms of this license at + # http://www.gnu.org/licenses/gpl-2.0.html + # + # Copyright (c) 2006 Simon Urbanek <simon.urbanek@r-project.org> + # Copyright (c) 2018, Oracle and/or its affiliates + # + # All rights reserved. +## + +# this part is common to all platforms and must be invoked +# from .First.lib after library.dynam + +# actual namespace environment of this package +.env <- environment() + +# variables in the rJava environment that will be initialized *after* the package is loaded +# they need to be pre-created at load time and populated later by .jinit +.delayed.export.variables <- c(".jniInitialized", ".jclassObject", ".jclassString", ".jclassClass", + ".jclass.int", ".jclass.double", ".jclass.float", ".jclass.boolean", + ".jclass.void", ".jinit.merge.error") +# variables that are delayed but not exported are added here +.delayed.variables <- c(.delayed.export.variables, ".rJava.class.loader") + +.jfirst <- function(libname, pkgname) { + # FASTR - no more C entry points + .registerFastrFunctions() + # register all C entry points + # addr <- getNativeSymbolInfo(.register.addr, pkgname) + # for (name in .register.addr) + # .env[[name]] <- addr[[name]]$address + + # previously set in C + assign(".rJava_initialized", FALSE, .env) + + assign(".rJava.base.path", paste(libname, pkgname, sep=.Platform$file.sep), .env) + assign(".jzeroRef", .createZeroRef(), .env) + + for (x in .delayed.variables) assign(x, NULL, .env) + assign(".jniInitialized", FALSE, .env) + + # FASTR - no JVM params + # # default JVM initialization parameters + # if (is.null(getOption("java.parameters"))) + # options("java.parameters"="-Xmx512m") + + # assign(".rJava.debug", T, .env) + + ## S4 classes update - all classes are created earlier in classes.R, but jobjRef's prototype is only valid after the dylib is loaded + setClass("jobjRef", representation(jobj="externalptr", jclass="character"), prototype=list(jobj=.jzeroRef, jclass="java/lang/Object"), where=.env, validity=.jobjRef.validity) +} + +# FASTR <<<<< + +.registerFastrFunctions <- function() { + # .C + .fastr.register.functions("rJava", .env, 0, + list(RclearException=.RclearException)) + # .External + .fastr.register.functions("rJava", .env, 3, + list(RcreateObject = .RcreateObject, + RgetStringValue = .RgetStringValue, + RinitJVM = .RinitJVM, + RtoString = .RtoString, + RcallMethod = .RcallMethod)) + # .Call + .fastr.register.functions("rJava", .env, 1, + list(RJavaCheckExceptions = .RJavaCheckExceptions, + RpollException = .RpollException, + RthrowException = .RthrowException, + RidenticalRef = .RidenticalRef, + RisAssignableFrom = .RisAssignableFrom, + RJava_checkJVM = .RJava_checkJVM, + RJava_needs_init = .RJava_needs_init, + initRJavaTools = .initRJavaTools, + RJava_set_memprof = .RJava_set_memprof, + RgetStringArrayCont = .RgetStringArrayCont, + RgetIntArrayCont = .RgetIntArrayCont, + RgetBoolArrayCont = .RgetBoolArrayCont, + RgetCharArrayCont = .RgetCharArrayCont, + RgetShortArrayCont = .RgetShortArrayCont, + RgetByteArrayCont = .RgetByteArrayCont, + RgetDoubleArrayCont = .RgetDoubleArrayCont, + RgetFloatArrayCont = .RgetFloatArrayCont, + RgetLongArrayCont = .RgetLongArrayCont, + RgetObjectArrayCont = .RgetObjectArrayCont, + RcreateArray = .RcreateArray, + RgetField = .RgetField, + RsetField = .RsetField)) +} + +.createZeroRef <- function() { + zr <- methods:::.newExternalptr() + attr(zr, ".rJava.zeroRef") <- T + zr +} + +.jobjRef.validity <- function(object) { + object # force args + + o <- attr(object@jobj, "external.object", exact=TRUE) + if(is.null(o)) { + zr <- attr(object@jobj, ".rJava.zeroRef", exact=TRUE) + if(is.null(zr)) { + return(FALSE) + } + } else if(!is.external(o)) { + # truffle unboxes first and fastr then converts primitive types into equivalent R values + # and the external.object attr in such a case isn't a truffle object + # in e.g. .jnew and .jcall rJava knows and stores the return value class name in the jclass slot, + # but it is only the externalptr, that is passed into functions like RcallMethod + # and we might need the class name for calls like getClass or isInstance + attr(object@jobj, "external.classname") <- object@jclass + } + return(TRUE) +} + +# TODO issues: +# - bytes converted to int +# - chars converted to int + +.dispatchOverrides <- function(orig, .NAME, ...) { + if(is.character(.NAME)) { + fun <- switch(.NAME, + "RJavaCheckExceptions" = .RJavaCheckExceptions, + "RpollException" = .RpollException, + "RthrowException" = .RthrowException, + "RclearException" = .RclearException, + "RcallMethod" = .RcallMethod, + "RidenticalRef" = .RidenticalRef, + "RisAssignableFrom" = .RisAssignableFrom, + "RinitJVM" = .RinitJVM, + "RJava_checkJVM" = .RJava_checkJVM, + "RJava_needs_init" = .RJava_needs_init, + "initRJavaTools" = .initRJavaTools, + "RJava_set_memprof" = .RJava_set_memprof, + "RgetStringValue" = .RgetStringValue, + "RcreateObject" = .RcreateObject, + "RgetStringArrayCont" = .RgetStringArrayCont, + "RgetIntArrayCont" = .RgetIntArrayCont, + "RgetBoolArrayCont" = .RgetBoolArrayCont, + "RgetCharArrayCont" = .RgetCharArrayCont, + "RgetShortArrayCont" = .RgetShortArrayCont, + "RgetByteArrayCont" = .RgetByteArrayCont, + "RgetDoubleArrayCont" = .RgetDoubleArrayCont, + "RgetFloatArrayCont" = .RgetFloatArrayCont, + "RgetLongArrayCont" = .RgetLongArrayCont, + "RgetObjectArrayCont" = .RgetObjectArrayCont, + "RcreateArray" = .RcreateArray, + "RgetField" = .RgetField, + "RsetField" = .RsetField, + "RtoString" = .RtoString, + function(...) orig(.NAME, ...) + ) + fun(...) + } else { + orig(.NAME, ...) + } +} + +.RJavaCheckExceptions <- function(silent) { + silent # force args + + .fastr.interop.checkException(silent, ".jcheck") +} + +.RpollException <- function() { + e <- .fastr.interop.getTryException(FALSE) + if(is.null(e)) { + return(NULL) + } + .fromJ(e) +} + +.RclearException <- function() { + .fastr.interop.clearTryException() +} + +.RthrowException <- function(silent) { + # TODO do we need this? not used directly from rJava code +} + +.RidenticalRef <- function(x1, x2) { + x1; x2 # force args + + if(!inherits(x1, "externalptr") || !inherits(x2, "externalptr")) { + return(NULL) + } + o1 <- attr(x1, "external.object", exact=TRUE) + o2 <- attr(x2, "external.object", exact=TRUE) + if(!(is.external(o1) && is.external(o2))) { + return(identical(o1, o2)) + } + if(!is.external(o1) || !is.external(o2)) { + return(NULL) + } + .fastr.interop.isIdentical(o1, o2) +} + +.RisAssignableFrom <- function(cl1, cl2) { + cl1; cl2 # force args + + if(!inherits(cl1, "externalptr") || !inherits(cl2, "externalptr")) { + stop("invalid type") + } + # should be already ensured that both args are class extpointer-s + .fastr.interop.isAssignableFrom(attr(cl1, "external.object", exact=TRUE), attr(cl2, "external.object", exact=TRUE)) +} + +.RcallMethod <- function(obj, returnSig, method, ...) { + obj; returnSig; method # force args + + if(is.null(obj)) { + stop("RcallMethod: call on a NULL object") + } + + o <- NULL + clnam <- NULL + if(inherits(obj, "externalptr")) { + o <- attr(obj, "external.object", exact=TRUE) + } else if(is.character(obj) && length(obj) == 1) { + clnam <- obj + } else { + stop("RcallMethod: invalid object parameter") + } + if(is.null(o) && is.null(clnam)) { + stop("RcallMethod: attempt to call a method of a NULL object.") + } + + # <<<<<< j.l.Class HACKs <<<<<< + # truffle provides no access to j.l.Class methods + if (method == "forName") { + if (!is.null(clnam) && clnam %in% c("java/lang/Class", "java.lang.Class")) { + res <- .fastr.interop.try(function() { new.java.class(list(...)[[1]]) }, FALSE) + return(.fromJ(res)) + } + } else if (method == "getName" && !is.null(o)) { + if (java.class(o) %in% c("java/lang/Class", "java.lang.Class")) { + res <- java.class(o, T) + return(.fromJ(res)) + } + } else if (method == "isInstance") { + if (!is.external(o)) { + o <- new.java.class(attr(obj, "external.classname", exact=TRUE)) + } + o2 <- .toJ(list(...)[[1]]) + res <- .fastr.interop.isInstance(o, o2) + return(.fromJ(res)) + } else if (method == "getClass") { + if(is.external(o)) { + res <- .fastr.interop.getJavaClass(o) + return(.fromJ(res)) + } else { + extClName <- attr(obj, "external.classname", exact=TRUE) + if(!is.null(extClName)) { + res <- new.java.class(extClName) + return(.fromJ(res)) + } + } + } + # >>>>>> j.l.Class HACKs >>>>>> + + if (!is.null(o) && !is.external(o)) { + o <- .asTruffleObject(o, attr(obj, "external.classname", exact=TRUE)) + } + + if(!is.character(returnSig) || length(returnSig) != 1) { + stop("RcallMethod: invalid return signature parameter") + } + + if(!is.character(method) || length(method) != 1) { + stop("RcallMethod: invalid method name") + } + + # if((!is.null(o) && !(method %in% names(o))) || + # (!is.null(cls) && !(method %in% names(cls))) ) { + # stop(paste0("method ", method, " with signature ", returnSig, " not found")) + # } + + if(is.null(o)) { + cls <- NULL + if (!is.null(clnam)) { + cls <- .fastr.interop.try(function() { new.java.class(clnam) }, FALSE) + } + if (is.null(cls)) { + stop("RcallMethod: cannot determine object class") + } + + extMethod <- function(...) cls[method](...) + } else { + extMethod <- function(...) o[method](...) + } + + args <- .ellipsisToJ(...) + res <- .fastr.interop.try(function() { do.call(extMethod, args) }, FALSE) + if(is.null(res)) { + return(NULL) + } + + if(substr(returnSig, 1, 1) %in% c("L", "[")) { + .fromJ(res, toExtPointer=TRUE) + } else { + .fromJ(res, toExtPointer=FALSE) + } +} + +.RgetField <- function(obj, sig, name, trueclass) { + obj; sig; name; trueclass # force args + + if(is.null(obj)) { + return(NULL) + } + + if (!is.character(name) || length(name) != 1) { + stop("invalid field name") + } + + if (!is.null(sig) && (!is.character(sig) || length(sig) != 1)) { + stop("invalid signature parameter") + } + + if(.IS_JOBJREF(obj)) { + obj <- obj@jobj + } + clnam <- NULL + externalClassName <- NULL + o <- NULL + if(inherits(obj, "externalptr")) { + o <- .toJ(obj) + externalClassName <- attr(obj, "external.classname", exact=TRUE) + } else if(is.character(obj) && length(obj) == 1) { + clnam <- obj + } else { + stop("invalid object parameter") + } + if (is.null(o) && is.null(clnam)) { + stop("cannot access a field of a NULL object") + } + + if(!is.null(o)) { + if(!is.external(o)) { + o <- .asTruffleObject(o, externalClassName) + } + res <- o[name] + } else { + cls <- new.java.class(clnam) + if (is.null(cls)) { + stop("cannot determine object class") + } + res <- cls[name] + } + if(is.null(res)) { + return(.jnull()) + } + if(!is.external(res)) { + # TODO there are cases when the passed signature is NULL - e.g. rJavaObject$someField + # with truffle we have no way to defferenciate if the unboxed return value relates to an Object or primitive field + # but the original field.c RgetField implementation checks the return type and + # if field not primitive an "jobjRef" S4 object is returned. + # rjava: + # > rJavaObject$fieldIntegerObject + # [1] "Java-Object{2147483647}" + # vs fastr: + # > rJavaObject$fieldIntegerObject + # [1] 2147483647 + # Note that his is not the case in rjava with method calls: + # > rJavaObject$methodIntegerObject() + # [1] 2147483647 + return(res) + } + + # as opposed to RcallMethod, we have to return a S4 objects at this place + if(trueclass) { + clsname <- java.class(res) + } else { + clsname <- .signatureToClassName(sig) + } + res <- .fromJ(res) + return(new("jobjRef", jobj=res, jclass=clsname)) +} + +.RsetField <- function(ref, name, value) { + ref; name; value # force args + + obj <- ref + if (is.null(obj)) { + stop("cannot set a field of a NULL object") + } + + if (!is.character(name) && length(name) != 1) { + stop("invalid field name") + } + + if(.IS_JOBJREF(obj)) { + obj <- obj@jobj + } + clnam <- NULL + o <- NULL + if(inherits(obj, "externalptr")) { + o <- .toJ(obj) + } else if(is.character(obj) && length(obj) == 1) { + clnam <- obj + } else { + stop("invalid object parameter") + } + if (is.null(o) && is.null(clnam)) { + stop("cannot set a field of a NULL object") + } + + value <- .toJ(value) + if(!is.null(o)) { + o[name] <- value + } else { + cls <- new.java.class(clnam) + cls[name] <- value + } + ref +} + +.RcreateObject <-function(class, ..., silent, class.loader) { + class; silent; class.loader # force args + + if(!is.character(class) || length(class) != 1) { + stop("RcreateObject: invalid class name") + } + + co <- .fastr.interop.try(function() { new.java.class(class, silent) }, FALSE) + if(is.null(co)) { + return(NULL) + } + args <- .ellipsisToJ(co, ...) + res <- .fastr.interop.try(function() { do.call(new.external, args) }, FALSE) + + # create an external pointer even for java.lang.String & co + .fromJ(res, toExtPointer=TRUE) +} + +.RgetStringArrayCont <- function(obj) { + obj # force args + + .getArrayCont(obj, as.character) +} + +.RgetIntArrayCont <- function(obj) { + obj # force args + + .getArrayCont(obj, as.integer) +} + +.RgetBoolArrayCont <- function(obj) { + obj # force args + + .getArrayCont(obj, as.logical) +} + +.RgetCharArrayCont <- function(obj) { + obj # force args + + # TODO as.integer might cause trouble? + .getArrayCont(obj, as.integer) +} + +.RgetShortArrayCont <- function(obj) { + obj # force args + + .getArrayCont(obj, as.integer) +} + +.RgetByteArrayCont <- function(obj) { + obj # force args + + .getArrayCont(obj, as.raw) +} + +.RgetDoubleArrayCont <- function(obj) { + obj # force args + + .getArrayCont(obj, as.double) +} + +.RgetFloatArrayCont <- function(obj) { + obj # force args + + .getArrayCont(obj, as.double) +} + +.RgetLongArrayCont <- function(obj) { + obj # force args + + .getArrayCont(obj, as.double) +} + +.RgetObjectArrayCont <- function(obj) { + obj # force args + + if (is.null(obj)) { + return(obj) + } + + if(!(inherits(obj, "externalptr"))) { + error("invalid object parameter") + } + obj <- attr(obj, "external.object", exact=TRUE) + # we rely on this being called only if obj is an external array + obj <- .fastr.interop.fromArray(obj) + lapply(obj, function(e) {.fromJ(e, toExtPointer=TRUE)}) +} + +.getArrayCont <- function(obj, toVectorFun) { + obj; toVectorFun # force args + + if(is.null(obj)) { + return(obj) + } else if(!(inherits(obj, "externalptr"))) { + stop("invalid object parameter") + } + obj <- attr(obj, "external.object", exact=TRUE) + if(is.null(obj)) { + stop("invalid object parameter") + } + + if(missing(toVectorFun)) { + res <- as.vector(obj) + } else { + res <- toVectorFun(obj) + } + res +} + +.RcreateArray <- function(ar, cl) { + ar; cl # force args + + if(is.null(ar)) { + return(NULL) + } + + type <- typeof(ar) + if(type %in% c("integer", "double", "character", "logical", "raw")) { + ar <- .vectorToJArray(ar) + sig <- java.class(ar) + return(new("jarrayRef", jobj=.fromJ(ar), jclass=sig, jsig=sig)) + } else if(is.list(ar)) { + + lapply(ar, function(e) { + if(!is.null(e) && !.IS_JOBJREF(e)) { + stop("Cannot create a Java array from a list that contains anything other than Java object references.") + } + }) + + if(length(cl) > 0) { + clsName <- cl[1] + } else { + clsName <- "java.lang.Object" + } + ar <- .listToJ(ar) + ar <- as.java.array(ar, clsName) + sig <- java.class(ar) + return(new("jarrayRef", jobj=.fromJ(ar), jclass=sig, jsig=sig)) + } + stop("Unsupported type to create Java array from.") +} + +.RtoString <- function(obj) { + obj # force args + + if(is.null(obj)) { + return(obj) + } + + if(!inherits(obj, "externalptr")) { + stop("RtoString: invalid object parameter") + } + + if(!is.null(attr(obj, "external.object", exact=TRUE))) { + obj <- .toJ(obj) + if(is.external(obj)) { + if(java.class(obj) == "java.lang.Class") { + res <- paste0("class ", java.class(obj, T)) + } else { + res <- obj["toString"]() + } + } else { + res <- obj + } + } else { + stop("RtoString: invalid object parameter") + } + res +} + + +.RgetStringValue <- function(obj) { + obj # force args + + if (inherits(obj, "externalptr")) { + attr(obj, "external.object", exact=TRUE) + } else { + obj + } +} + +.RinitJVM <- function(...) { + .rJava_initialized <- TRUE +} + +.RJava_needs_init <- function(...) { + !.rJava_initialized +} + +.RJava_set_memprof <- function(...) { + stop("memory profiling not enabled") +} + +.RJava_checkJVM <- function(...) { + # do nothing +} + +.initRJavaTools <- function(...) { + # do nothing +} + +.fromJ <- function(x, toExtPointer=FALSE) { + x; toExtPointer # force args + + if(is.null(x)) { + return(.jzeroRef) + } else { + if(toExtPointer || is.external(x)) { + ep <- methods:::.newExternalptr() + attr(ep, "external.object") <- x + ep + } else { + x + } + } +} + +.ellipsisToJ <- function(...) { + lapply(list(...), function(x) .toJ(x)) +} + +.listToJ <- function(l) { + l # force args + + lapply(l, function(x) .toJ(x)) +} + +.toJ <- function(x) { + x # force args + + if (is(x, "jobjRef")) { + x <- x@jobj + } else if (is(x, "jclassName")) { + x <- x@jobj@jobj + } + if(inherits(x, "externalptr")) { + if(.jidenticalRef(x, .jzeroRef)) { + x <- NULL + } else { + xo <- attr(x, "external.object", exact=TRUE) + if (is.null(xo)) { + stop(paste0("missing 'external' attribute on: ", x)) + } + if (is.external(xo)) { + return(xo) + } else { + return(.asTruffleObject(xo, attr(x, "external.classname", exact=TRUE))) + } + } + } + if(length(x) > 1) { + .vectorToJArray(x) + } else { + if (inherits(x, "jbyte")) { + x <- as.external.byte(x) + } else if (inherits(x, "jchar")) { + x <- as.external.char(x) + } else if (inherits(x, "jfloat")) { + x <- as.external.float(x) + } else if (inherits(x, "jlong")) { + x <- as.external.long(x) + } else if (inherits(x, "jshort")) { + x <- as.external.short(x) + } + x + } +} + +.vectorToJArray <- function(x) { + x # force args + + switch(class(x), + "jbyte" = as.java.array(x, "byte"), + "jchar" = as.java.array(x, "char"), + "jfloat" = as.java.array(x, "float"), + "jlong" = as.java.array(x, "long"), + "jshort" = as.java.array(x, "short"), + as.java.array(x) + ) +} + +.asTruffleObject <- function(x, className=NULL) { + x; className # force args + + if(!is.null(className)) { + x <- switch(gsub("/", ".", className), + "java.lang.Byte" = as.external.byte(x), + "java.lang.Character" = as.external.char(x), + "java.lang.Float" = as.external.float(x), + "java.lang.Long" = as.external.long(x), + "java.lang.Short" = as.external.short(x), + x + ) + } + .fastr.interop.asJavaTruffleObject(x) +} + +.signatureToClassName <- function(sig) { + sig # force args + + if(startsWith(sig, "L")) { + if(endsWith(sig, ";")) { + substr(sig, 2, nchar(sig) - 1) + } else { + substr(sig, 2, nchar(sig)) + } + } else { + sig + } +} + +.IS_JOBJREF <- function(obj) { + obj # force args + + inherits(obj, "jobjRef") || inherits(obj, "jarrayRef") || inherits(obj,"jrectRef") +} + +.IS_JARRAYREF <- function(obj) { + obj # force args + + inherits(obj, "jobjRef") || inherits(obj, "jarrayRef") || inherits(obj, "jrectRef") +} + +.IS_JRECTREF <- function(obj) { + obj # force args + + inherits(obj,"jrectRef") +} diff --git a/com.oracle.truffle.r.pkgs/rJava/R/jinit.R b/com.oracle.truffle.r.pkgs/rJava/R/jinit.R new file mode 100644 index 0000000000000000000000000000000000000000..aa63fb778581a79f8461b3298a459a475144fa2e --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/R/jinit.R @@ -0,0 +1,261 @@ +## + # This material is distributed under the GNU General Public License + # Version 2. You may review the terms of this license at + # http://www.gnu.org/licenses/gpl-2.0.html + # + # Copyright (c) 2006 Simon Urbanek <simon.urbanek@r-project.org> + # Copyright (c) 2018, Oracle and/or its affiliates + # + # All rights reserved. +## + +## This file is part of the rJava package - low-level R/Java interface +## (C)2006 Simon Urbanek <simon.urbanek@r-project.org> +## For license terms see DESCRIPTION and/or LICENSE +## +## $Id$ + +.check.JVM <- function() + .Call(RJava_checkJVM) +.need.init <- function() + .Call(RJava_needs_init) + +## initialization +.jinit <- function(classpath=NULL, parameters=getOption("java.parameters"), ..., silent=FALSE, force.init=FALSE) { + running.classpath <- character() + if (!.need.init()) { + running.classpath <- .jclassPath() + if (!force.init) { + if (length(classpath)) { + cpc <- unique(unlist(strsplit(classpath, .Platform$path.sep))) + if (length(cpc)) .jaddClassPath(cpc) + } + return(0) + } + } + + ## determine path separator + path.sep <- .Platform$path.sep + + if (!is.null(classpath)) { + classpath <- as.character(classpath) + if (length(classpath)) + classpath <- paste(classpath,collapse=path.sep) + } + + # merge CLASSPATH environment variable if present + cp<-Sys.getenv("CLASSPATH") + if (!is.null(cp)) { + if (is.null(classpath)) + classpath<-cp + else + classpath<-paste(classpath,cp,sep=path.sep) + } + + # set rJava/java/boot for boostrap (so we can get RJavaClassLoader) + boot.classpath <- file.path(.rJava.base.path,"java","boot") + + # if running in a sub-arch, append -Dr.arch in case someone gets the idea to start JRI + if (is.character(.Platform$r_arch) && nzchar(.Platform$r_arch) && length(grep("-Dr.arch", parameters, fixed=TRUE)) == 0L) + parameters <- c(paste("-Dr.arch=/", .Platform$r_arch, sep=''), as.character(parameters)) + + ## unfortunately Sys/setlocale()/Sys.getlocale() have incompatible interfaces so there + ## is no good way to get/set locales -- so we have to hack around it ... + locale.list <- c("LC_COLLATE", "LC_CTYPE", "LC_MONETARY", "LC_NUMERIC", "LC_TIME", "LC_MESSAGES", "LC_PAPER", "LC_MEASUREMENT") + locales <- sapply(locale.list, Sys.getlocale) + loc.sig <- Sys.getlocale() + + #cat(">> init CLASSPATH =",classpath,"\n") + #cat(">> boot class path: ", boot.classpath,"\n") + # call the corresponding C routine to initialize JVM + xr <- .External(RinitJVM, boot.classpath, parameters) + + ## we have to re-set the locales right away + suppressWarnings(try(if (!identical(Sys.getlocale(), loc.sig)) for (i in names(locales)) try(Sys.setlocale(i, locales[i]), silent=TRUE), + silent=TRUE)) + + if (xr==-1) stop("Unable to initialize JVM.") + if (xr==-2) stop("Another VM is already running and rJava was unable to attach to that VM.") + # we'll handle xr==1 later because we need fully initialized rJava for that + + # this should remove any lingering .jclass objects from the global env + # left there by previous versions of rJava + pj <- grep("^\\.jclass",ls(1,all.names=TRUE),value=TRUE) + if (length(pj)>0) { + rm(list=pj,pos=1) + if (exists(".jniInitialized",1)) rm(list=".jniInitialized",pos=1) + if (!silent) warning("rJava found hidden Java objects in your workspace. Internal objects from previous versions of rJava were deleted. Please note that Java objects cannot be saved in the workspace.") + } + + ##--- HACK-WARNING: we're operating directly on the namespace environment + ## this could be dangerous. + for (x in .delayed.variables) unlockBinding(x, .env) + assign(".jniInitialized", TRUE, .env) + # get cached class objects for reflection + assign(".jclassObject", .jcall("java/lang/Class","Ljava/lang/Class;","forName","java.lang.Object"), .env) + assign(".jclassClass", .jcall("java/lang/Class","Ljava/lang/Class;","forName","java.lang.Class"), .env) + assign(".jclassString", .jcall("java/lang/Class","Ljava/lang/Class;","forName","java.lang.String"), .env) + + # copied from bellow, need the RJavaTools to invoke $getField("TYPE") instead of not working .jcall + .Call( initRJavaTools) + .jaddClassPath(file.path(.rJava.base.path,"java")) + # FASTR <<<<< + # ic <- .jcall("java/lang/Class","Ljava/lang/Class;","forName","java.lang.Integer") + # # TODO .jcall does not work because in RcallMethod we do javaObejct[getField]("TYPE") and truffle does not allow it? + # # on the other hand ic$getField() is routed via the reflective RJavaTools.invokeMethod() + # #f<-.jcall(ic,"Ljava/lang/reflect/Field;","getField", "TYPE") + # f <- ic$getField("TYPE") + # assign(".jclass.int", .jcast(.jcall(f,"Ljava/lang/Object;","get",.jcast(ic,"java/lang/Object")),"java/lang/Class"), .env) + # ic <- .jcall("java/lang/Class","Ljava/lang/Class;","forName","java.lang.Double") + # # f<-.jcall(ic,"Ljava/lang/reflect/Field;","getField", "TYPE") + # f <- ic$getField("TYPE") + # assign(".jclass.double", .jcast(.jcall(f,"Ljava/lang/Object;","get",.jcast(ic,"java/lang/Object")),"java/lang/Class"), .env) + # ic <- .jcall("java/lang/Class","Ljava/lang/Class;","forName","java.lang.Float") + # # f<-.jcall(ic,"Ljava/lang/reflect/Field;","getField", "TYPE") + # f <- ic$getField("TYPE") + # assign(".jclass.float", .jcast(.jcall(f,"Ljava/lang/Object;","get",.jcast(ic,"java/lang/Object")),"java/lang/Class"), .env) + # ic <- .jcall("java/lang/Class","Ljava/lang/Class;","forName","java.lang.Boolean") + # # f<-.jcall(ic,"Ljava/lang/reflect/Field;","getField", "TYPE") + # f <- ic$getField("TYPE") + # assign(".jclass.boolean", .jcast(.jcall(f,"Ljava/lang/Object;","get",.jcast(ic,"java/lang/Object")),"java/lang/Class"), .env) + # ic <- .jcall("java/lang/Class","Ljava/lang/Class;","forName","java.lang.Void") + # # f<-.jcall(ic,"Ljava/lang/reflect/Field;","getField", "TYPE") + # f <- ic$getField("TYPE") + # assign(".jclass.void", .jcast(.jcall(f,"Ljava/lang/Object;","get",.jcast(ic,"java/lang/Object")),"java/lang/Class"), .env) + + ## if NOAWT is set, set AWT to headless + #if (nzchar(Sys.getenv("NOAWT"))) .jcall("java/lang/System","S","setProperty","java.awt.headless","true") + + #lib <- "libs" + #if (nchar(.Platform$r_arch)) lib <- file.path("libs", .Platform$r_arch) + + # TODO classloader stuff, ignore for now, maybe forever + # rjcl <- NULL + # if (xr==1) { # && nchar(classpath)>0) { + # # ok, so we're attached to some other JVM - now we need to make sure that + # # we can load our class loader. If we can't then we have to use our bad hack + # # to be able to squeeze our loader in + + # # first, see if this is actually JRIBootstrap so we have a loader already + # rjcl <- .Call(RJava_primary_class_loader) + # if (is.null(rjcl) || .jidenticalRef(rjcl,.jzeroRef)) rjcl <- NULL + # else rjcl <- new("jobjRef", jobj=rjcl, jclass="RJavaClassLoader") + # if (is.jnull(rjcl)) + # rjcl <- .jnew("RJavaClassLoader", .rJava.base.path, + # file.path(.rJava.base.path, lib), check=FALSE) + # .jcheck(silent=TRUE) + # if (is.jnull(rjcl)) { + # ## it's a hack, so we run it in try(..) in case BadThings(TM) happen ... + # cpr <- try(.jmergeClassPath(boot.classpath), silent=TRUE) + # if (inherits(cpr, "try-error")) { + # .jcheck(silent=TRUE) + # if (!silent) warning("Another VM is running already and the VM did not allow me to append paths to the class path.") + # assign(".jinit.merge.error", cpr, .env) + # } + # if (length(parameters)>0 && any(parameters!=getOption("java.parameters")) && !silent) + # warning("Cannot set VM parameters, because VM is running already.") + # } + # } + + # if (is.jnull(rjcl)) + # rjcl <- .jnew("RJavaClassLoader", .rJava.base.path, + # file.path(.rJava.base.path, lib), check=FALSE ) + + # if (!is.jnull(rjcl)) { + # ## init class loader + # assign(".rJava.class.loader", rjcl, .env) + + # ##-- set the class for native code + # .Call(RJava_set_class_loader, .env$.rJava.class.loader@jobj) + + # ## now it's time to add any additional class paths + cpc <- unique(strsplit(classpath, .Platform$path.sep)[[1]]) + if (length(cpc)) .jaddClassPath(cpc) + # } else stop("Unable to create a Java class loader.") + # FASTR >>>>> + ##.Call(RJava_new_class_loader, .rJava.base.path, file.path(.rJava.base.path, lib)) + + ## lock namespace bindings + for (x in .delayed.variables) lockBinding(x, .env) + + ## now we need to update the attached namespace (package env) as well + m <- match(paste("package", getNamespaceName(.env), sep = ":"), search())[1] + if (!is.na(m)) { ## only is it is attached + pe <- as.environment(m) + for (x in .delayed.export.variables) { + unlockBinding(x, pe) + pe[[x]] <- .env[[x]] + lockBinding(x, pe) + } + } + + # FIXME: is this the best place or should this be done + # internally right after the RJavaClassLoader is instanciated + # init the cached RJavaTools class in the jni side + .Call( "initRJavaTools", PACKAGE = "rJava" ) + + # not yet + # import( c( "java.lang", "java.util") ) + + invisible(xr) +} + +# FIXME: this is not always true: osgi, eclipse etc use a different +# class loader strategy, we should add some sort of hook to let people +# define how they want this to be done +.jmergeClassPath <- function(cp) { + ccp <- .jcall("java/lang/System","S","getProperty","java.class.path") + ccpc <- strsplit(ccp, .Platform$path.sep)[[1]] + cpc <- strsplit(cp, .Platform$path.sep)[[1]] + rcp <- unique(cpc[!(cpc %in% ccpc)]) + if (length(rcp) > 0) { + # the loader requires directories to include trailing slash + # Windows: need / or \ ? (untested) + dirs <- which(file.info(rcp)$isdir) + for (i in dirs) + if (substr(rcp[i],nchar(rcp[i]),nchar(rcp[i]))!=.Platform$file.sep) + rcp[i]<-paste(rcp[i], .Platform$file.sep, sep='') + + ## this is a hack, really, that exploits the fact that the system class loader + ## is in fact a subclass of URLClassLoader and it also subverts protection + ## of the addURL class using reflection - yes, bad hack, but we use it + ## only if the boot class path doesn't contain our own class loader so + ## we cannot replace the system loader with our own (this will happen when we + ## need to attach to an existing VM) + ## The original discussion and code for this hack was at: + ## http://forum.java.sun.com/thread.jspa?threadID=300557&start=15&tstart=0 + + ## it should probably be run in try(..) because chances are that it will + ## break if Sun changes something... + cl <- .jcall("java/lang/ClassLoader", "Ljava/lang/ClassLoader;", "getSystemClassLoader") + urlc <- .jcall("java/lang/Class", "Ljava/lang/Class;", "forName", "java.net.URL") + clc <- .jcall("java/lang/Class", "Ljava/lang/Class;", "forName", "java.net.URLClassLoader") + ar <- .jcall("java/lang/reflect/Array", "Ljava/lang/Object;", + "newInstance", .jclassClass, 1:1) + .jcall("java/lang/reflect/Array", "V", "set", + .jcast(ar, "java/lang/Object"), 0:0, + .jcast(urlc, "java/lang/Object")) + m<-.jcall(clc, "Ljava/lang/reflect/Method;", "getDeclaredMethod", "addURL", .jcast(ar,"[Ljava/lang/Class;")) + .jcall(m, "V", "setAccessible", TRUE) + + ar <- .jcall("java/lang/reflect/Array", "Ljava/lang/Object;", + "newInstance", .jclassObject, 1:1) + + for (fn in rcp) { + f <- .jnew("java/io/File", fn) + url <- .jcall(f, "Ljava/net/URL;", "toURL") + .jcall("java/lang/reflect/Array", "V", "set", + .jcast(ar, "java/lang/Object"), 0:0, + .jcast(url, "java/lang/Object")) + .jcall(m, "Ljava/lang/Object;", "invoke", + .jcast(cl, "java/lang/Object"), .jcast(ar, "[Ljava/lang/Object;")) + } + + # also adjust the java.class.path property to not confuse others + if (length(ccp)>1 || (length(ccp)==1 && nchar(ccp[1])>0)) + rcp <- c(ccp, rcp) + acp <- paste(rcp, collapse=.Platform$path.sep) + .jcall("java/lang/System","S","setProperty","java.class.path",as.character(acp)) + } # if #rcp>0 + invisible(.jcall("java/lang/System","S","getProperty","java.class.path")) +} diff --git a/com.oracle.truffle.r.pkgs/rJava/R/loader.R b/com.oracle.truffle.r.pkgs/rJava/R/loader.R new file mode 100644 index 0000000000000000000000000000000000000000..07603a50130abd7acaf9e5d90d3e84cfe0a91d10 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/R/loader.R @@ -0,0 +1,85 @@ +## + # This material is distributed under the GNU General Public License + # Version 2. You may review the terms of this license at + # http://www.gnu.org/licenses/gpl-2.0.html + # + # Copyright (c) 2006 Simon Urbanek <simon.urbanek@r-project.org> + # Copyright (c) 2018, Oracle and/or its affiliates + # + # All rights reserved. +## + +.jaddClassPath <- function(path) { + if (!length(path)) return(invisible(NULL)) + # FASTR <<<<< + # if (!is.jnull(.rJava.class.loader)) + # invisible(.jcall(.rJava.class.loader,"V","addClassPath",as.character(path))) + # else { + # cpr <- try(.jmergeClassPath(paste(path,collapse=.Platform$path.sep)), silent=TRUE) + # invisible(!inherits(cpr, "try-error")) + #} + invisible(java.addToClasspath(path)) + # FASTR >>>>> +} + +.jclassPath <- function() { + # FASTR <<<<< + # if (is.jnull(.rJava.class.loader)) { + # cp <- .jcall("java/lang/System", "S", "getProperty", "java.class.path") + # unlist(strsplit(cp, .Platform$path.sep)) + # } else { + # .jcall(.rJava.class.loader,"[Ljava/lang/String;","getClassPath") + # } + java.classpath() + # FASTR >>>>> +} + +.jaddLibrary <- function(name, path) { + # FASTR TODO + stop(".jaddLibrary overriden but not yet implemented") + # if (!is.jnull(.rJava.class.loader)) + # invisible(.jcall(.rJava.class.loader, "V", "addRLibrary", as.character(name)[1], as.character(path)[1])) +} + +.jrmLibrary <- function(name) { + ## FIXME: unimplemented +} + +.jclassLoader <- function() { + # FASTR TODO + stop(".jclassLoader overriden but not yet implemented") + #.rJava.class.loader +} + +.jpackage <- function(name, jars='*', morePaths='', nativeLibrary=FALSE, lib.loc=NULL) { + if (!.jniInitialized) .jinit() + classes <- system.file("java", package=name, lib.loc=lib.loc) + if (nchar(classes)) { + .jaddClassPath(classes) + if (length(jars)) { + if (length(jars)==1 && jars=='*') { + jars <- grep(".*\\.jar",list.files(classes,full.names=TRUE),TRUE,value=TRUE) + if (length(jars)) .jaddClassPath(jars) + } else .jaddClassPath(paste(classes,jars,sep=.Platform$file.sep)) + } + } + if (any(nchar(morePaths))) { + cl <- as.character(morePaths) + cl <- cl[nchar(cl)>0] + .jaddClassPath(cl) + } + if (is.logical(nativeLibrary)) { + if (nativeLibrary) { + libs <- "libs" + if (nchar(.Platform$r_arch)) lib <- file.path("libs", .Platform$r_arch) + lib <- system.file(libs, paste(name, .Platform$dynlib.ext, sep=''), package=name, lib.loc=lib.loc) + if (nchar(lib)) + .jaddLibrary(name, lib) + else + warning("Native library for `",name,"' could not be found.") + } + } else { + .jaddLibrary(name, nativeLibrary) + } + invisible(TRUE) +} diff --git a/com.oracle.truffle.r.pkgs/rJava/R/memprof.R b/com.oracle.truffle.r.pkgs/rJava/R/memprof.R new file mode 100644 index 0000000000000000000000000000000000000000..f90745301bf078c9a542a7c4f846e398436a92ed --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/R/memprof.R @@ -0,0 +1,15 @@ +## + # This material is distributed under the GNU General Public License + # Version 2. You may review the terms of this license at + # http://www.gnu.org/licenses/gpl-2.0.html + # + # Copyright (c) 2006 Simon Urbanek <simon.urbanek@r-project.org> + # Copyright (c) 2018, Oracle and/or its affiliates + # + # All rights reserved. +## + +.jmemprof <- function(file = "-") { + if (is.null(file)) file <- "" + invisible(.Call(RJava_set_memprof, as.character(file))) +} diff --git a/com.oracle.truffle.r.pkgs/rJava/R/methods.R b/com.oracle.truffle.r.pkgs/rJava/R/methods.R new file mode 100644 index 0000000000000000000000000000000000000000..75435d009d420cf8911c47472e24b4b3e9a1b7e6 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/R/methods.R @@ -0,0 +1,38 @@ +## + # This material is distributed under the GNU General Public License + # Version 2. You may review the terms of this license at + # http://www.gnu.org/licenses/gpl-2.0.html + # + # Copyright (c) 2006 Simon Urbanek <simon.urbanek@r-project.org> + # Copyright (c) 2018, Oracle and/or its affiliates + # + # All rights reserved. +## + +## methods for jobjRef class +## +## additional methods ($ and $<-) are defined in reflection.R + +# show method +# FIXME: this should show the class of the object instead of Java-Object +setMethod("show", c(object="jobjRef"), function(object) { + if (is.jnull(object)) show("Java-Object<null>") else show(paste("Java-Object{", .jstrVal(object), "}", sep='')) + invisible(NULL) +}) + +setMethod("show", c(object="jarrayRef"), function(object) { + show(paste("Java-Array-Object",object@jsig,":", .jstrVal(object), sep='')) + invisible(NULL) +}) + +# map R comparison operators to .jequals +setMethod("==", c(e1="jobjRef",e2="jobjRef"), function(e1,e2) .jequals(e1,e2)) +setMethod("==", c(e1="jobjRef"), function(e1,e2) .jequals(e1,e2)) +setMethod("==", c(e2="jobjRef"), function(e1,e2) .jequals(e1,e2)) + +setMethod("!=", c(e1="jobjRef",e2="jobjRef"), function(e1,e2) !.jequals(e1,e2)) +setMethod("!=", c(e1="jobjRef"), function(e1,e2) !.jequals(e1,e2)) +setMethod("!=", c(e2="jobjRef"), function(e1,e2) !.jequals(e1,e2)) + +# other operators such as <,> are defined in comparison.R + diff --git a/com.oracle.truffle.r.pkgs/rJava/R/options.R b/com.oracle.truffle.r.pkgs/rJava/R/options.R new file mode 100644 index 0000000000000000000000000000000000000000..e28823c0c975d6365f52b6d2072427343e9a5020 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/R/options.R @@ -0,0 +1,22 @@ +## + # This material is distributed under the GNU General Public License + # Version 2. You may review the terms of this license at + # http://www.gnu.org/licenses/gpl-2.0.html + # + # Copyright (c) 2006 Simon Urbanek <simon.urbanek@r-project.org> + # Copyright (c) 2018, Oracle and/or its affiliates + # + # All rights reserved. +## + +.joptions <- function(...) { + l <- list(...) + if (length(l)==0) return(list()) + if ("jni.cache" %in% names(l)) { + v <- l[["jni.cache"]] + if (!is.logical(v) || length(v)!=1) + stop("jni.cache must be a logical vector of length 1") + .C(RuseJNICache,v) + invisible(NULL) + } +} diff --git a/com.oracle.truffle.r.pkgs/rJava/R/reflection.R b/com.oracle.truffle.r.pkgs/rJava/R/reflection.R new file mode 100644 index 0000000000000000000000000000000000000000..bee29283b150d874576e8e20f0ac7e77c2e177f4 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/R/reflection.R @@ -0,0 +1,290 @@ +## + # This material is distributed under the GNU General Public License + # Version 2. You may review the terms of this license at + # http://www.gnu.org/licenses/gpl-2.0.html + # + # Copyright (c) 2006 Simon Urbanek <simon.urbanek@r-project.org> + # Copyright (c) 2018, Oracle and/or its affiliates + # + # All rights reserved. +## + +### reflection functions - convenience function relying on the low-level +### functions .jcall/.jnew and friends + +### reflection tools (inofficial so far, because it returns strings +### instead of the reflection objects - it's useful for quick checks, +### though) +.jmethods <- function(o, name=NULL, as.obj=FALSE) { + cl <- if (is(o, "jobjRef")) .jcall(o, "Ljava/lang/Class;", "getClass") else if (is(o, "jclassName")) o@jobj else .jfindClass(as.character(o)) + ms<-.jcall(cl,"[Ljava/lang/reflect/Method;","getMethods") + if (isTRUE(as.obj)) return(ms) + ss<-unlist(lapply(ms,function(x) .jcall(x,"S","toString"))) + if (!is.null(name)) + grep(paste("\\.",name,"\\(",sep=''),ss,value=TRUE) + else + ss +} + +.jconstructors <- function(o, as.obj=FALSE) { + cl <- if (is(o, "jobjRef")) .jcall(o, "Ljava/lang/Class;", "getClass") else if (is(o, "jclassName")) o@jobj else .jfindClass(as.character(o)) + cs<-.jcall(cl,"[Ljava/lang/reflect/Constructor;","getConstructors") + if (isTRUE(as.obj)) return(cs) + unlist(lapply(cs,function(x) .jcall(x,"S","toString"))) +} + +### this list maps R class names to Java class names for which the constructor does the necessary conversion (for use in .jrcall) +.class.to.jclass <- c(character= "java/lang/String", + jbyte = "java/lang/Byte", + integer = "java/lang/Integer", + numeric = "java/lang/Double", + logical = "java/lang/Boolean", + jlong = "java/lang/Long", + jchar = "java/lang/Character", + jshort = "java/lang/Short", + jfloat = "java/lang/Float") + +### Java classes that have a corresponding primitive type and thus a corresponding TYPE field to use with scalars +.primitive.classes = c("java/lang/Byte", "java/lang/Integer", "java/lang/Double", "java/lang/Boolean", + "java/lang/Long", "java/lang/Character", "java/lang/Short", "java/lang/Float") + +### creates a valid java object +### if a is already a java object reference, all is good +### otherwise some primitive conversion occurs +# this is used for internal purposes only, in particular +# it does not dispatch arrays to jrectRef +._java_valid_object <- function(a) { + if (is(a, "jobjRef")) a + else if (is.null(a)) .jnull() else { + cm <- match(class(a)[1], names(.class.to.jclass)) + if (!any(is.na(cm))) { + if (length(a) == 1) { + y <- .jnew(.class.to.jclass[cm], a) + if (.class.to.jclass[cm] %in% .primitive.classes) attr(y, "primitive") <- TRUE + y + } else .jarray(a, dispatch = FALSE) + } else { + stop("Sorry, parameter type `", cm ,"' is ambiguous or not supported.") + } + } +} + +### creates a list of valid java parameters, used in both .J and .jrcall +._java_valid_objects_list <- function( ... ) + lapply(list(...), ._java_valid_object ) + + +### returns a list of Class objects +### this is used in both .J and .jrcall +._isPrimitiveReference <- function(x) + isTRUE(attr(x, "primitive")) + +._java_class <- function( x ){ + if (is.jnull(x)) { if (is(x,"jobjRef")) .jfindClass(x@jclass) else .jclassObject } else { + if (._isPrimitiveReference(x)) .jfield(x, "Ljava/lang/Class;", "TYPE") else .jcall(x, "Ljava/lang/Class;", "getClass") + } +} +._java_class_list <- function( objects_list ) + lapply(objects_list, ._java_class ) + +### reflected call - this high-level call uses reflection to call a method +### it is much less efficient than .jcall but doesn't require return type +### specification or exact matching of parameter types +.jrcall <- function(o, method, ..., simplify=TRUE) { + if (!is.character(method) | length(method) != 1) + stop("Invalid method name - must be exactly one character string.") + # FASTR <<<<< + # bypassing invokeMethod reflection call + + # if (inherits(o, "jobjRef") || inherits(o, "jarrayRef")) + # cl <- .jcall(o, "Ljava/lang/Class;", "getClass") + # else + # cl <- .jfindClass(o) + # if (is.null(cl)) + # stop("Cannot find class of the object.") + + # # p is a list of parameters that are formed solely by valid Java objects + # p <- ._java_valid_objects_list(...) + + # # list of classes + # pc <- ._java_class_list( p ) + + # # invoke the method directly from the RJavaTools class + # # ( this throws the actual exception instead of an InvocationTargetException ) + # j_p <- .jarray(p, "java/lang/Object" , dispatch = FALSE ) + # j_pc <- .jarray(pc, "java/lang/Class" , dispatch = FALSE ) + + # r <- .jcall( "RJavaTools", "Ljava/lang/Object;", "invokeMethod", + # cl, .jcast(if(inherits(o,"jobjRef") || inherits(o, "jarrayRef")) o else cl, "java/lang/Object"), + # .jnew( "java/lang/String", method), + # j_p, j_pc, use.true.class = TRUE, evalString = simplify, evalArray = FALSE ) + + r <- .jcall( o, "Ljava/lang/Object;", method, ..., use.true.class = TRUE, evalString = simplify, evalArray = FALSE ) + # FASTR >>>>> + + # null is returned when the return type of the method is void + # TODO[romain]: not sure how to distinguish when the result is null but the + # return type is not null + if( is.jnull( r ) || is.null(r) ){ + return( invisible( NULL ) ) + } + + # simplify if needed and return the object + if( is(r, "jarrayRef" ) && simplify ){ + ._jarray_simplify( r ) + } else if (simplify){ + .jsimplify(r) + } else { + r + } +} + +### reflected construction of java objects +### This uses reflection to call a suitable constructor based +### on the classes of the ... it does not require exact match between +### the objects and the constructor parameters +### This is to .jnew what .jrcall is to .jcall +.J <- function(class, ...) { + # allow non-JNI specifiation + class <- gsub("\\.","/",class) + + # p is a list of parameters that are formed solely by valid Java objects + p <- ._java_valid_objects_list(...) + + # list of classes + pc <- ._java_class_list( p ) + + # use RJavaTools to find create the object + o <- .jcall("RJavaTools", "Ljava/lang/Object;", + "newInstance", .jfindClass(class), + .jarray(p,"java/lang/Object", dispatch = FALSE ), + .jarray(pc,"java/lang/Class", dispatch = FALSE ), + evalString = FALSE, evalArray = FALSE, use.true.class = TRUE ) + + o +} + +## make sure Java's -2147483648 +.iNA <- function(o, convert=TRUE) if(convert && is.na(o)) -2147483648.0 else o + +### simplify non-scalar reference to a scalar object if possible +.jsimplify <- function(o, promote=FALSE) { + if (!inherits(o, "jobjRef") && !inherits(o, "jarrayRef")) + return(o) + cn <- .jclass(o, true=TRUE) + if (cn == "java.lang.Boolean") .jcall(o, "Z", "booleanValue") else + if (cn == "java.lang.Integer" || cn == "java.lang.Short" || cn == "java.lang.Character" || cn == "java.lang.Byte") .iNA(.jcall(o, "I", "intValue"), promote) else + if (cn == "java.lang.Number" || cn == "java.lang.Double" || cn == "java.lang.Long" || cn == "java.lang.Float") .jcall(o, "D", "doubleValue") else + if (cn == "java.lang.String") .jstrVal(.jcast(o, "java/lang/String")) else + o +} + +#! ### get the value of a field (static class fields are not supported yet) +#! .jrfield <- function(o, name, simplify=TRUE, true.class=TRUE) { +#! if (!inherits(o, "jobjRef") && !inherits(o, "jarrayRef") && !is.character(o)) +#! stop("Object must be a Java reference or class name.") +#! if (is.character(o)) { +#! cl <- .jfindClass(o) +#! .jcheck(silent=TRUE) +#! if (is.null(cl)) +#! stop("class not found") +#! o <- .jnull() +#! } else { +#! cl <- .jcall(o, "Ljava/lang/Class;", "getClass") +#! o <- .jcast(o, "java/lang/Object") +#! } +#! f <- .jcall(cl, "Ljava/lang/reflect/Field;", "getField", name) +#! r <- .jcall(f,"Ljava/lang/Object;","get",o) +#! if (simplify) r <- .jsimplify(r) +#! if (true.class && (inherits(r, "jobjRef") || inherits(r, "jarrayRef"))) { +#! cl <- .jcall(r, "Ljava/lang/Class;", "getClass") +#! cn <- .jcall(cl, "Ljava/lang/String;", "getName") +#! if (substr(cn,1,1) != '[') +#! r@jclass <- gsub("\\.","/",cn) +#! } +#! r +#! } + +### list the fields of a class or object +.jfields <- function(o, name=NULL, as.obj=FALSE) { + cl <- if (is(o, "jobjRef")) .jcall(o, "Ljava/lang/Class;", "getClass") else if (is(o, "jclassName")) o@jobj else .jfindClass(as.character(o)) + f <- .jcall(cl, "[Ljava/lang/reflect/Field;", "getFields") + if (isTRUE(as.obj)) return(f) + fl <- unlist(lapply(f, function(x) .jcall(x, "S", "toString"))) + if (!is.null(name)) grep(paste("\\.",name,"$",sep=''), fl) else fl +} + +._must_be_character_of_length_one <- function(name){ + if( !is.character(name) || length(name) != 1L ){ + stop( "'name' must be a character vector of length one" ) + } +} +### checks if the java object x has a field called name +hasField <- function( x, name ){ + ._must_be_character_of_length_one(name) + .jcall("RJavaTools", "Z", "hasField", .jcast( x, "java/lang/Object" ), name) +} + +hasJavaMethod <- function( x, name ){ + ._must_be_character_of_length_one(name) + .jcall("RJavaTools", "Z", "hasMethod", .jcast( x, "java/lang/Object" ), name) +} + +hasClass <- function( x, name){ + ._must_be_character_of_length_one(name) + .jcall("RJavaTools", "Z", "hasClass", .jcast( x, "java/lang/Object" ), name) +} + +### the following ones are needed for the static version of $ +classHasField <- function(x, name, static=FALSE) { + if (is(x, "jclassName")) x <- x@jobj else if (!is(x, "jobjRef")) x <- .jfindClass(as.character(x)) + ._must_be_character_of_length_one(name) + .jcall("RJavaTools", "Z", "classHasField", x, name, static) +} + +classHasMethod <- function(x, name, static=FALSE) { + if (is(x, "jclassName")) x <- x@jobj else if (!is(x, "jobjRef")) x <- .jfindClass(as.character(x)) + ._must_be_character_of_length_one(name) + .jcall("RJavaTools", "Z", "classHasMethod", x, name, static) +} + +classHasClass <- function(x, name, static=FALSE) { + if (is(x, "jclassName")) x <- x@jobj else if (!is(x, "jobjRef")) x <- .jfindClass(as.character(x)) + ._must_be_character_of_length_one(name) + .jcall("RJavaTools", "Z", "classHasClass", x, name, static) +} + +### syntactic sugar to allow object$field and object$methods(...) +### first attempts to find a field of that name and then a method +._jobjRef_dollar <- function(x, name) { + if (hasField(x, name) ){ + .jfield(x, , name) + } else if( hasJavaMethod( x, name ) ) { + function(...) .jrcall(x, name, ...) + } else if( hasClass(x, name) ) { + cl <- .jcall( x, "Ljava/lang/Class;", "getClass" ) + inner.cl <- .jcall( "RJavaTools", "Ljava/lang/Class;", "getClass", cl, name, FALSE ) + new("jclassName", name=.jcall(inner.cl, "S", "getName"), jobj=inner.cl) + } else if( is.character(name) && length(name) == 1L && name == "length" && isJavaArray(x) ){ + length( x ) + } else { + stop( sprintf( "no field, method or inner class called '%s' ", name ) ) + } +} +setMethod("$", c(x="jobjRef"), ._jobjRef_dollar ) + +### support for object$field<-... +._jobjRef_dollargets <- function(x, name, value) { + if( hasField( x, name ) ){ + .jfield(x, name) <- value + } + x +} +setMethod("$<-", c(x="jobjRef"), ._jobjRef_dollargets ) + +# get a class name for an object +.jclass <- function(o, true=TRUE) { + if (true) .jcall(.jcall(o, "Ljava/lang/Class;", "getClass"), "S", "getName") + else if( inherits( o, "jarrayRef" ) ) o@jsig else o@jclass +} + diff --git a/com.oracle.truffle.r.pkgs/rJava/R/rep.R b/com.oracle.truffle.r.pkgs/rJava/R/rep.R new file mode 100644 index 0000000000000000000000000000000000000000..11491ea3ca89e3f3a741e5097467033100e5c3f7 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/R/rep.R @@ -0,0 +1,41 @@ +## + # This material is distributed under the GNU General Public License + # Version 2. You may review the terms of this license at + # http://www.gnu.org/licenses/gpl-2.0.html + # + # Copyright (c) 2006 Simon Urbanek <simon.urbanek@r-project.org> + # Copyright (c) 2018, Oracle and/or its affiliates + # + # All rights reserved. +## + +# :tabSize=4:indentSize=4:noTabs=false:folding=explicit:collapseFolds=1: + +# {{{ rep +setGeneric("rep") +setMethod( "rep", "jobjRef", function( x, times = 1L, ... ){ + .jcall( "RJavaArrayTools", "[Ljava/lang/Object;", "rep", + .jcast(x), as.integer(times), evalArray = FALSE ) +} ) +setMethod( "rep", "jarrayRef", function(x, times = 1L, ...){ + .NotYetImplemented() +} ) +setMethod( "rep", "jrectRef", function(x, times = 1L, ...){ + .NotYetImplemented() +} ) +# }}} + +# {{{ clone +clone <- function( x, ... ){ + UseMethod( "clone" ) +} +clone.default <- function( x, ... ){ + .NotYetImplemented() +} +setGeneric( "clone" ) +setMethod( "clone", "jobjRef", function(x, ...){ + .jcall( "RJavaArrayTools", "Ljava/lang/Object;", "cloneObject", .jcast( x ) ) +} ) +setMethod( "clone", "jarrayRef", function(x, ...){ .NotYetImplemented( ) } ) +setMethod( "clone", "jrectRef", function(x, ...){ .NotYetImplemented( ) } ) +# }}} diff --git a/com.oracle.truffle.r.pkgs/rJava/R/rj.R b/com.oracle.truffle.r.pkgs/rJava/R/rj.R deleted file mode 100644 index 238d2d9a91884db9100cbf45a2f956a9cac61a68..0000000000000000000000000000000000000000 --- a/com.oracle.truffle.r.pkgs/rJava/R/rj.R +++ /dev/null @@ -1,392 +0,0 @@ -## - # This material is distributed under the GNU General Public License - # Version 2. You may review the terms of this license at - # http://www.gnu.org/licenses/gpl-2.0.html - # - # Copyright (c) 2006 Simon Urbanek <simon.urbanek@r-project.org> - # Copyright (c) 2017, Oracle and/or its affiliates - # - # All rights reserved. -## - -#' @export -.jnew <- function (class, ..., check = TRUE, silent = !check) { - class <- gsub("/", ".", as.character(class)) - co <- new.java.class(class) - args <- .ellipsisToJObj(co, ...) - o <- .fastr.interop.try(function() { do.call(new.external, args) }, check) - new("jobjRef", jobj=o, jclass=class) -} - - -#' @export -.jcall <- function (obj, returnSig = "V", method, ..., evalArray = TRUE, - evalString = TRUE, check = TRUE, interface = "RcallMethod", - simplify = FALSE, use.true.class = FALSE) { - obj <- .toJObj(obj) - - if (is.character(obj)) { - obj <- gsub("/", ".", as.character(obj)) - obj <- new.java.class(obj) - } - - args <- .ellipsisToJObj(...) - extMethod <- function(...) {obj[method](...)} - r <- .fastr.interop.try(function() { do.call(extMethod, args) }, check) - - if (is.null(r) && returnSig == "V") { - return(invisible(NULL)) - } - - .toS4(r) -} - -#' @export -.jfield <- function (obj, sig = NULL, name, true.class = is.null(sig), convert = TRUE) { - if (isS4(obj)) { - obj <- obj@jobj - } - if (is.character(obj)) { - co <- new.java.class(obj) - r <- co[name] - } else { - r <- obj[name] - } - .toS4(r) -} - -#' @export -.jarray <- function (x, contents.class = NULL, dispatch = FALSE) { - as.java.array(x, ,TRUE) -} - -#' @export -.jevalArray <- function (x, contents.class = NULL, dispatch = FALSE) { - .fastr.interop.fromArray(x) -} - -#' @export -.jbyte <- function (x) { - storage.mode( x ) <- "integer" - new("jbyte", x) -} - -#' @export -.jchar <- function (x) { - storage.mode( x ) <- "character" - new("jchar", x) -} - -#' @export -.jshort <- function (x) { - storage.mode( x ) <- "integer" - new("jshort", x) -} - -#' @export -.jlong <- function (x) { - storage.mode( x ) <- "double" - new("jlong", x) -} - -#' @export -.jfloat <- function (x) { - storage.mode( x ) <- "double" - new("jfloat", x ) -} - -#' @export -J <- function (class, method, ...) { - if (nargs() == 1L && missing(method)) { - if(inherits(class, "jclassName")) { - return(class) - } else if(is.character(class)) { - className <- class - jobj <- .jfindClass(class) - } else { - className <- as.character(class) - jobj <- .jfindClass(className) - } - new("jclassName", name=className, jobj=jobj) - } else { - # .jrcall(class, method, ...) - .jcall(class, , method, ...) - } -} - -#' @export -.jpackage <- function (name, jars='*', morePaths='', nativeLibrary=FALSE, lib.loc=NULL) { - classes <- system.file("java", package = name, lib.loc = lib.loc) - if (nchar(classes)) { - .jaddClassPath(classes) - if (length(jars)) { - if (length(jars) == 1 && jars == "*") { - jars <- grep(".*\\.jar", list.files(classes, - full.names = TRUE), TRUE, value = TRUE) - if (length(jars)) - .jaddClassPath(jars) - } - else .jaddClassPath(paste(classes, jars, sep = .Platform$file.sep)) - } - } - if (any(nchar(morePaths))) { - cl <- as.character(morePaths) - cl <- cl[nchar(cl) > 0] - .jaddClassPath(cl) - } - if (is.logical(nativeLibrary)) { - if (nativeLibrary) { - libs <- "libs" - if (nchar(.Platform$r_arch)) - lib <- file.path("libs", .Platform$r_arch) - lib <- system.file(libs, paste(name, .Platform$dynlib.ext, - sep = ""), package = name, lib.loc = lib.loc) - if (nchar(lib)) - .jaddLibrary(name, lib) - else warning("Native library for `", name, "' could not be found.") - } - } - else { - .jaddLibrary(name, nativeLibrary) - } - invisible(TRUE) -} - -#' @export -.jaddClassPath <- function (path) { - java.addToClasspath(path) -} - -#' @export -.jfindClass <- function (cl, silent = FALSE) { - if (inherits(cl, "jclassName")) return(cl@jobj) - if (!is.character(cl) || length(cl)!=1) { - stop("invalid class name") - } - - cl <- gsub("/", ".", as.character(cl)) - javaClass <- new.java.class(cl) - cls <- new('jobjRef', jobj=javaClass, jclass='java.lang.Class', stringValue=paste0("class ", cl)) - .jcheck(silent) - if (!silent && is.jnull(cls)) stop("class not found") - cls -} - -.toS4 <- function(obj) { - res <- obj - if (is.external(obj)) { - if (is.external.array(obj)) { - res <- as.vector(obj) - } else { - res <- new("jobjRef", jobj=obj, jclass=java.class(obj)) - } - } - res -} - -.ellipsisToJObj <- function(...) { - lapply(list(...), function(x) .toJObj(x)) -} - -.toJObj <- function(x) { - if (is(x, "jobjRef")) { - x@jobj - } else if (is(x, "jclassName")) { - x@jobj@jobj - } else if (is(x, "jbyte")) { - as.external.byte(x) - } else if (is(x, "jchar")) { - as.external.char(x) - } else if (is(x, "jfloat")) { - as.external.float(x) - } else if (is(x, "jlong")) { - as.external.long(x) - } else if (is(x, "jshort")) { - as.external.short(x) - } else { - x - } -} - -#' @export -.jgetEx <- function (clear = FALSE) { - interopEx <- .fastr.interop.getTryException(clear) - if (is.null(interopEx)) - return(NULL) - new("jobjRef", jobj = interopEx, jclass = "java/lang/Throwable") -} - -#' @export -.jclear <- function () { - invisible(.fastr.interop.clearTryException()) -} - -#' @export -.jinit <- function (classpath = NULL, parameters = getOption("java.parameters"), ..., silent = FALSE, force.init = FALSE) { - if (!is.null(classpath)) java.addToClasspath(classpath) -} - -#' @export -.jnull <- function (class = "java/lang/Object") { - new("jobjRef", jobj=NULL, jclass=class) -} - -#' @export -is.jnull <- function (x) { - is.null(x) || is.external.null(x) || (is(x,"jobjRef") && is.null(x@jobj)) -} - -#' @export -.jaddLibrary <- function (name, path) { - cat(paste0("********************************************************\n", - "*** WARNING!!!\n", - "*** .jaddLibrary is not yet implemented.\n", - "*** Please ensure that all native libraries from:\n", - "*** ", path, "\n", - "*** are set on LD_LIBRARY_PATH or java.library.path\n", - "********************************************************\n")) -} - -#' @export -.jcast <- function(obj, new.class="java/lang/Object", check = FALSE, convert.array = FALSE) { - if (!is(obj,"jobjRef")) - stop("cannot cast anything but Java objects") - # TODO implement checks - # if( check && !.jinstanceof( obj, new.class) ){ - # stop( sprintf( "cannot cast object to '%s'", new.class ) ) - # } - - new.class <- gsub("\\.","/", as.character(new.class)) # allow non-JNI specifiation - # if( convert.array && !is( obj, "jarrayRef" ) && isJavaArray( obj ) ){ - # r <- .jcastToArray( obj, signature = new.class) - # } else { - r <- obj - r@jclass <- new.class - # } - r -} - -#' @export -.jstrVal <- function (obj) { - if (is.character(obj)) { - return(obj) - } - r <- NULL - if (!is(obj, "jobjRef")) { - stop("can get value of Java objects only") - } - if(!is.null(obj@stringValue)) { - obj@stringValue - } else { - obj@jobj["toString"]() - } -} - -.isJavaArray <- function(o){ - is.external.array(o) && java.class(o) != NULL -} - -# -# S4 -# - -setClass("truffle.object", representation(jobj="ANY")) -setClassUnion("TruffleObjectOrNull",members=c("truffle.object", "NULL")) -setClassUnion("characterOrNull",members=c("character", "NULL")) - -# -# jobjRef -# -setClass("jobjRef", representation(jobj="TruffleObjectOrNull", jclass="character", stringValue="characterOrNull"), prototype=list(jobj=NULL, jclass="java/lang/Object", stringValue=NULL)) - -setMethod("$", c(x="jobjRef"), function(x, name) { - if(name %in% names(x@jobj)) { - if(is.external.executable(x@jobj[name])) { - function(...) { .jcall(x, , name, ...) } - } else { - .jfield(x, , name) - } - } else if( is.character(name) && length(name) == 1L && name == "length" && is.external.array(x) ) { - length( x@obj ) - } else { - stop(sprintf( "no field, method or inner class called '%s' ", name)) - } -}) - -setMethod("$<-", c(x="jobjRef"), function(x, name, value) { - if(name %in% names(x@jobj)) { - if(!is.external.executable(x@jobj[name])) { - value <- .toJObj(value) - x@jobj[name] <- value - } - } - x -}) - -setMethod("show", c(object="jobjRef"), function(object) { - if (is.jnull(object)) { - show("Java-Object<null>") - } else { - show(paste("Java-Object{", .jstrVal(object), "}", sep='')) - } - invisible(NULL) -}) - -# -# jclassName -# - -setClass("jclassName", representation(name="character", jobj="jobjRef")) -setMethod("show", c(object="jclassName"), function(object) { - invisible(show(paste("Java-Class-Name:", object@name))) -}) -setMethod("as.character", c(x="jclassName"), function(x, ...) x@name) -setMethod("$", c(x="jclassName"), function(x, name) { - if(name == "class") { - x@jobj - } - obj <- x@jobj@jobj - if(name %in% names(obj)) { - if(is.external.executable(obj[name])) { - function(...) { .jcall(obj, , name, ...) } - } else { - .jfield(obj, , name) - } - } else { - stop("no static field, method or inner class called `", name, "' in `", x@name, "'") - } -}) -setMethod("$<-", c(x="jclassName"), function(x, name, value) { - value <- .toJObj(value) - x@jobj@jobj[name] <- value - x -}) - -# TODO makes CMD INSTALL complain -# setGeneric("new") -# setMethod("new", signature(Class="jclassName"), function(Class, ...) .jnew(Class, ...)) - -setClass("jfloat", representation("array")) -setClass("jlong", representation("array")) -setClass("jbyte", representation("array")) -setClass("jshort", representation("array")) -setClass("jchar", representation("array")) - -# -# noop stubs -# - -#' @export -.jsimplify <- function (x) { - x -} - -#' @export -.jcheck <- function(silent = FALSE) { - FALSE -} - -#' @export -.jthrow <- function (exception, message = NULL) { - # do nothing -} diff --git a/com.oracle.truffle.r.pkgs/rJava/R/serialize.R b/com.oracle.truffle.r.pkgs/rJava/R/serialize.R new file mode 100644 index 0000000000000000000000000000000000000000..cb0ef7655508b6caff5f8e16dcbf7f7cdd7f15e1 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/R/serialize.R @@ -0,0 +1,40 @@ +## + # This material is distributed under the GNU General Public License + # Version 2. You may review the terms of this license at + # http://www.gnu.org/licenses/gpl-2.0.html + # + # Copyright (c) 2006 Simon Urbanek <simon.urbanek@r-project.org> + # Copyright (c) 2018, Oracle and/or its affiliates + # + # All rights reserved. +## + +## Java serialization/unserialization + +.jserialize <- function(o) { + if (!is(o, "jobjRef")) + stop("can serialize Java objects only") + .jcall("RJavaClassLoader","[B","toByte",.jcast(o, "java.lang.Object")) +} + +.junserialize <- function(data) { + if (!is.raw(data)) + stop("can de-serialize raw vectors only") + o <- .jcall("RJavaClassLoader","Ljava/lang/Object;","toObjectPL",.jarray(data, dispatch = FALSE)) + if (!is.jnull(o)) { + cl<-try(.jclass(o), silent=TRUE) + if (all(class(cl) == "character")) + o@jclass <- gsub("\\.","/",cl) + } + o +} + +.jcache <- function(o, update=TRUE) { + if (!is(o, "jobjRef")) + stop("o must be a Java object") + if (!is.null(update) && (!is.logical(update) || length(update) != 1)) + stop("update must be TRUE, FALSE of NULL") + what <- update + if (isTRUE(what)) what <- .jserialize(o) + invisible(.Call(javaObjectCache, o@jobj, what)) +} diff --git a/com.oracle.truffle.r.pkgs/rJava/R/tools.R b/com.oracle.truffle.r.pkgs/rJava/R/tools.R new file mode 100644 index 0000000000000000000000000000000000000000..197ff8a40e9d381ac5a450f6eacc8ff1fd607a98 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/R/tools.R @@ -0,0 +1,40 @@ +## + # This material is distributed under the GNU General Public License + # Version 2. You may review the terms of this license at + # http://www.gnu.org/licenses/gpl-2.0.html + # + # Copyright (c) 2006 Simon Urbanek <simon.urbanek@r-project.org> + # Copyright (c) 2018, Oracle and/or its affiliates + # + # All rights reserved. +## + +#' converts a java class name to jni notation +tojni <- function( cl = "java.lang.Object" ){ + gsub( "[.]", "/", cl ) +} + +tojniSignature <- function( cl ){ + sig <- tojni( cl ) + + # TODO FASTR how comes that sig %in% c("boolean", "byte", "char", "double", "float", "int", "long", "short") + if( isPrimitiveTypeName(sig) || isPrimitiveArraySignature(sig) || sig %in% c("boolean", "byte", "char", "double", "float", "int", "long", "short")){ + return( sig ) + } + + n <- nchar( sig ) + last <- substr( sig, n, n ) + add.semi <- last != ";" + + first <- substr( sig, 1, 1 ) + add.L <- ! first %in% c("L", "[" ) + + sig <- if( !add.L && !add.semi) sig else sprintf( "%s%s%s", if( add.L ) "L" else "", sig, if( add.semi ) ";" else "" ) + sig +} + +#' converts jni notation to java notation +tojava <- function( cl = "java/lang/Object" ){ + gsub( "/", ".", cl ) +} + diff --git a/com.oracle.truffle.r.pkgs/rJava/R/with.R b/com.oracle.truffle.r.pkgs/rJava/R/with.R new file mode 100644 index 0000000000000000000000000000000000000000..6fc68e76821ee16c374fd20de71e2a165a89c040 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/R/with.R @@ -0,0 +1,186 @@ +## + # This material is distributed under the GNU General Public License + # Version 2. You may review the terms of this license at + # http://www.gnu.org/licenses/gpl-2.0.html + # + # Copyright (c) 2010 Romain Francois <francoisromain@free.fr> + # Copyright (c) 2018, Oracle and/or its affiliates + # + # All rights reserved. +## + +## This program is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 2 of the License, or +## (at your option) any later version. +## +## This program 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 for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see <http://www.gnu.org/licenses/>. +## +## Author: Romain Francois <francoisromain@free.fr> + +._populate_with_fields_and_methods <- function( env, fields, methods, classes, data, only.static = FALSE ){ + object <- if( only.static ) .jnull() else .jcast( data ) + + # fields + if( !is.jnull(fields) ) { + lapply( fields, function(x ){ + n <- .jcall( x, "S", "getName" ) + type <- .jcall( .jcall( x, "Ljava/lang/Class;", "getType"), "Ljava/lang/String;" , "getName" ) + suffix <- switch( type, + "boolean" = "Boolean", + "byte" = "Byte", + "char" = "Char", + "double" = "Double", + "float" = "Float", + "int" = "Int", + "long" = "Long", + "short" = "Short", + "" ) + target <- switch( type, + "boolean" = "Z", + "byte" = "B", + "char" = "C", + "double" = "D", + "float" = "F", + "int" = "I", + "long" = "L", + "short" = "S", + "Ljava/lang/Object;" ) + set_method <- sprintf( "set%s", suffix) + get_method <- sprintf( "get%s", suffix ) + + makeActiveBinding( n, function(v){ + if( missing(v) ){ + ## get + .jcall( x, target, get_method, object ) + } else { + ## set + .jcall( x, "V", set_method , object, v ) + } + }, env ) + } ) + } + + # methods + if( !is.jnull(methods) ){ + done.this <- NULL + lapply( methods, function(m){ + n <- .jcall( m, "S", "getName" ) + if( n %in% done.this ) return() + fallback <- tryCatch( match.fun( n ), error = function(e) NULL ) + assign( n, function(...) { + tryCatch( .jrcall( if(only.static) data@name else data , n, ...), error = function(e){ + if( !is.null(fallback) && inherits(fallback, "function") ){ + fallback( ... ) + } + } ) + }, env ) + done.this <<- c( done.this, n ) + } ) + } + + # classes + if( !is.jnull( classes ) ){ + lapply( classes, function( cl ){ + name <- .jcall( cl, "S", "getSimpleName" ) + assign( name, new("jclassName", name=.jcall(cl, "S", "getName"), jobj=cl), env ) + } ) + } +} +grabDots <- function( env, ...){ + dots <- list(...) + if( length( dots ) ){ + dots.names <- names(dots) + sapply( dots.names, function( name ){ + if( name != "" ){ + assign( name, dots[[ name ]], env ) + } + } ) + + } +} + +with.jobjRef <- function( data, expr, ...){ + env <- new.env( parent = parent.frame() ) + clazz <- .jcall( data, "Ljava/lang/Class;", "getClass") + + fields <- .jcall( clazz, "[Ljava/lang/reflect/Field;", "getFields" ) + methods <- .jcall( clazz, "[Ljava/lang/reflect/Method;", "getMethods" ) + classes <- .jcall( clazz, "[Ljava/lang/Class;" , "getClasses" ) + ._populate_with_fields_and_methods( env, fields, methods, classes, data, only.static = FALSE ) + + assign( "this", data, env ) + + grabDots( env, ... ) + + eval( substitute( expr ), env ) +} + +within.jobjRef <- function(data, expr, ... ){ + call <- match.call() + call[[1]] <- as.name("with.jobjRef") + eval( call, parent.frame() ) + data +} + +with.jarrayRef <- function( data, expr, ...){ + env <- new.env( parent = environment() ) + clazz <- .jcall( data, "Ljava/lang/Class;", "getClass") + + fields <- .jcall( clazz, "[Ljava/lang/reflect/Field;", "getFields" ) + methods <- .jcall( clazz, "[Ljava/lang/reflect/Method;", "getMethods" ) + classes <- .jcall( clazz, "[Ljava/lang/Class;" , "getClasses" ) + ._populate_with_fields_and_methods( env, fields, methods, classes, data, only.static = FALSE ) + + assign( "this", data, env ) + + # add "length" pseudo field + makeActiveBinding( "length", function(v){ + if( missing( v ) ){ + ._length_java_array( data ) + } else{ + stop( "cannot modify length of java array" ) + } + }, env ) + + grabDots( env, ... ) + + eval( substitute( expr ), env ) +} + +within.jarrayRef <- function(data, expr, ... ){ + call <- match.call() + call[[1]] <- as.name("with.jarrayRef") + eval( call, parent.frame() ) + data +} + +with.jclassName <- function( data, expr, ... ){ + env <- new.env( parent = environment() ) + clazz <- data@jobj + + static_fields <- .jcall( "RJavaTools", "[Ljava/lang/reflect/Field;", "getStaticFields", clazz ) + static_methods <- .jcall( "RJavaTools", "[Ljava/lang/reflect/Method;", "getStaticMethods", clazz ) + static_classes <- .jcall( clazz, "[Ljava/lang/Class;", "getClasses" ) + + ._populate_with_fields_and_methods( env, static_fields, + static_methods, static_classes, data, only.static = TRUE ) + + grabDots( env, ... ) + eval( substitute( expr ), env ) +} + +within.jclassName <- function(data, expr, ... ){ + call <- match.call() + call[[1]] <- as.name("with.jclassName") + eval( call, parent.frame() ) + data +} + + diff --git a/com.oracle.truffle.r.pkgs/rJava/R/zzz.R b/com.oracle.truffle.r.pkgs/rJava/R/zzz.R new file mode 100644 index 0000000000000000000000000000000000000000..c71da4a48727b3c5db780d13d2499e7dd7519910 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/R/zzz.R @@ -0,0 +1,33 @@ +## + # This material is distributed under the GNU General Public License + # Version 2. You may review the terms of this license at + # http://www.gnu.org/licenses/gpl-2.0.html + # + # Copyright (c) 2006 Simon Urbanek <simon.urbanek@r-project.org> + # Copyright (c) 2018, Oracle and/or its affiliates + # + # All rights reserved. +## + +.onLoad <- function(libname, pkgname) { + # FASTR <<<<< + #Sys.setenv("LD_LIBRARY_PATH"=paste(Sys.getenv("LD_LIBRARY_PATH"),"@JAVA_LD@",sep=':')) + ## On OS X with Oracle Java we may need to work around Oracle bug: + ## https://bugs.openjdk.java.net/browse/JDK-7131356 + #if (length(grep("^darwin", R.version$os)) && file.exists("/usr/libexec/java_home")) { + # jh <- Sys.getenv("JAVA_HOME") + # if (!nzchar(jh)) jh <- system("/usr/libexec/java_home", intern=TRUE)[1L] + # if (file.exists(file.path(jh, "jre/lib"))) jh <- file.path(jh, "jre") + # if (file.exists(jli <- file.path(jh, "lib/jli/libjli.dylib"))) { + # dyn.load(jli, FALSE) + # dlp <- Sys.getenv("DYLD_LIBRARY_PATH") + # if (nzchar(dlp)) dlp <- paste0(":", dlp) + # if (file.exists(file.path(jh, "lib/server/libjvm.dylib"))) + # Sys.setenv(DYLD_LIBRARY_PATH=paste0(file.path(jh, "lib/server"), dlp)) + # } + #} + #library.dynam("rJava", pkgname, libname) + # FASTR >>>>> + # pass on to the system-independent part + .jfirst(libname, pkgname) +} diff --git a/com.oracle.truffle.r.pkgs/rJava/man/Exceptions.Rd b/com.oracle.truffle.r.pkgs/rJava/man/Exceptions.Rd new file mode 100644 index 0000000000000000000000000000000000000000..e0907a2146f080b834e543fda365ea66d731de5f --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/Exceptions.Rd @@ -0,0 +1,55 @@ +\name{Exceptions} +\alias{Exceptions} +\alias{$.Throwable} +\alias{$<-.Throwable} +\title{Exception handling} +\description{R handling of java exception} +\usage{ + \S3method{$}{Throwable}(x, name ) + \S3method{$}{Throwable}(x, name ) <- value +} +\arguments{ + \item{x}{condition} + \item{name}{...} + \item{value}{...} +} +\details{ + Java exceptions are mapped to R conditions that are relayed by the + \code{\link{stop}} function. + + The R condition contains the actual exception object as the + \code{jobj} item. + + The class name of the R condition is made of a vector + of simple java class names, the class names without their package + path. This allows the R code to use direct handlers similar to + direct exception handlers in java. See the example below. +} +\examples{ +\dontshow{.jinit()} + +Integer <- J("java.lang.Integer") +tryCatch( Integer$parseInt( "10.." ), NumberFormatException = function(e){ + e$jobj$printStackTrace() +} ) + +# the dollar method is also implemented for Throwable conditions, +# so that syntactic sugar can be used on condition objects +# however, in the example below e is __not__ a jobjRef object reference +tryCatch( Integer$parseInt( "10.." ), NumberFormatException = function(e){ + e$printStackTrace() +} ) + + +\dontshow{ +tryCatch( Integer$parseInt( "10.." ), NumberFormatException = function(e){ + classes <- class( e ) + stopifnot( "NumberFormatException" \%in\% classes ) + stopifnot( "Exception" \%in\% classes ) + stopifnot( "Object" \%in\% classes ) + stopifnot( "error" \%in\% classes ) + stopifnot( "condition" \%in\% classes ) +} ) +} + +} diff --git a/com.oracle.truffle.r.pkgs/rJava/man/J.Rd b/com.oracle.truffle.r.pkgs/rJava/man/J.Rd new file mode 100644 index 0000000000000000000000000000000000000000..fa5a3e2b16bc100f19d107089996323c85277161 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/J.Rd @@ -0,0 +1,76 @@ +\name{J} +\alias{J} +\title{ +High level API for accessing Java +} +\description{ +\code{J} creates a Java class reference or calls a Java method +} +\usage{ +J(class, method, ...) +} +\arguments{ + \item{class}{ + java object reference or fully qualified class name in JNI + notation (e.g "java/lang/String" ) or standard java notation (e.g + "java.lang.String") + } + \item{method}{ + if present then \code{J} results in a method call, otherwise it + just creates a class name reference. + } + \item{\dots}{ + optional parameters that will be passed to the method (if the + \code{method} argument is present) + } +} +\details{ +\code{J} is the high-level access to Java. + +If the \code{method} argument is missing then \code{code} must be a +class name and \code{J} creates a class name reference that can be +used either in a call to \code{new} to create a new Java object +(e.g. \code{new(J("java.lang.String"), "foo")}) or with \code{$} +operator to call a static method +(e.g. \code{J("java.lang.Double")$parseDouble("10.2")}.) + +If the \code{method} argument is present then it must be a string +vector of length one which defines the method to be called on the +object. +} +\value{ + If \code{method} is missing the the returned value is an object of + the class \code{jclassName}. Otherwise the value is the result of + the method invocation. In the latter case Java exceptions may be + thrown and the function doesn't return. +} +\note{ +\code{J} is a high-level API which is slower than \code{\link{.jnew}} +or \code{\link{.jcall}} since it has to use reflection to find the +most suitable method. +} +\seealso{ +\code{\link{.jcall}}, \code{\link{.jnew}} +} +\examples{ +\dontshow{.jinit()} + +if (!nzchar(Sys.getenv("NOAWT"))) { + f <- new(J("java.awt.Frame"), "Hello") + f$setVisible(TRUE) +} + +J("java.lang.Double")$parseDouble("10.2") +J("java.lang.Double", "parseDouble", "10.2" ) + +Double <- J("java.lang.Double") +Double$parseDouble( "10.2") + +# String[] strings = new String[]{ "string", "array" } ; + strings <- .jarray( c("string", "array") ) +# this uses the JList( Object[] ) constructor +# even though the "strings" parameter is a String[] + l <- new( J("javax.swing.JList"), strings) + +} +\keyword{interface} diff --git a/com.oracle.truffle.r.pkgs/rJava/man/accessOp.Rd b/com.oracle.truffle.r.pkgs/rJava/man/accessOp.Rd new file mode 100644 index 0000000000000000000000000000000000000000..a51155d8c857dab587a9321b0825cb8319dbd47a --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/accessOp.Rd @@ -0,0 +1,83 @@ +\name{JavaAccess} +\alias{$,jobjRef-method} +\alias{$,jclassName-method} +\alias{$<-,jobjRef-method} +\alias{$<-,jclassName-method} +\alias{names,jobjRef-method} +\alias{names,jclassName-method} +\alias{names,jarrayRef-method} +\alias{names,jrectRef-method} +\alias{.DollarNames.jobjRef} +\alias{.DollarNames.jclassName} +\alias{.DollarNames.jarrayRef} +\alias{.DollarNames.jrectRef} + +\title{ + Field/method operator for Java objects +} +\description{ + The \code{$} operator for \code{jobjRef} Java object references provides convenience access to object attributes and calling Java methods. +} +\usage{ + \S3method{.DollarNames}{jobjRef} (x, pattern = "" ) + \S3method{.DollarNames}{jarrayRef} (x, pattern = "" ) + \S3method{.DollarNames}{jrectRef} (x, pattern = "" ) + \S3method{.DollarNames}{jclassName}(x, pattern = "" ) +} +\arguments{ + \item{x}{object to complete} + \item{pattern}{pattern} +} +\section{Methods}{ + \describe{ + \item{\code{$}}{\code{signature(x = "jobjRef")}: ... } + \item{\code{$}}{\code{signature(x = "jclassName")}: ... } + \item{\code{$<-}}{\code{signature(x = "jobjRef")}: ... } + \item{\code{$<-}}{\code{signature(x = "jclassName")}: ... } + \item{\code{names}}{\code{signature(x = "jobjRef")}: ... } + \item{\code{names}}{\code{signature(x = "jarrayRef")}: ... } + \item{\code{names}}{\code{signature(x = "jrectRef")}: ... } + \item{\code{names}}{\code{signature(x = "jclassName")}: ... } + } +} +\details{ + rJava provies two levels of API: low-level JNI-API in the form of \code{\link{.jcall}} function and high-level reflection API based on the \code{$} operator. The former is very fast, but inflexible. The latter is a convenient way to use Java-like programming at the cost of performance. The reflection API is build around the \code{$} operator on \code{\link{jobjRef-class}} objects that allows to access Java attributes and call object methods. + + \code{$} returns either the value of the attribute or calls a method, depending on which name matches first. + + \code{$<-} assigns a value to the corresponding Java attribute. + + \code{names} and \code{.DollarNames} returns all fields and methods associated with the object. + Method names are followed by \code{(} or \code{()} depending on arity. + This use of names is mainly useful for code completion, it is not intended to be used programmatically. + + This is just a convenience API. Internally all calls are mapped into \code{\link{.jcall}} calls, therefore the calling conventions and returning objects use the same rules. For time-critical Java calls \code{\link{.jcall}} should be used directly. +} +\seealso{ + \code{\link{J}}, \code{\link{.jcall}}, \code{\link{.jnew}}, \code{\link{jobjRef-class}} +} +\examples{ +\dontshow{.jinit()} + +v <- new(J("java.lang.String"), "Hello World!") +v$length() +v$indexOf("World") +names(v) + +\dontshow{ +stopifnot( v$length() == 12L ) +stopifnot( v$indexOf("World") == 6L ) +} + +J("java.lang.String")$valueOf(10) + +Double <- J("java.lang.Double") +# the class pseudo field - instance of Class for the associated class +# similar to java Double.class +Double$class +\dontshow{ + stopifnot( Double$class$getName() == "java.lang.Double" ) +} + +} +\keyword{interface} diff --git a/com.oracle.truffle.r.pkgs/rJava/man/aslist.Rd b/com.oracle.truffle.r.pkgs/rJava/man/aslist.Rd new file mode 100644 index 0000000000000000000000000000000000000000..a23e34e4abf2fba7c3136f5d3bdf3d1c4145dad2 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/aslist.Rd @@ -0,0 +1,59 @@ +\name{aslist} +\alias{as.list.jobjRef} +\alias{as.list.jarrayRef} +\alias{as.list.jrectRef} +\title{ +Converts java objects or arrays to R lists +} +\description{ + \code{as.list} is implemented for java objects and java arrays + to facilitate using \code{lapply} calls over elements of a java array + or items of an Iterator associated with an Iterable object + + For java array references, \code{as.list} is mapped to + \code{\link{.jevalArray}} + + For java objects that implement the Iterable interface, + the list is created by iterating over the associated iterator +} +\usage{ +\S3method{as.list}{jobjRef}(x, ...) +\S3method{as.list}{jarrayRef}(x, ...) +} +\arguments{ + \item{x}{java array or Iterable java object} + \item{\dots}{ignored} +} +\value{ +An R list, or vector. +} +\note{ +The function is not intended to be called directly. It is implemented +so that java arrays or Iterable java objects can be used as the first +argument of \code{\link{lapply}} +} +\seealso{ + \code{\link{.jevalArray}}, \code{\link{lapply}} +} +\examples{ +\dontshow{.jinit()} + # lapplying over a java array + a <- .jarray( list( + .jnew( "java/awt/Point", 10L, 10L ), + .jnew( "java/awt/Point", 30L, 30L ) + ) ) + lapply( a, function(point){ + with(point, { + (x + y ) ^ 2 + } ) + } ) + +# lapply over a Vector (implements Iterable) +v <- .jnew("java/util/Vector") +v$add( "foo" ) +v$add( .jnew("java/lang/Double", 10.2 ) ) +sapply( v, function(item) item$getClass()$getName() ) + +} +\keyword{ programming } + diff --git a/com.oracle.truffle.r.pkgs/rJava/man/clone.Rd b/com.oracle.truffle.r.pkgs/rJava/man/clone.Rd new file mode 100644 index 0000000000000000000000000000000000000000..5a43481617fee868da07054f5921b2d276b9c0b6 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/clone.Rd @@ -0,0 +1,51 @@ +\name{clone} +\alias{clone} +\alias{clone,jobjRef-method} +\alias{clone,jarrayRef-method} +\alias{clone,jrectRef-method} + +\title{ +Object cloner +} +\description{ +Generic function to clone objects +} +\usage{ +clone(x, ...) +} +\section{Methods}{ + \describe{ + \item{clone}{\code{signature(x = "jobjRef")}: clone a java object reference (must implement Cloneable) } + \item{clone}{\code{signature(x = "jarrayRef")}: clone a java rugged array (not yet implemented) } + \item{clone}{\code{signature(x = "jrectRef")}: clone a java rectangular array (not yet implemented) } + } +} + +\arguments{ + \item{x}{An object to clone} + \item{\dots}{Further arguments, ignored} +} +\value{ +A clone of the object +} +\section{Warning}{ + The implementation of clone for java object references uses + the clone method of the Object class. The reading of its description + in the java help page is \emph{strongly} recommended. +} + +\examples{ +\dontshow{.jinit()} + + p1 <- .jnew("java/awt/Point" ) + p2 <- clone( p1 ) + p2$move( 10L, 10L ) + p1$getX() + + # check that p1 and p2 are not references to the same java object + stopifnot( p1$getX() == 0 ) + stopifnot( p2$getX() == 10 ) + +} +\keyword{ programming } + diff --git a/com.oracle.truffle.r.pkgs/rJava/man/instanceof.Rd b/com.oracle.truffle.r.pkgs/rJava/man/instanceof.Rd new file mode 100644 index 0000000000000000000000000000000000000000..39fd12efa70e2f84abd0e65ff58b1997848bfa91 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/instanceof.Rd @@ -0,0 +1,61 @@ +\name{.jinstanceof} +\Rdversion{1.1} +\alias{\%instanceof\%} +\alias{.jinstanceof} +\title{ +Is a java object an instance of a given java class +} +\description{ +Is a java object an instance of a given java class +} +\usage{ +o \%instanceof\% cl +.jinstanceof( o, cl ) +} +\arguments{ + \item{o}{java object reference} + \item{cl}{java class. This can be a character vector of length one + giving the name of the class, or another java object, or an instance + of the Class class, or a object of class \code{jclassName}.} +} +\value{ +TRUE if o is an instance of cl +} +\author{ +Romain Francois <francoisromain@free.fr> +} +\examples{ +\dontshow{ +.jinit() +} +Double <- J("java.lang.Double") +d <- new( Double, "10.2" ) + +# character +d \%instanceof\% "java.lang.Double" +d \%instanceof\% "java.lang.Number" + +# jclassName +d \%instanceof\% Double + +# instance of Class +Double.class <- Double@jobj +d \%instanceof\% Double.class + +# other object +other.double <- new( Double, 10.2 ) +d \%instanceof\% other.double + +\dontshow{ +% simple unit tests +stopifnot( d \%instanceof\% "java.lang.Double" ) +stopifnot( d \%instanceof\% "java.lang.Number" ) +stopifnot( d \%instanceof\% "java.lang.Object" ) +stopifnot( d \%instanceof\% Double.class ) +stopifnot( d \%instanceof\% other.double ) +stopifnot( d \%instanceof\% Double ) +} + +} +\keyword{ interface } + diff --git a/com.oracle.truffle.r.pkgs/rJava/man/jarray.Rd b/com.oracle.truffle.r.pkgs/rJava/man/jarray.Rd new file mode 100644 index 0000000000000000000000000000000000000000..ac531b4930fc19bb02eca57713534738b50bf6a1 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/jarray.Rd @@ -0,0 +1,100 @@ +\name{jarray} +\alias{.jarray} +\alias{.jevalArray} +\title{ + Java array handling functions +} +\description{ + \code{.jarray} takes a vector (or a list of Java references) as its + argument, creates a Java array containing the elements of the vector + (or list) and returns a reference to such newly created array. + + \code{.jevalArray} takes a reference to a Java array and returns its + contents (if possible). +} +\usage{ +.jarray(x, contents.class = NULL, dispatch = FALSE) +.jevalArray(obj, rawJNIRefSignature = NULL, silent = FALSE, simplify = FALSE) +} +\arguments{ + \item{x}{vector or a list of Java references} + \item{contents.class}{common class of the contained objects, see + details} + \item{obj}{Java object reference to an array that is to be evaluated} + \item{rawJNIRefSignature}{JNI signature that whould be used for + conversion. If set to \code{NULL}, the signature is detected + automatically.} + \item{silent}{if set to true, warnings are suppressed} + \item{dispatch}{logical. If \code{TRUE} the code attemps to dispatch + to either a \code{jarrayRef} object for rugged arrays and + \code{jrectRef} objects for rectangular arrays, creating possibly a + multi-dimensional object in Java (e.g., when used with a matrix).} + \item{simplify}{if set to \code{TRUE} more than two-dimensional arrays + are converted to native obejcts (e.g., matrices) if their type and + size matches (essentially the inverse for objects created with + \code{dispatch=TRUE}).} +} +\value{ + \code{.jarray} returns a Java array reference (\code{jarrayRef} or \code{jrectRef}) to an + array created with the supplied contents. + + \code{.jevalArray} returns the contents of the array object. +} +\details{ + \code{.jarray}: The input can be either a vector of some sort (such as + numeric, integer, logical, ...) or a list of Java references. The + contents is pushed to the Java side and a corresponding array is + created. The type of the array depends on the input vector type. For + example numeric vector creates \code{double[]} array, integer vector + creates \code{int[]} array, character vector \code{String[]} array and + so on. If \code{x} is a list, it must contain Java references only (or + \code{NULL}s which will be treated as \code{NULL} references). + + The \code{contents.class} parameter is used only if \code{x} is a list + of Java object references and it can specify the class that will be + used for all objects in the array. If set to \code{NULL} no assumption + is made and \code{java/lang/Object} will be used. Use with care and + only if you know what you're doing - you can always use + \code{\link{.jcast}} to cast the entire array to another type even if + you use a more general object type. One typical use is to construct + multi-dimensional arrays which mandates passing the array type as + \code{contents.class}. + + The result is a reference to the newly created array. + + The inverse function which fetches the elements of an array reference + is \code{.jevalArray}. + + \code{.jevalArray} currently supports only a subset of all possible + array types. Recursive arrays are handled by returning a list of + references which can then be evaluated separately. The only exception + is \code{simplify=TRUE} in which case \code{.jevalArray} arrempts to + convert multi-dimensional arrays into native R type if there is a + such. This only works for rectangular arrays of the same basic type + (i.e. the length and type of each referenced array is the same - + sometimes matrices are represented that way in Java). +} +\examples{ +\dontshow{.jinit()} +a <- .jarray(1:10) +print(a) +.jevalArray(a) +b <- .jarray(c("hello","world")) +print(b) +c <- .jarray(list(a,b)) +print(c) +# simple .jevalArray will return a list of references +print(l <- .jevalArray(c)) +# to convert it back, use lapply +lapply(l, .jevalArray) + +# two-dimensional array resulting in int[2][10] +d <- .jarray(list(a,a),"[I") +print(d) +# use dispatch to convert a matrix to [[D +e <- .jarray(matrix(1:12/2, 3), dispatch=TRUE) +print(e) +# simplify it back to a matrix +.jevalArray(e, simplify=TRUE) +} +\keyword{interface} diff --git a/com.oracle.truffle.r.pkgs/rJava/man/jarrayRef-class.Rd b/com.oracle.truffle.r.pkgs/rJava/man/jarrayRef-class.Rd new file mode 100644 index 0000000000000000000000000000000000000000..e39e4d5917f0ffff8687af7d2f5d1d566ea31263 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/jarrayRef-class.Rd @@ -0,0 +1,65 @@ +\name{jarrayRef-class} +\docType{class} +\alias{jarrayRef-class} +\alias{[,jarrayRef-method} +\alias{[[,jarrayRef-method} +\alias{[[<-,jarrayRef-method} +\alias{head,jarrayRef-method} +\alias{tail,jarrayRef-method} +\alias{length,jarrayRef-method} +\alias{str,jarrayRef-method} +\alias{unique,jarrayRef-method} +\alias{duplicated,jarrayRef-method} +\alias{anyDuplicated,jarrayRef-method} +\alias{sort,jarrayRef-method} +\alias{rev,jarrayRef-method} +\alias{min,jarrayRef-method} +\alias{max,jarrayRef-method} +\alias{range,jarrayRef-method} + +\title{Class "jarrayRef" Reference to an array Java object } +\description{ This class is a subclass of \link{jobjRef-class} +and represents a reference to an array Java object. } +\section{Objects from the Class}{ +Objects cannot be created directly, but only as the return +value of \code{\link{.jcall}} function. +} +\section{Slots}{ + \describe{ + \item{\code{jsig}:}{JNI signature of the array type} + \item{\code{jobj}:}{Internal identifier of the object} + \item{\code{jclass}:}{Inherited from \code{jobjRef}, but unspecified} + } +} +\section{Methods}{ + \describe{ + \item{[}{\code{signature(x = "jarrayRef")}: \emph{not yet implemented} } + \item{[[}{\code{signature(x = "jarrayRef")}: R indexing of java arrays } + \item{[[<-}{\code{signature(x = "jarrayRef")}: replacement method } + \item{\code{head}}{\code{signature(x = "jarrayRef")}: head of the java array } + \item{\code{tail}}{\code{signature(x = "jarrayRef")}: tail of the java array } + \item{length}{\code{signature(object = "jarrayRef")}: Number of java objects in the java array } + \item{str}{\code{signature(object = "jarrayRef")}: ... } + \item{unique}{\code{signature(x = "jarrayRef")}: \emph{not yet implemented} } + \item{duplicated}{\code{signature(x = "jarrayRef")}: \emph{not yet implemented} } + \item{anyDuplicated}{\code{signature(x = "jarrayRef")}: \emph{not yet implemented} } + \item{sort}{\code{signature(x = "jarrayRef")}: \emph{not yet implemented} } + \item{rev}{\code{signature(x = "jarrayRef")}: \emph{not yet implemented} } + \item{min}{\code{signature(x = "jarrayRef")}: \emph{not yet implemented} } + \item{max}{\code{signature(x = "jarrayRef")}: \emph{not yet implemented} } + \item{range}{\code{signature(x = "jarrayRef")}: \emph{not yet implemented} } + } +} +\section{Extends}{ +Class \code{"\linkS4class{jobjRef}"}, directly. +} +\author{ Simon Urbanek } +\seealso{ + \code{\link{.jcall}} or \code{\linkS4class{jobjRef}} + \code{\linkS4class{jrectRef}} for rectangular arrays +} +% need to find examples of rugged arrays +% \examples{ +% \dontshow{.jinit()} +% } +\keyword{classes} diff --git a/com.oracle.truffle.r.pkgs/rJava/man/java-tools.Rd b/com.oracle.truffle.r.pkgs/rJava/man/java-tools.Rd new file mode 100644 index 0000000000000000000000000000000000000000..925c883c03ede1f7c4337420209c4a7c41d62c58 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/java-tools.Rd @@ -0,0 +1,22 @@ +\name{java-tools} +\alias{java-tools} +\title{java tools used internally in rJava} +\description{java tools used internally in rJava} +\examples{ +\dontshow{ +# running the java unit tests from the R examples +.jinit() +J("RJavaTools_Test")$runtests() +J("RJavaArrayTools_Test")$runtests() +J("ArrayWrapper_Test")$runtests() +J("RectangularArrayBuilder_Test")$runtests() + + + p <- .jnew( "java/awt/Point" ) + classes <- .Call( "RgetSimpleClassNames", p@jobj, TRUE, PACKAGE = "rJava" ) + stopifnot( all( c( "Point", "Point2D", "Object", "error", "condition" ) \%in\% classes ) ) + classes <- .Call( "RgetSimpleClassNames", p@jobj, FALSE, PACKAGE = "rJava" ) + stopifnot( all( c( "Point", "Point2D", "Object" ) \%in\% classes ) ) + +} +} diff --git a/com.oracle.truffle.r.pkgs/rJava/man/javaImport.Rd b/com.oracle.truffle.r.pkgs/rJava/man/javaImport.Rd new file mode 100644 index 0000000000000000000000000000000000000000..cdee86972a7771499e17cf1749d35c3b616c2027 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/javaImport.Rd @@ -0,0 +1,56 @@ +\name{javaImport} +\alias{javaImport} +\title{ +Attach mechanism for java packages +} +\description{ +The \code{javaImport} function creates an item on R's +search that maps names to class names references found in +one or several "imported" java packages. +} +\usage{ +javaImport(packages = "java.lang") +} +\arguments{ + \item{packages}{character vector containing java package paths} +} +\value{ +An external pointer to a java specific \code{UserDefinedDatabase} object +} +\references{ + \emph{User-Defined Tables in the R Search Path}. Duncan Temple Lang. December 4, 2001 + \url{http://www.omegahat.org/RObjectTables/} +} +\author{ +Romain Francois <francoisromain@free.fr> +} +\note{ +Currently the list of objects in the imported package is populated +as new objects are found, \emph{not} at creation time. +} +\section{Warning}{ +This feature is experimental. Use with caution, and don't forget to +detach. +} +\seealso{ +\code{\link{attach}} +} +\examples{ +\dontrun{ + attach( javaImport( "java.util" ), pos = 2 , name = "java:java.util" ) + + # now we can just do something like this + v <- new( Vector ) + v$add( "foobar" ) + ls( pos = 2 ) + + # or this + m <- new( HashMap ) + m$put( "foo", "bar" ) + ls( pos = 2 ) + + # or even this : + Collections$EMPTY_MAP +} +} +\keyword{ programming } diff --git a/com.oracle.truffle.r.pkgs/rJava/man/jcall.Rd b/com.oracle.truffle.r.pkgs/rJava/man/jcall.Rd new file mode 100644 index 0000000000000000000000000000000000000000..b8e801b97c8f25eceb1799f6dac1a96624ef2cfc --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/jcall.Rd @@ -0,0 +1,110 @@ +\name{jcall} +\alias{.jcall} +\title{ + Call a Java method +} +\description{ + \code{.jcall} calls a Java method with the supplied arguments. +} +\usage{ +.jcall(obj, returnSig = "V", method, ..., evalArray = TRUE, + evalString = TRUE, check = TRUE, interface = "RcallMethod", + simplify = FALSE, use.true.class = FALSE) +} +\arguments{ + \item{obj}{Java object (\code{jobjRef} as returned by + \code{\link{.jcall}} or \code{\link{.jnew}}) or fully qualified + class name in JNI notation (e.g. \code{"java/lang/String"}).} + \item{returnSig}{Return signature in JNI notation (e.g. "V" for void, + "[I" for \code{int[]} etc.). For convenience additional type + \code{"S"} is supported and expanded to + \code{"Ljava/lang/String;"}, re-mapping \code{"T"} to represent the + type \code{short}.} + \item{method}{The name of the method to be called} + \item{...}{ + Any parametes that will be passed to the Java method. The parameter + types are determined automatically and/or taken from the + \code{jobjRef} object. All named parameters are discarded.} + \item{evalArray}{This flag determines whether the array return value + is evaluated (\code{TRUE}) or passed back as Java object reference + (\code{FALSE}).} + \item{simplify}{If \code{evalArray} is \code{TRUE} then this argument + is passed to \code{\link{.jevalArray}()}.} + \item{evalString}{This flag determines whether string result is returned + as characters or as Java object reference.} + \item{check}{If set to \code{TRUE} then checks for exceptions are + performed before and after the call using + \code{\link{.jcheck}(silent=FALSE)}. This is usually the desired + behavior, because all calls fail until an expection is cleared.} + \item{interface}{This option is experimental and specifies the + interface used for calling the Java method; the current + implementation supports two interfaces: + \itemize{ + \item{\code{"RcallMethod"}}{the default interface.} + \item{\code{"RcallSyncMethod"}}{synchronized call of a + method. This has simmilar effect as using \code{synchronize} in + Java.} + } + } + \item{use.true.class}{logical. If set to \code{TRUE}, the true class + of the returned object will be used instead of the declared signature. + \code{TRUE} allows for example to grab the actual class of an object when + the return type is an interface, or allows to grab an array when the + declared type is Object and the returned object is an array. Use \code{FALSE} + for efficiency when you are sure about the return type. } +} +\value{ + Returns the result of the method. +} +\details{ + \code{.jcall} requires exact match of argument and return types. For + higher efficiency \code{.jcall} doesn't perform any lookup in the + reflection tables. This means that passing subclasses of the classes + present in the method definition requires explicit casting using + \code{\link{.jcast}}. Passing \code{null} arguments also needs a + proper class specification with \code{\link{.jnull}}. + + Java types \code{long} and \code{float} have no corresponding types in + R and therefore any such parameters must be flagged as such using + \code{\link{.jfloat}} and \code{\link{.jlong}} functions respectively. + + Java also distinguishes scalar and array types whereas R doesn't have + the concept of a scalar. In R a scalar is basically a vector (called + array in Java-speak) of the length 1. Therefore passing vectors of the + length 1 is ambiguous. \code{.jcall} assumes that any vector of the + length 1 that corresponds to a native Java type is a scalar. All other + vectors are passed as arrays. Therefore it is important to use + \code{\link{.jarray}} if an arbitrary vector (including those of the + length 1) is to be passed as an array parameter. + + \emph{Important note about encoding of character vectors:} + Java interface always works with strings in UTF-8 encoding, therefore + the safest way is to run R in a UTF-8 locale. If that is not + possible for some reason, rJava can be used in non-UTF-8 locales, + but care must be taken. Since R 2.7.0 it is possible to associate + encoding with strings and rJava will flag all strings it produces + with the appropriate UTF-8 tag. R will then perform corresponding + appropriate conversions where possible (at a cost of speed and + memory usage), but 3rd party code may not (e.g. older + packages). Also rJava relies on correct encoding flags for strings + passed to it and will attempt to perform conversions where + necessary. If some 3rd party code produces strings incorreclty + flagged, all bets are off. + + Finally, for performance reasons class, method and field names as + well as signatures are not always converted and should not contain + non-ASCII characters. +} +\seealso{ + \code{\link{.jnew}}, \code{\link{.jcast}}, \code{\link{.jnull}}, + \code{\link{.jarray}} +} +\examples{ +\dontshow{.jinit()} +.jcall("java/lang/System","S","getProperty","os.name") +if (!nzchar(Sys.getenv("NOAWT"))) { + f <- .jnew("java/awt/Frame","Hello") + .jcall(f,,"setVisible",TRUE) +} +} +\keyword{interface} diff --git a/com.oracle.truffle.r.pkgs/rJava/man/jcast.Rd b/com.oracle.truffle.r.pkgs/rJava/man/jcast.Rd new file mode 100644 index 0000000000000000000000000000000000000000..513038061931fb887f0688e2fa01bc0e69a52b10 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/jcast.Rd @@ -0,0 +1,45 @@ +\name{jcast} +\alias{.jcast} +\title{ + Cast a Java object to another class +} +\description{ + \code{.jcast} returns a Java object reference cast to another Java class. +} +\usage{ +.jcast(obj, new.class = "java/lang/Object", check = FALSE, convert.array = FALSE) +} +\arguments{ + \item{obj}{a Java object reference} + \item{new.class}{fully qualified class name in JNI notation + (e.g. \code{"java/lang/String"}). } + \item{check}{logical. If \code{TRUE}, it is checked that the object +effectively is an instance of the new class. See \code{\link{\%instanceof\%}}. +Using FALSE (the default) for this argument, rJava does not perform type check and this +will cause an error on the first use if the cast is illegal.} +\item{convert.array}{logical. If \code{TRUE} and the object is an array, +it is converted into a \code{jarrayRef} reference. } +} +\value{ + Returns a Java object reference (\code{jobjRef}) to the object + \code{obj}, changing the object class. +} +\details{ + This function is necessary if a argument of \code{\link{.jcall}} or + \code{\link{.jnew}} is defined as the superclass of the object to be + passed (see \code{\link{.jcall}}). The original object is not modified. + + The default values for the arguments \code{check} and \code{convert.array} + is \code{FALSE} in order to guarantee backwards compatibility, + but it is recommended to set the arguments to \code{TRUE} +} +\seealso{ + \code{\link{.jcall}} +} +\examples{ +\dontrun{ +v <- .jnew("java/util/Vector") +.jcall("java/lang/System","I","identityHashCode",.jcast(v, "java/lang/Object")) +} +} +\keyword{interface} diff --git a/com.oracle.truffle.r.pkgs/rJava/man/jcastToArray.Rd b/com.oracle.truffle.r.pkgs/rJava/man/jcastToArray.Rd new file mode 100644 index 0000000000000000000000000000000000000000..d3543bddd7ae071b9a0ad05e3124a223cb0a70e7 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/jcastToArray.Rd @@ -0,0 +1,69 @@ +\name{jcastToArray} +\alias{.jcastToArray} +\title{ + Ensures that a given object is an array reference +} +\description{ + \code{.jcastToArray} takes a Java object reference of any kind and + returns Java array reference if the given object is a reference to an + array. +} +\usage{ +.jcastToArray(obj, signature=NULL, class="", quiet=FALSE) +} +\arguments{ + \item{obj}{Java object reference to cast or a scalar vector} + \item{signature}{array signature in JNI notation (e.g. \code{"[I"} for + an array of integers). If set to \code{NULL} (the default), + the signature is automatically determined from the object's class.} + \item{class}{force the result to pose as a particular Java + class. This has the same effect as using \code{\link{.jcast}} on the + result and is provided for convenience only.} + \item{quiet}{if set to \code{TRUE}, no failures are reported and the + original object is returned unmodified.} +} +\value{ + Returns a Java array reference (\code{jarrayRef}) on success. If + \code{quiet} is \code{TRUE} then the result can also be the original + object in the case of failure. +} +\details{ + Sometimes a result of a method is by definition of the class + \code{java.lang.Object}, but the acutal referenced object may be an + array. In that case the method returns a Java object reference instead + of an array reference. In order to obtain an array reference, it is + necessary to cast such an object to an array reference - this is done + using the above \code{.jcastToArray} function. + + The input is an object reference that points to an array. Ususally the + signature should be left at \code{NULL} such that it is determined + from the object's class. This is also a check, because if the object's + class is not an array, then the functions fails either with an error + (when \code{quiet=FALSE}) or by returing the original object (when + \code{quiet=TRUE}). If the signature is set to anything else, it is + not verified and the array reference is always created, even if it may + be invalid and unusable. + + For convenience \code{.jcastToArray} also accepts non-references in + which case it simply calls \code{\link{.jarray}}, ignoring all other + parameters. +} +\examples{ +\dontrun{ +a <- .jarray(1:10) +print(a) +# let's create an array containing the array +aa <- .jarray(list(a)) +print(aa) +ba <- .jevalArray(aa)[[1]] +# it is NOT the inverse, because .jarray works on a list of objects +print(ba) +# so we need to cast the object into an array +b <- .jcastToArray(ba) +# only now a and b are the same array reference +print(b) +# for convenience .jcastToArray behaves like .jarray for non-references +print(.jcastToArray(1:10/2)) +} +} +\keyword{interface} diff --git a/com.oracle.truffle.r.pkgs/rJava/man/jcheck.Rd b/com.oracle.truffle.r.pkgs/rJava/man/jcheck.Rd new file mode 100644 index 0000000000000000000000000000000000000000..d9f6efcaf38bf050f1c24a73925d75a0d00ae550 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/jcheck.Rd @@ -0,0 +1,96 @@ +\name{jcheck} +\alias{.jcheck} +\alias{.jthrow} +\alias{.jclear} +\alias{.jgetEx} +\title{ + Java exception handling +} +\description{ + \code{.jcheck} checks the Java VM for any pending exceptions and + clears them. + + \code{.jthrow} throws a Java exception. + + \code{.jgetEx} polls for any pending expections and returns the exception object. + + \code{.jclear} clears a pending exception. +} +\usage{ +.jcheck(silent = FALSE) + +.jthrow(exception, message = NULL) +.jgetEx(clear = FALSE) +.jclear() +} +\arguments{ + \item{silent}{If set to \code{FALSE} then Java is instructed to print + the exception on \code{stderr}. Note that Windows Rgui doesn't show + \code{stderr} so it will not appear there (as of rJava 0.5-1 some + errors that the JVM prints using the vfprintf callback are passed + to R. However, some parts are printed using \code{System.err} in + which case the ususal redirection using the \code{System} class + can be used by the user).} + \item{exception}{is either a class name of an exception to create or a + throwable object reference that is to be thrown.} + \item{message}{if \code{exception} is a class name then this parameter + specifies the string to be used as the message of the exception. This + parameter is ignored if \code{exception} is a reference.} + \item{clear}{if set to \code{TRUE} then the returned exception is also + cleared, otherwise the throwable is returned without clearing the + cause.} +} +\value{ + \code{.jcheck} returns \code{TRUE} if an exception occurred or + \code{FALSE} otherwise. + + \code{.jgetEx} returns \code{NULL} if there are no pending exceptions + or an object of the class "java.lang.Throwable" representing the + current exception. +} +\details{ + Please note that some functions (such as \code{\link{.jnew}} or + \code{\link{.jcall}}) call \code{.jcheck} implicitly unless + instructed to not do so. If you want to handle Java exceptions, you + should make sure that those function don't clear the exception you may + want to catch. + + The exception handling is still as a very low-level and experimental, + because it requires polling of exceptions. A more elaboate system + using constructs similar to \code{try} ... \code{catch} is planned for + next major version of \code{rJava}. + + \emph{Warning:} When requesting exceptions to not be cleared + automatically, please note that the \code{show} method (which is + called by \code{print}) has a side-effect of making a Java call to get + the string representation of a Java object. This implies that it will + be impeded by any pending exceptions. Therefore exceptions obtained + through \code{.jgetEx} can be stored, but should not be printed + (or otherwise used in Java calls) until after the exception is + cleared. In general, all Java calls will fail (possibly silently) + until the exception is cleared. +} +\seealso{ + \code{\link{.jcall}}, \code{\link{.jnew}} +} +\examples{ +\donttest{ +# we try to create a bogus object and +# instruct .jnew to not clear the exception +# this will raise an exception +v <- .jnew("foo/bar", check=FALSE) + +# you can poll for the exception, but don't try to print it +# (see details above) +if (!is.null(e<-.jgetEx())) print("Java exception was raised") + +# expect TRUE result here because the exception was still not cleared +print(.jcheck(silent=TRUE)) +# next invocation will be FALSE because the exception is now cleared +print(.jcheck(silent=TRUE)) + +# now you can print the actual expection (even after it was cleared) +print(e) +} +} +\keyword{interface} diff --git a/com.oracle.truffle.r.pkgs/rJava/man/jclassName.Rd b/com.oracle.truffle.r.pkgs/rJava/man/jclassName.Rd new file mode 100644 index 0000000000000000000000000000000000000000..c7263d027d15e9effbd166370f22f44b855ebe22 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/jclassName.Rd @@ -0,0 +1,41 @@ +\name{jclassName-class} +\docType{class} +\alias{jclassName-class} +\alias{as.character,jclassName-method} + +\title{Class "jclassName" - a representation of a Java class name } +\description{ This class holds a name of a class in Java. } +\section{Objects from the Class}{ +Objects of this class should *not* be created directly. Instead, the +function \code{\link{J}} should be used to create new objects of this class. +} +\section{Slots}{ + \describe{ + \item{\code{name}:}{Name of the class (in source code notation)} + \item{\code{jobj}:}{Object representing the class in Java} + } +} +\section{Methods}{ + The objects of class \code{jclassName} are used indirectly to be able + to create new Java objects via \code{new} such as + \code{new(J("java.lang.String"), "foo")} or to use the \code{$} + convenience operator on static classes, such as + \code{J("java.lang.Double")$parseDouble("10.2")}. + + \describe{ + \item{\code{as.character}}{\code{signature(x = "jclassName")}: + returns the class name as a string vector of length one. + } + } +} +%\references{ ~put references to the literature/web site here ~ } +\author{ Simon Urbanek } +%\note{ ~~further notes~~ } +% ~Make other sections like Warning with \section{Warning }{....} ~ +\seealso{ + \code{\link{J}}, \code{\link{new}} +} +%\examples{ +%##---- Should be DIRECTLY executable !! ---- +%} +\keyword{classes} diff --git a/com.oracle.truffle.r.pkgs/rJava/man/jequals.Rd b/com.oracle.truffle.r.pkgs/rJava/man/jequals.Rd new file mode 100644 index 0000000000000000000000000000000000000000..e49d8bbbd7cc90bd6bf64e4062c5f0ff1e6288f2 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/jequals.Rd @@ -0,0 +1,162 @@ +\name{jequals} +\alias{.jequals} +\alias{.jcompare} +\alias{!=,ANY,jobjRef-method} +\alias{!=,jobjRef,jobjRef-method} +\alias{!=,jobjRef,ANY-method} +\alias{==,ANY,jobjRef-method} +\alias{==,jobjRef,jobjRef-method} +\alias{==,jobjRef,ANY-method} +\alias{<,ANY,jobjRef-method} +\alias{<,jobjRef,jobjRef-method} +\alias{<,jobjRef,ANY-method} +\alias{>,ANY,jobjRef-method} +\alias{>,jobjRef,jobjRef-method} +\alias{>,jobjRef,ANY-method} +\alias{<=,ANY,jobjRef-method} +\alias{<=,jobjRef,jobjRef-method} +\alias{<=,jobjRef,ANY-method} +\alias{>=,ANY,jobjRef-method} +\alias{>=,jobjRef,jobjRef-method} +\alias{>=,jobjRef,ANY-method} + +\title{ + Comparing Java References +} +\description{ + \code{.jequals} function can be used to determine whether two objects + are equal. In addition, it allows mixed comparison of non-Java object + for convenience, unless strict comparison is desired. + + The binary operators \code{==} and \code{!=} are mapped to + (non-strict) call to \code{.jequals} for convenience. + + \code{.jcompare} compares two objects in the sense of the + \code{java.lang.Comparable} interface. + + The binary operators \code{<}, \code{>}, \code{<=}, \code{>=} are mapped + to calls to \code{.jcompare} for convenience +} +\usage{ +.jequals(a, b, strict = FALSE) +.jcompare( a, b ) +} +\arguments{ + \item{a}{first object} + \item{b}{second object} + \item{strict}{when set to \code{TRUE} then non-references save for + \code{NULL} are always treated as different, see details.} +} +\value{ + \code{.jequals} returns \code{TRUE} if both object + are considered equal, \code{FALSE} otherwise. + + \code{.jcompare} returns the result of the \code{compareTo} java method + of the object a applied to b +} +\section{Methods}{ + \describe{ + \item{!=}{\code{signature(e1 = "ANY", e2 = "jobjRef")}: ... } + \item{!=}{\code{signature(e1 = "jobjRef", e2 = "jobjRef")}: ... } + \item{!=}{\code{signature(e1 = "jobjRef", e2 = "ANY")}: ... } + \item{==}{\code{signature(e1 = "ANY", e2 = "jobjRef")}: ... } + \item{==}{\code{signature(e1 = "jobjRef", e2 = "jobjRef")}: ... } + \item{==}{\code{signature(e1 = "jobjRef", e2 = "ANY")}: ... } + \item{<}{\code{signature(e1 = "ANY", e2 = "jobjRef")}: ... } + \item{<}{\code{signature(e1 = "jobjRef", e2 = "jobjRef")}: ... } + \item{<}{\code{signature(e1 = "jobjRef", e2 = "ANY")}: ... } + \item{>}{\code{signature(e1 = "ANY", e2 = "jobjRef")}: ... } + \item{>}{\code{signature(e1 = "jobjRef", e2 = "jobjRef")}: ... } + \item{>}{\code{signature(e1 = "jobjRef", e2 = "ANY")}: ... } + \item{>=}{\code{signature(e1 = "ANY", e2 = "jobjRef")}: ... } + \item{>=}{\code{signature(e1 = "jobjRef", e2 = "jobjRef")}: ... } + \item{>=}{\code{signature(e1 = "jobjRef", e2 = "ANY")}: ... } + \item{<=}{\code{signature(e1 = "ANY", e2 = "jobjRef")}: ... } + \item{<=}{\code{signature(e1 = "jobjRef", e2 = "jobjRef")}: ... } + \item{<=}{\code{signature(e1 = "jobjRef", e2 = "ANY")}: ... } + + } +} +\details{ + \code{.jequals} compares two Java objects by calling \code{equals} + method of one of the objects and passing the other object as its + argument. This allows Java objects to define the `equality' in + object-dependent way. + + In addition, \code{.jequals} allows the comparison of Java object to + other scalar R objects. This is done by creating a temporary Java + object that corresponds to the R object and using it for a call to the + \code{equals} method. If such conversion is not possible a warning is + produced and the result it \code{FALSE}. The automatic conversion + will be avoided if \code{strict} parameter is set to \code{TRUE}. + + \code{NULL} values in \code{a} or \code{b} are replaced by Java + \code{null}-references and thus \code{.jequals(NULL,NULL)} is \code{TRUE}. + + If neither \code{a} and \code{b} are Java objects (with the exception + of both being \code{NULL}) then the result is identical to that of + \code{all.equal(a,b)}. + + Neither comparison operators nor \code{.jequals} supports vectors and + returns \code{FALSE} in that case. A warning is also issued unless + strict comparison was requested. +} +\note{ + Don't use \code{x == NULL} to check for + \code{null}-references, because \code{x} could be \code{NULL} and thus + the result would be an empty vector. Use \code{\link{is.jnull}} + instead. + (In theory \code{is.jnull} and \code{x == .jnull()} are the the same, + but \code{is.jnull} is more efficient.) +} +\seealso{ + \code{\link{is.jnull}} +} +\examples{ +\dontshow{.jinit()} +s <- .jnew("java/lang/String", "foo") +.jequals(s, "foo") # TRUE +.jequals(s, "foo", strict=TRUE) # FALSE - "foo" is not a Java object +t <- s +.jequals(s, t, strict=TRUE) # TRUE + +s=="foo" # TRUE + +\dontshow{ + stopifnot( + .jequals(s, "foo"), + !.jequals(s, "foo", strict=TRUE), + .jequals(s, t, strict=TRUE), + s == "foo" + ) +} + +Double <- J("java.lang.Double") +d1 <- new( Double, 0.0 ) +d2 <- new( Double, 1.0 ) +d3 <- new( Double, 0.0 ) + +d1 < d2 +d1 <= d3 +d1 >= d3 +d1 > d2 + +# cannot compare a Double and a String +try( d1 < "foo" ) + +# but can compare a Double and an Integer +d1 < 10L + +\dontshow{ + stopifnot( + d1 < d2 , + d1 <= d3 , + d1 >= d3 , + ! (d1 > d2 ) , + inherits( try( d1 < "foo", silent = TRUE ), "try-error" ), + d1 < 10L ) +} + + +} +\keyword{interface} diff --git a/com.oracle.truffle.r.pkgs/rJava/man/jfield.Rd b/com.oracle.truffle.r.pkgs/rJava/man/jfield.Rd new file mode 100644 index 0000000000000000000000000000000000000000..46438c89f771a973561afcf9912d41ddbfefed55 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/jfield.Rd @@ -0,0 +1,57 @@ +\name{jfield} +\alias{.jfield} +\alias{.jfield<-} +\title{ + Obtains the value of a field +} +\description{ + \code{.jfield} returns the value of the specified field on an object. +} +\usage{ +.jfield(o, sig = NULL, name, true.class = is.null(sig), convert = TRUE) +`.jfield<-`(o, name, value) +} +\arguments{ + \item{o}{Class name or object (Java reference) whose field is to be + accessed. Static fields are supported both by specifying the class + name or using an instance.} + \item{sig}{signature (JNI type) of the field. If set to \code{NULL} + rJava attempts to determine the signature using reflection. For + efficiency it is recommended to specify the signature, because + the reflection lookup is quite expensive.} + \item{name}{name of the field to access} + \item{true.class}{by default the class of the resulting object matches + the siganture of the field. Setting this flag to \code{TRUE} causes + \code{.jfield} to use true class name of the resulting object + instead. (this flag has no effect on scalar fields)} + \item{convert}{when set to \code{TRUE} all references are converted to + native types (where possible). Otherwise Java references are + returned directly.} + \item{value}{value to assign into the field. The field signature is + determined from the value in the same way that parameter signatures + are determined in \code{\link{.jcall}} - be sure to cast the value + as necessary, no automatic conversion is done.} +} +\value{ + \code{.jfield}: contents of the field, \code{.jfield<-}: modified object. +} +\details{ + The detection of a field signature in \code{.jfield} using reflection + is considerably expensive (more than 3 additional method calls have to + be performed), therefore it is recommended for time-critical code to + specify the field signature beforehand. + + NOTE: The sequence of arguments in \code{.jfield} has been changed + since rJava 0.5 to be more consistent and match the sequence in + \code{.jcall}. Also \code{.jsimplify} is no longer needed as primitive + types are obtained directly. +} +\seealso{ + \code{\link{.jcall}} +} +\examples{ +\dontrun{ +.jfield("java/lang/Boolean",, "TYPE") +} +} +\keyword{interface} diff --git a/com.oracle.truffle.r.pkgs/rJava/man/jfloat-class.Rd b/com.oracle.truffle.r.pkgs/rJava/man/jfloat-class.Rd new file mode 100644 index 0000000000000000000000000000000000000000..f7eb877cc4e5e5fefc57ff01a417ffa9c4b06bb9 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/jfloat-class.Rd @@ -0,0 +1,54 @@ +\name{jfloat-class} +\docType{class} +\alias{jfloat-class} +\alias{jlong-class} +\alias{jbyte-class} +\alias{jchar-class} +\title{Classes "jfloat", "jlong", "jbyte" and "jchar" specify Java + native types that are not native in R} +\description{ These classes wrap a numeric vector to be treated as + \code{float} or \code{long} argument when passed to Java and an + integer vector to be treated as \code{byte} or \code{char}. R doesn't + distinguish between \code{double} and \code{float}, but Java + does. In order to satisfy object types, numeric vectors that should be + converted to floats or long on the Java side must be wrapped in this + class. In addition \code{jbyte} must be used when passing scalar byte + (but not byte arrays, those are mapped into RAW vectors). Finally + \code{jchar} it used when mapping integer vectors into unicode Java + character vectors.} +\section{Objects from the Class}{ + Objects can be created by calling \code{\link{.jfloat}}, + \code{\link{.jlong}}, \code{\link{.jbyte}} or \code{\link{.jchar}} + respectively. +} +\section{Slots}{ + \describe{ + \item{\code{.Data}:}{Payload} + } +} +\section{Extends}{ + "jfloat" and "jlong": + Class \code{"numeric"}, from data part. + Class \code{"vector"}, by class \code{"numeric"}. + + "jbyte" and "jchar": + Class \code{"integer"}, from data part. + Class \code{"vector"}, by class \code{"integer"}. +} +\section{Methods}{ + "jfloat" and "jlong" have no methods other than those inherited from "numeric". + "jbyte" and "jchar" have no methods other than those inherited from "integer". +} +%\references{ ~put references to the literature/web site here ~ } +\author{ Simon Urbanek } +%\note{ ~~further notes~~ } + +% ~Make other sections like Warning with \section{Warning }{....} ~ + +\seealso{ + \code{\link{.jfloat}}, \code{\link{.jlong}}, \code{\link{.jbyte}}, \code{\link{.jchar}} and \code{\link{.jcall}} +} +%\examples{ +%##---- Should be DIRECTLY executable !! ---- +%} +\keyword{classes} diff --git a/com.oracle.truffle.r.pkgs/rJava/man/jfloat.Rd b/com.oracle.truffle.r.pkgs/rJava/man/jfloat.Rd new file mode 100644 index 0000000000000000000000000000000000000000..378918d92055460c2cd5a7910b4ad8d140571e13 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/jfloat.Rd @@ -0,0 +1,66 @@ +\name{jfloat} +\alias{.jfloat} +\alias{.jlong} +\alias{.jbyte} +\alias{.jchar} +\alias{.jshort} +\alias{jfloat} +\alias{jlong} +\alias{jbyte} +\alias{jchar} +\alias{jshort} +\title{ + Wrap numeric vector as flat Java parameter +} +\description{ + \code{.jfloat} marks a numeric vector as an object that can be used + as parameter to Java calls that require \code{float} parameters. + Similarly, \code{.jlong} marks a numeric vector as \code{long} parameter. +} +\usage{ +.jfloat(x) +.jlong(x) +.jbyte(x) +.jchar(x) +.jshort(x) +} +\arguments{ + \item{x}{numeric vector} +} +\value{ + Returns a numeric vector of the class \code{jfloat}, \code{jlong}, + \code{jbyte}, \code{jshort} or \code{jchar} + that can be used as parameter to Java calls that require + \code{float}, \code{long}, \code{byte}, \code{short} or \code{char} + parameters respectively. +} +\details{ + R has no native \code{float} or \code{long} type. Numeric vectors are + stored as \code{double}s, hence there is no native way to pass float + numbers to Java methods. The \code{.jfloat} call marks a numeric + vector as having the Java type \code{float} by wrapping it in the + \code{jfloat} class. The class is still a subclass of \code{numeric}, + therefore all regular R operations are unaffected by this. + + Similarly, \code{.jlong} is used to mark a numeric vector as a + parameter of the \code{long} Java type. Please note that in general R + has no native type that will hold a \code{long} value, so conversion + between Java's \code{long} type and R's numeric is potentially lossy. + + \code{.jbyte} is used when a scalar byte is to be passed ot Java. Note + that byte arrays are natively passed as RAW vectors, not as + \code{.jbyte} arrays. + + \code{jchar} is strictly experimental and may be based on + \code{character} vectors in the future. +} +\seealso{ + \code{\link{.jcall}}, \code{\link{jfloat-class}} +} +%\examples{ +%\dontrun{ +%v <- .jnew("java/util/Vector") +%.jcall("java/lang/System","I","identityHashCode",.jcast(v, "java/lang/Object")) +%} +%} +\keyword{interface} diff --git a/com.oracle.truffle.r.pkgs/rJava/man/jinit.Rd b/com.oracle.truffle.r.pkgs/rJava/man/jinit.Rd new file mode 100644 index 0000000000000000000000000000000000000000..075f5210832aa7c3ff6f04db9fbd1dde168128d8 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/jinit.Rd @@ -0,0 +1,74 @@ +\name{jinit} +\alias{.jinit} +\title{ + Initialize Java VM +} +\description{ + \code{.jinit} initializes the Java Virtual Machine (JVM). This + function must be called before any rJava functions can be used. +} +\usage{ +.jinit(classpath = NULL, parameters = getOption("java.parameters"), ..., +silent = FALSE, force.init = FALSE) +} +\arguments{ + \item{classpath}{Any additional classes to include in the Java class + paths (i.e. locations of Java classes to use). This path will be + prepended to paths specified in the \code{CLASSPATH} environment + variable. Do NOT set this system class path initializing a package, + use \code{\link{.jpackage}} instead, see details.} + \item{parameters}{character vector of parameters to be passed to + the virtual machine. They are implementation dependent and apply + to JDK version 1.2 or higher only. Please note that each parameter + must be in a separate element of the array, you cannot use a + space-separated string with multiple parameters.} + \item{...}{Other optional Java initialization parameters (implementation-dependent).} + \item{silent}{If set to \code{TRUE} no warnings are issued.} + \item{force.init}{If set to \code{TRUE} JVM is re-initialized even if + it is already running.} +} +\value{ + The return value is an integer specifying whether and how the VM was + initialized. Negative values indicate failure, zero denotes successful + initialization and positive values signify partially successful + initilization (i.e. the VM is up, but parameters or class path could + not be set due to an existing or incompatible VM). +} +\details{ + Starting with version 0.5 rJava provides a custom class loader that can + automatically track classes and native libraries that are provided in + R packages. Therefore R packages should NOT use \code{.jinit}, but + call \code{\link{.jpackage}} instead. In addition this allows the use + of class path modifying function \code{\link{.jaddClassPath}}. + + Important note: if a class is found on the system class path (i.e. on + the \code{classpath} specified to \code{.jinit}) then the system class + loader is used instead of the rJava loader, which can lead to problems + with reflection and native library support is not enabled. Therefore + it is highly recommended to use \code{.jpackage} or + \code{.jaddClassPath} instead of \code{classpath} (save for system + classes). + + Stating with version 0.3-8 rJava is now capable of modifying the class + path on the fly for certain Sun-based Java virtual machines, even when + attaching to an existing VM. However, this is done by exploiting the + way ClassLoader is implemented and may fail in the future. In general + it is officially not possible to change the class path of a running + VM. + + At any rate, it is impossible to change any other VM parameters of a + running VM, so when using \code{.jinit} in a package, be generous with + limits and don't use VM parameters to unnecessarily restrict + resources (or preferably use \code{\link{.jpackage}} instead). +} +\seealso{ + \code{\link{.jpackage}} +} +\examples{ +\dontrun{ +## set heap size limit to 512MB (see java -X) and +## use "myClasses.jar" as the class path +.jinit(classpath="myClasses.jar", parameters="-Xmx512m") +} +} +\keyword{interface} diff --git a/com.oracle.truffle.r.pkgs/rJava/man/jmemprof.Rd b/com.oracle.truffle.r.pkgs/rJava/man/jmemprof.Rd new file mode 100644 index 0000000000000000000000000000000000000000..9f0857bef89de7d9138a0a26b4f40d7a432dbc31 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/jmemprof.Rd @@ -0,0 +1,47 @@ +\name{jmemprof} +\alias{.jmemprof} +\title{ + rJava memory profiler +} +\description{ + \code{.jmemprof} enables or disables rJava memory profiling. If rJava + was compiled without memory profiling support, then a call to this + function always causes an error. +} +\usage{ +.jmemprof(file = "-") +} +\arguments{ + \item{file}{file to write profiling information to or \code{NULL} to + disable profiling} +} +\value{ + Returns \code{NULL}. +} +\details{ + The \code{file} parameter must be either a filename (which will be + opened in append-mode) or "-" to use standard output or \code{NULL} to + disable profiling. An empty string "" is equivalent to \code{NULL} in + this context. + + Note that lots of finalizers are run only when R exists, so usually + you want to enable profiling early and let R exit to get a sensible + profile. Runninng gc may be helpful to get rid of references that can + be collected in R. + + A simple perl script is provided to analyze the result of the + profiler. Due to its simple text format, it is possible to capture + entire stdout including the profiler information to have both the + console context for the allocations and the profile. Memory profiling + is also helful if rJava debug is enabled. + + Note that memory profiling support must be compiled in rJava and it is + by default compiled only if debug mode is enabled (which is not the + case by default). +} +\examples{ +\donttest{ +.jmemprof("rJava.mem.profile.txt") +} +} +\keyword{interface} diff --git a/com.oracle.truffle.r.pkgs/rJava/man/jnew.Rd b/com.oracle.truffle.r.pkgs/rJava/man/jnew.Rd new file mode 100644 index 0000000000000000000000000000000000000000..d599012346b9c68b248b07d50eca708ca537a36b --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/jnew.Rd @@ -0,0 +1,51 @@ +\name{jnew} +\alias{.jnew} +\title{ + Create a Java object +} +\description{ + \code{.jnew} create a new Java object. +} +\usage{ +.jnew(class, ..., check=TRUE, silent=!check, class.loader=NULL) +} +\arguments{ + \item{class}{fully qualified class name in JNI notation (e.g. \code{"java/lang/String"}).} + \item{...}{ + Any parameters that will be passed to the corresponding + constructor. The parameter types are determined automatically and/or + taken from the \code{jobjRef} object. For details see + \code{\link{.jcall}}. Note that all named parameters are discarded.} + \item{check}{ + If set to \code{TRUE} then \code{\link{.jcheck}} is invoked before + and after the call to the constructor to clear any pending Java + exceptions.} + \item{silent}{ + If set to \code{FALSE} then \code{.jnew} will fail with an error if + the object cannot be created, otherwise a null-reference is returned + instead. In addition, this flag is also passed to final + \code{.jcheck} if \code{check} above is set to \code{TRUE}. Note + that the error handling also clears exceptions, so + \code{check=FALSE, silent=FALSE} is usually not a meaningful + combination. + } + \item{class.loader}{optional class loader to force for loading the + class. If not set, the rJava class loader is used first. The default + Java class loader is always used as a last resort. This is for + expert use only! If you set the class loader, the class loading + behavior changes - use only in very special circumstances.} +} +\value{ + Returns the reference (\code{jobjRef}) to the newly created object or + \code{null}-reference (see \code{\link{.jnull}}) if something went wrong. +} +\seealso{ + \code{\link{.jcall}}, \code{\link{.jnull}} +} +\examples{ +\dontrun{ +f <- .jnew("java/awt/Frame","Hello") +.jcall(f,,"setVisible",TRUE) +} +} +\keyword{interface} diff --git a/com.oracle.truffle.r.pkgs/rJava/man/jnull.Rd b/com.oracle.truffle.r.pkgs/rJava/man/jnull.Rd new file mode 100644 index 0000000000000000000000000000000000000000..2f316f7faff4ced0bca90d90c797575ef7761a1a --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/jnull.Rd @@ -0,0 +1,57 @@ +\name{jnull} +\alias{.jnull} +\alias{is.jnull} +\title{ + Java null object reference +} +\description{ + \code{.jnull} returns a \code{null} reference of a specified class + type. The resulting object is of the class \code{jobjRef}. + + \code{is.jnull} is an extension of \code{is.null} that also returns + \code{TRUE} if the supplied object is a \code{null} Java reference. +} +\usage{ +.jnull(class = "java/lang/Object") +is.jnull(x) +} +\arguments{ + \item{class}{fully qualified target class name in JNI notation + (e.g. \code{"java/lang/String"}).} + \item{x}{object to check} +} +\value{ + \code{.jnull} returns a Java object reference (\code{jobjRef}) of a + \code{null} object having the specified object class. + + \code{is.jnull} returns \code{TRUE} if \code{is.null(x)} is + \code{TRUE} or if \code{x} is a Java \code{null} reference. +} +\details{ + \code{.jnull} is necesary if \code{null} is to be passed as an + argument of \code{\link{.jcall}} or \code{\link{.jnew}}, in order to be + able to find the correct method/constructor. + + Example: given the following method definitions of the class \code{A}: + \itemize{ + \item{o}{public static void run(String a);} + \item{o}{public static void run(Double n);} + } + Calling \code{.jcall("A",,"run",NULL)} is ambiguous, because it is + unclear which method is to be used. Therefore rJava requires class + information with each argument to \code{\link{.jcall}}. If we wanted + to run the String-version, we could use + \code{.jcall("A",,"run",.jnull("java/lang/String"))}. + + \code{is.jnull} is a test that should be used to determine whether a + given Java reference is a \code{null} reference. +} +\seealso{ + \code{\link{.jcall}}, \code{\link{.jcast}} +} +\examples{ +\dontrun{ +.jcall("java/lang/System","I","identityHashCode",.jnull()) +} +} +\keyword{interface} diff --git a/com.oracle.truffle.r.pkgs/rJava/man/jobjRef-class.Rd b/com.oracle.truffle.r.pkgs/rJava/man/jobjRef-class.Rd new file mode 100644 index 0000000000000000000000000000000000000000..af2bfa852abaa9a6f7048a41485afcfe3d0ee1f3 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/jobjRef-class.Rd @@ -0,0 +1,24 @@ +\name{jobjRef-class} +\docType{class} +\alias{jobjRef-class} + +\title{Class "jobjRef" - Reference to a Java object } +\description{ This class describes a reference to an object held in a JavaVM. } +\section{Objects from the Class}{ +Objects of this class should *not* be created directly. Instead, the function \code{\link{.jnew}} should be use to create new Java objects. They can also be created as results of the \code{\link{.jcall}} function. +} +\section{Slots}{ + \describe{ + \item{\code{jobj}:}{Internal identifier of the object (external pointer to be precise)} + \item{\code{jclass}:}{Java class name of the object (in JNI notation)} + } + Java-side attributes are not accessed via slots, but the \code{$} operator instead. +} +\section{Methods}{ +This object's Java methods are not accessed directly. Instead, \code{\link{.jcall}} JNI-API should be used for invoking Java methods. For convenience the \code{$} operator can be used to call methods via reflection API. +} +\author{ Simon Urbanek } +\seealso{ + \code{\link{.jnew}}, \code{\link{.jcall}} or \code{\link{jarrayRef-class}} +} +\keyword{classes} diff --git a/com.oracle.truffle.r.pkgs/rJava/man/jpackage.Rd b/com.oracle.truffle.r.pkgs/rJava/man/jpackage.Rd new file mode 100644 index 0000000000000000000000000000000000000000..29daddf40f423213ae23a5ef3b4b0dd06b82ba8d --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/jpackage.Rd @@ -0,0 +1,66 @@ +\name{jpackage} +\alias{.jpackage} +\title{ + Initialize an R package containing Java code +} +\description{ + \code{.jpackage} initializes the Java Virtual Machine (JVM) for an R + package. In addition to starting the JVM it also registers Java + classes and native code contained in the package with the JVM. + function must be called before any rJava functions can be used. +} +\usage{ +.jpackage(name, jars='*', morePaths='', nativeLibrary=FALSE, lib.loc=NULL) +} +\arguments{ + \item{name}{name of the package. It should correspond to the + \code{pkgname} parameter of \code{.onLoad} or \code{.First.lib} + function.} + \item{jars}{Java archives in the \code{java} directory of the package + that should be added to the class path. The paths must be relative + to package's \code{java} directory. A special value of + \code{'*'} adds all \code{.jar} files from the \code{java} the + directory.} + \item{morePaths}{vector listing any additional entries that should + be added to the class path.} + \item{nativeLibrary}{a logical determining whether rJava should look + for native code in the R package's shared object or not.} + \item{lib.loc}{a character vector with path names of R libraries, or + \code{NULL} (see \code{\link{system.file}} and examples below).} + +} +\value{ + The return value is an invisible TRUE if the initialization was successful. +} +\details{ + \code{.jpackage} initializes a Java R package as follows: first the + JVM is initialized via \code{\link{.jinit}} (if it is not running + already). Then the \code{java} directory of the package is added to + the class path. Then \code{.jpackage} prepends \code{jars} with the + path to the \code{java} directory of the package and adds them to the + class path (or all \code{.jar} files if \code{'*'} was specified). + Finally the \code{morePaths} parameter (if set) is passed to a call + to \code{\link{.jaddClassPath}}. + + Therefore the easiest way to create a Java package is to add + \code{.jpackage(pkgname, lib.loc=libname)} in \code{.onLoad} or + \code{.First.lib}, and copy all necessary classes to a JAR file(s) + which is placed in the \code{inst/java/} directory of the source + package. + + If a package needs special Java parameters, \code{"java.parameters"} + option can be used to set them on initialization. Note, however, that + Java parameters can only be used during JVM initialization and other + package may have intialized JVM already. +} +\seealso{ + \code{\link{.jinit}} +} +\examples{ +\dontrun{ +.onLoad <- function(libname, pkgname) { + .jpackage(pkgname, lib.loc=libname) +} +} +} +\keyword{interface} diff --git a/com.oracle.truffle.r.pkgs/rJava/man/jrectRef-class.Rd b/com.oracle.truffle.r.pkgs/rJava/man/jrectRef-class.Rd new file mode 100644 index 0000000000000000000000000000000000000000..88787d014f0e107b84c319344e7da964e3ba10e8 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/jrectRef-class.Rd @@ -0,0 +1,278 @@ +\name{jrectRef-class} +\Rdversion{1.1} +\docType{class} +\alias{jrectRef-class} +\alias{[,jrectRef-method} +\alias{length,jrectRef-method} +\alias{str,jrectRef-method} +\alias{dim,jrectRef-method} +\alias{dim<-,jrectRef-method} +\alias{unique,jrectRef-method} +\alias{duplicated,jrectRef-method} +\alias{anyDuplicated,jrectRef-method} +\alias{sort,jrectRef-method} +\alias{rev,jrectRef-method} +\alias{min,jrectRef-method} +\alias{max,jrectRef-method} +\alias{range,jrectRef-method} + +\title{Rectangular java arrays} +\description{References to java arrays that are guaranteed to be rectangular, i.e similar +to R arrays} +\section{Objects from the Class}{ +Objects of this class should *not* be created directly. +Instead, they usually come as a result of a java method call. +} +\section{Slots}{ + \describe{ + \item{\code{jsig}:}{JNI signature of the array type} + \item{\code{jobj}:}{Internal identifier of the object} + \item{\code{jclass}:}{Inherited from \code{jobjRef}, but unspecified} + \item{\code{dimension}:}{dimension vector of the array} + } +} +\section{Extends}{ +Class \code{"\linkS4class{jarrayRef}"}, directly. +Class \code{"\linkS4class{jobjRef}"}, by class "jarrayRef", distance 2. +} +\section{Methods}{ + \describe{ + \item{length}{\code{signature(x = "jrectRef")}: The number of elements in the array. + Note that if the array has more than one dimension, + it gives the number of arrays in the first dimension, and not the total + number of atomic objects in tha array (like R does). This gives what would be + returned by \code{array.length} in java.} + \item{str}{\code{signature(object = "jrectRef")}: ... } + \item{[}{\code{signature(x = "jrectRef")}: R indexing of rectangular java arrays } + \item{dim}{\code{signature(x = "jrectRef")}: extracts the dimensions of the array } + \item{dim<-}{\code{signature(x = "jrectRef")}: sets the dimensions of the array } + \item{unique}{\code{signature(x = "jrectRef")}: unique objects in the array} + \item{duplicated}{\code{signature(x = "jrectRef")}: see \code{\link{duplicated}} } + \item{anyDuplicated}{\code{signature(x = "jrectRef")}: see \code{\link{anyDuplicated}} } + \item{sort}{\code{signature(x = "jrectRef")}: returns a \emph{new} array with elements from x in order } + \item{rev}{\code{signature(x = "jrectRef")}: returns a \emph{new} array with elements from x reversed } + \item{min}{\code{signature(x = "jrectRef")}: the smallest object in the array (in the sense of the Comparable interface) } + \item{max}{\code{signature(x = "jrectRef")}: the biggest object in the array (in the sense of the Comparable interface) } + \item{range}{\code{signature(x = "jrectRef")}: the range of the array (in the sense of the Comparable interface) } + } +} +\examples{ +\dontshow{ +# these examples are only unit tests so far +.jinit() +} +v <- new( J("java.util.Vector") ) +v$add( "hello" ) +v$add( "world" ) +v$add( new( J("java.lang.Double"), "10.2" ) ) +array <- v$toArray() + +array[ c(TRUE,FALSE,TRUE) ] +array[ 1:2 ] +array[ -3 ] + +# length +length( array ) +\dontshow{stopifnot(length(array) == 3L)} + +# also works as a pseudo field as in java +array$length +\dontshow{stopifnot(array$length == 3L)} + + +\dontshow{ +# # 2d +dim2d <- c(5L, 2L) + +x <- .jcall( "RectangularArrayExamples", "[[Z", +"getBooleanDoubleRectangularArrayExample", evalArray = TRUE, simplify = TRUE) +stopifnot( identical( typeof( x ), "logical" ) ) +stopifnot( identical( dim(x) , dim2d ) ) +stopifnot( identical( as.vector(x), rep( c(FALSE,TRUE), 5 ) ) ) + +x <- .jcall( "RectangularArrayExamples", "[[I", +"getIntDoubleRectangularArrayExample", evalArray = TRUE, simplify = TRUE ) +stopifnot( identical( typeof( x ), "integer" ) ) +stopifnot( identical( dim(x) , dim2d ) ) +stopifnot( identical( as.vector(x), 0:9 ) ) + +x <- .jcall( "RectangularArrayExamples", "[[B", +"getByteDoubleRectangularArrayExample", evalArray = TRUE, simplify = TRUE ) +stopifnot( identical( typeof( x ), "raw" ) ) +stopifnot( identical( dim(x) , dim2d ) ) +stopifnot( identical( as.vector(x), as.raw(0:9) ) ) + +x <- .jcall( "RectangularArrayExamples", "[[J", +"getLongDoubleRectangularArrayExample", evalArray = TRUE, simplify = TRUE ) +stopifnot( identical( typeof( x ), "double" ) ) +stopifnot( identical( dim(x) , dim2d ) ) +stopifnot( identical( as.vector(x), as.numeric(0:9) ) ) + +x <- .jcall( "RectangularArrayExamples", "[[S", +"getShortDoubleRectangularArrayExample", evalArray = TRUE, simplify = TRUE ) +stopifnot( identical( typeof( x ), "integer" ) ) +stopifnot( identical( dim(x) , dim2d ) ) +stopifnot( identical( as.vector(x), 0:9 ) ) + +x <- .jcall( "RectangularArrayExamples", "[[D", +"getDoubleDoubleRectangularArrayExample", evalArray = TRUE, simplify = TRUE ) +stopifnot( identical( typeof( x ), "double" ) ) +stopifnot( identical( dim(x) , dim2d ) ) +stopifnot( identical( as.vector(x), as.numeric(0:9) ) ) + +x <- .jcall( "RectangularArrayExamples", "[[C", +"getCharDoubleRectangularArrayExample", evalArray = TRUE, simplify = TRUE ) +stopifnot( identical( typeof( x ), "integer" ) ) +stopifnot( identical( dim(x) , dim2d ) ) +stopifnot( identical( as.vector(x), 0:9 ) ) + +x <- .jcall( "RectangularArrayExamples", "[[F", +"getFloatDoubleRectangularArrayExample", evalArray = TRUE, simplify = TRUE ) +stopifnot( identical( typeof( x ), "double" ) ) +stopifnot( identical( dim(x) , dim2d ) ) +stopifnot( identical( as.vector(x), as.numeric(0:9) ) ) + +x <- .jcall( "RectangularArrayExamples", "[[Ljava/lang/String;", +"getStringDoubleRectangularArrayExample", evalArray = TRUE, simplify = TRUE ) +stopifnot( identical( typeof( x ), "character" ) ) +stopifnot( identical( dim(x) , dim2d ) ) +stopifnot( identical( as.vector(x), as.character(0:9) ) ) + + +# 3d + +dim3d <- c(5L, 3L, 2L) + +x <- .jcall( "RectangularArrayExamples", "[[[Z", +"getBooleanTripleRectangularArrayExample", evalArray = TRUE, simplify = TRUE) +stopifnot( identical( typeof( x ), "logical" ) ) +stopifnot( identical( dim(x) , dim3d ) ) +stopifnot( identical( as.vector(x), rep( c(FALSE,TRUE), 15L ) ) ) + +x <- .jcall( "RectangularArrayExamples", "[[[I", +"getIntTripleRectangularArrayExample", evalArray = TRUE, simplify = TRUE ) +stopifnot( identical( typeof( x ), "integer" ) ) +stopifnot( identical( dim(x) , dim3d ) ) +stopifnot( identical( as.vector(x), 0:29 ) ) + +x <- .jcall( "RectangularArrayExamples", "[[[B", +"getByteTripleRectangularArrayExample", evalArray = TRUE, simplify = TRUE ) +stopifnot( identical( typeof( x ), "raw" ) ) +stopifnot( identical( dim(x) , dim3d ) ) +stopifnot( identical( as.vector(x), as.raw(0:29) ) ) + +x <- .jcall( "RectangularArrayExamples", "[[[J", +"getLongTripleRectangularArrayExample", evalArray = TRUE, simplify = TRUE ) +stopifnot( identical( typeof( x ), "double" ) ) +stopifnot( identical( dim(x) , dim3d ) ) +stopifnot( identical( as.vector(x), as.numeric(0:29) ) ) + +x <- .jcall( "RectangularArrayExamples", "[[[S", +"getShortTripleRectangularArrayExample", evalArray = TRUE, simplify = TRUE ) +stopifnot( identical( typeof( x ), "integer" ) ) +stopifnot( identical( dim(x) , dim3d ) ) +stopifnot( identical( as.vector(x), 0:29 ) ) + +x <- .jcall( "RectangularArrayExamples", "[[[D", +"getDoubleTripleRectangularArrayExample", evalArray = TRUE, simplify = TRUE ) +stopifnot( identical( typeof( x ), "double" ) ) +stopifnot( identical( dim(x) , dim3d ) ) +stopifnot( identical( as.vector(x), as.numeric(0:29) ) ) + +x <- .jcall( "RectangularArrayExamples", "[[[C", +"getCharTripleRectangularArrayExample", evalArray = TRUE, simplify = TRUE ) +stopifnot( identical( typeof( x ), "integer" ) ) +stopifnot( identical( dim(x) , dim3d ) ) +stopifnot( identical( as.vector(x), 0:29 ) ) + +x <- .jcall( "RectangularArrayExamples", "[[[F", +"getFloatTripleRectangularArrayExample", evalArray = TRUE, simplify = TRUE ) +stopifnot( identical( typeof( x ), "double" ) ) +stopifnot( identical( dim(x) , dim3d ) ) +stopifnot( identical( as.vector(x), as.numeric(0:29) ) ) + +x <- .jcall( "RectangularArrayExamples", "[[[Ljava/lang/String;", +"getStringTripleRectangularArrayExample", evalArray = TRUE, simplify = TRUE ) +stopifnot( identical( typeof( x ), "character" ) ) +stopifnot( identical( dim(x) , dim3d ) ) +stopifnot( identical( as.vector(x), as.character(0:29) ) ) + + +# testing the indexing + +xj <- .jarray( x, dispatch = TRUE ) +stopifnot( dim( xj[ ,, ] ) == c( 5L, 3L, 2L ) ) +stopifnot( dim( xj[ ] ) == c( 5L, 3L, 2L ) ) +stopifnot( dim( xj[ ,,1,drop= TRUE] ) == c( 5L, 3L ) ) +stopifnot( dim( xj[ ,,1,drop= FALSE] ) == c( 5L, 3L, 1L ) ) +stopifnot( dim( xj[ ,1,,drop= TRUE] ) == c( 5L, 2L ) ) +stopifnot( dim( xj[ ,1,,drop= FALSE] ) == c( 5L, 1L, 2L ) ) +stopifnot( dim( xj[ 1,,,drop= TRUE] ) == c( 3L, 2L ) ) +stopifnot( dim( xj[ 1,,,drop= FALSE] ) == c( 1L, 3L, 2L ) ) +stopifnot( dim( xj[ ,1,1,drop= TRUE] ) == c( 5L ) ) +stopifnot( dim( xj[ ,1,1,drop= FALSE] ) == c( 5L, 1L, 1L ) ) +stopifnot( dim( xj[ 1,1,1,drop= TRUE] ) == c( 1L ) ) +stopifnot( dim( xj[ 1,1,1,drop= FALSE] ) == c( 1L, 1L, 1L ) ) + +# testing simplify +stopifnot( identical( xj[simplify=TRUE], x) ) +stopifnot( identical( xj[,1,,simplify=TRUE], x[,1,]) ) +stopifnot( identical( xj[,1,-1,simplify=TRUE], x[,1,-1]) ) +stopifnot( identical( xj[4,1,c(TRUE,FALSE),simplify=TRUE], x[4,1,c(TRUE,FALSE)]) ) +stopifnot( identical( xj[1:10,simplify=TRUE], x[1:10]) ) + +# test dim<- +dim( xj ) <- c( 15L, 2L ) +stopifnot( xj@jsig == "[[Ljava/lang/String;" ) +stopifnot( dim( xj ) == c(15L, 2L ) ) + +dim( xj ) <- NULL +stopifnot( xj@jsig == "[Ljava/lang/String;" ) +stopifnot( dim( xj ) == 30L ) + +# test unique +# **** FIXME: this should really work even with dispatch=FALSE since +# it's a vector but it does not! It applies to everything +# below +x <- .jarray( rep( 1:2, each = 5 ), dispatch = TRUE ) +xu <- unique( x ) +stopifnot( dim(xu) == 2L ) + + p1 <- .jnew( "java/awt/Point" ) + p2 <- .jnew( "java/awt/Point" ) + x <- .jarray( list( p1, p2 ), dispatch = TRUE ) + xu <- unique( x ) + stopifnot( dim( xu ) == 1L ) + +# test duplicated +x <- .jarray( rep( 1:2, each = 5 ), dispatch = TRUE ) +xd <- duplicated( x ) +stopifnot( xd == rep( c( FALSE, TRUE, TRUE, TRUE, TRUE), 2L ) ) +if (rJava:::.base.has.anyDuplicated) stopifnot( anyDuplicated( x ) == 2L ) + + p1 <- .jnew( "java/awt/Point" ) + p2 <- .jnew( "java/awt/Point" ) + x <- .jarray( list( p1, p2 ), dispatch = TRUE ) + xd <- duplicated( x ) + stopifnot( xd == c( FALSE, TRUE) ) + if (rJava:::.base.has.anyDuplicated) stopifnot( anyDuplicated( x ) == 2L ) + +# test sort, rev +d1 <- .jnew("java/lang/Double", 0) +d2 <- .jnew("java/lang/Double", -1) +a <- .jarray( list( d1, d2), dispatch = TRUE ) +stopifnot( sort( a )[[1]]$doubleValue() == -1.0 ) +stopifnot( rev( a )[[1]]$doubleValue() == -1.0 ) + +# test min, max, range +Double <- J("java.lang.Double") +a <- .jarray( list( new( Double, 10 ), new( Double, 4), new( Double, 5) +), "java/lang/Double", dispatch = TRUE ) +stopifnot( min( a )$doubleValue() == 4 ) +stopifnot( max( a )$doubleValue() == 10 ) +stopifnot( range(a)[[1]]$doubleValue() == 4 ) +stopifnot( range(a)[[2]]$doubleValue() == 10) + +} +} +\keyword{classes} diff --git a/com.oracle.truffle.r.pkgs/rJava/man/jreflection.Rd b/com.oracle.truffle.r.pkgs/rJava/man/jreflection.Rd new file mode 100644 index 0000000000000000000000000000000000000000..7d3b0600e1c8b15645dc8314798af1f6a545d6e9 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/jreflection.Rd @@ -0,0 +1,51 @@ +\name{jreflection} +\alias{.jmethods} +\alias{.jfields} +\alias{.jconstructors} +\title{ + Simple helper functions for Java reflection +} +\description{ + \code{.jconstructors} returns a character vector with all constructors for + a given class or object. + \code{.jmethods} returns a character vector with all methods for + a given class or object. + \code{.jfields} returns a character vector with all fileds (aka attributes) for a given class or object. +} +\usage{ +.jconstructors(o, as.obj = FALSE) +.jmethods(o, name = NULL, as.obj = FALSE) +.jfields(o, name = NULL, as.obj = FALSE) +} +\arguments{ + \item{o}{Name of a class (either notation is fine) or an object whose + class will be queried} + \item{name}{Name of the method/field to look for. May contain regular + expressions except for \code{^$}.} + \item{as.obj}{if \code{TRUE} then a list of Java objects is + returned, otherwise a character vector (obtained by calling + \code{toString()} on each entry).} +} +\value{ + Returns a character vector (if \code{as.obj} is \code{FALSE}) or a + list of Java objects. Each entry corresponds to the + \code{Constructor} resp. \code{Method} resp. \code{Field} object. +} +\details{ + There first two functions are intended to help with finding correct + signatures for methods and constructors. Since the low-level API in + rJava doesn't use reflection automatically, it is necessary to + provide a proper signature. That is somewhat easier using the above + methods. +} +\seealso{ + \code{\link{.jcall}}, \code{\link{.jnew}}, \code{\link{.jcast}} or \code{\link{$,jobjRef-method}} +} +\examples{ +\dontrun{ +.jconstructors("java/util/Vector") +v <- .jnew("java/util/Vector") +.jmethods(v, "add") +} +} +\keyword{interface} diff --git a/com.oracle.truffle.r.pkgs/rJava/man/jserialize.Rd b/com.oracle.truffle.r.pkgs/rJava/man/jserialize.Rd new file mode 100644 index 0000000000000000000000000000000000000000..29383d5784bf287dbdd6adb3de0ee81790d8f32d --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/jserialize.Rd @@ -0,0 +1,107 @@ +\name{jserialize} +\alias{.jserialize} +\alias{.junserialize} +\alias{.jcache} +\title{ + Java object serialization +} +\description{ + \code{.jserialize} serializes a Java object into raw vector using + Java serialization. + + \code{.junserialize} re-constructs a Java object from its serialized + (raw-vector) form. + + \code{.jcache} updates, retrieves or removes R-side object cache + which can be used for persistent storage of Java objects across + sessions. +} +\usage{ +.jserialize(o) +.junserialize(data) +.jcache(o, update=TRUE) +} +\arguments{ + \item{o}{Java object} + \item{data}{serialized Java object as a raw vector} + \item{update}{must be \code{TRUE} (cache is updated), \code{FALSE} + (cache is retrieved) or \code{NULL} (cache is deleted).} +} +\value{ + \code{.jserialize} returns a raw vector + + \code{.junserialize} returns a Java object or \code{NULL} if an error + occurred (currently you may use \code{.jcheck()} to further + investigate the error) + + \code{.jcache} returns the current cache (usually a raw vector) or + \code{NULL} if there is no cache. +} +\details{ + Not all Java objects support serialization, see Java documentation + for details. Note that Java serialization and serialization of R + objects are two entirely different mechanisms that cannot be + interchanged. \code{.jserialize} and \code{.junserialize} can + be used to access Java serialization facilities. + + \code{.jcache} manipulates the R-side Java object cache associated + with a given Java reference: + + Java objects do not persist across sessions, because the Java + Virtual Machine (JVM) is destroyed when R is closed. All saved Java + object references will be restored as \code{null} references, since + the corresponding objects no longer exist (see R documentation on + serialization). However, it is possible to serialize a Java object + (if supported by the object) and store its serialized form in + R. This allows for the object to be deserialized when loaded into + another active session (but see notes below!) + + R-side cache consists of a serialized form of the object as raw + vector. This cache is attached to the Java object and thus will be + saved when the Java object is saved. rJava provides an automated way + of deserializing Java references if they are \code{null} references + and have a cache attached. This is done on-demand basis whenever a + reference to a Java object is required. + + Therefore packages can use \code{.jcache} to provide a way of + creating Java references that persist across sessions. However, they + must be very cautious in doing so. First, make sure the serialized + form is not too big. Storing whole datasets in Java serialized form + will hog immense amounts of memory on the R side and should be + avoided. In addition, be aware that the cache is just a snapshot, it + doesn't change when the referenced Java object is modified. Hence it + is most useful only for references that are not modified outside + R. Finally, internal references to other Java objects accessible + from R are not retained (see below). Most common use of + \code{.jcache} is with Java references that point to definitions of + methods (e.g., models) and other descriptive objects which are then + used by other, active Java classes to act upon. Caching of such + active objects is not a good idea, they should be instantiated by + functions that operate on the descriptive references instead. + + \emph{Important note:} the serialization of Java references does NOT + take into account any dependencies on the R side. Therefore if you + hold a reference to a Java object in R that is also referenced by + the serialized Java object on the Java side, then this relationship + cannot be retained upon restore. Instead, two copies of disjoint + objects will be created which can cause confusion and errorneous + behavior. + + The cache is attached to the reference external pointer and thus it + is shared with all copies of the same reference (even when changed + via \code{\link{.jcast}} etc.), but it is independent of other + references to the object obtained separately + (e.g., via \code{\link{.jcall}} or \code{\link{.jfield}}). + + Also note that deserialization (even automated one) requires a + running virtual machine. Therefore you must make sure that either + \code{\link{.jinit}} or \code{\link{.jpackage}} is used before any + Java references are accessed. +} +%\seealso{ +%} +%\examples{ +%\dontrun{ +%} +%} +\keyword{interface} diff --git a/com.oracle.truffle.r.pkgs/rJava/man/jsimplify.Rd b/com.oracle.truffle.r.pkgs/rJava/man/jsimplify.Rd new file mode 100644 index 0000000000000000000000000000000000000000..59a8ed04624939d78d83db84ffd0566dde291749 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/jsimplify.Rd @@ -0,0 +1,49 @@ +\name{jsimplify} +\alias{.jsimplify} +\title{ + Converts Java object to a simple scalar if possible +} +\description{ + \code{.jsimplify} attempts to convert Java objects that represent + simple scalars into corresponding scalar representation in R. +} +\usage{ +.jsimplify(o, promote=FALSE) +} +\arguments{ + \item{o}{arbitrary object} + \item{promote}{logical, if \code{TRUE} then an ambiguous conversion + where the native type value would map to \code{NA} (e.g., Java + \code{int} type with value -2147483648) will be taken + to represent an actual value and will be promoted to a larger type + that can represent the value (in case of \code{int} promoted to + \code{double}). If \code{FALSE} then such values are assumed to + represent \code{NA}s.} +} +\value{ + Simple scalar or \code{o} unchanged. +} +\details{ + If \code{o} is not a Java object reference, \code{o} is returned + as-is. If \code{o} is a reference to a scalar object (such as single + integer, number, string or boolean) then the value of that object is + returned as R vector of the corresponding type and length one. + + This function is used by \code{\link{.jfield}} to simplify the results + of field access if required. + + Currently there is no function inverse to this, the usual way to wrap + scalar values in Java references is to use \code{\link{.jnew}} as the + corresponding constructor. +} +\seealso{ + \code{\link{.jfield}} +} +\examples{ +\dontrun{ +i <- .jnew("java/lang/Integer", as.integer(10)) +print(i) +print(.jsimplify(i)) +} +} +\keyword{interface} diff --git a/com.oracle.truffle.r.pkgs/rJava/man/loader.Rd b/com.oracle.truffle.r.pkgs/rJava/man/loader.Rd new file mode 100644 index 0000000000000000000000000000000000000000..f7a8e4afebf1c8ac0bb74894bb5fd292e67ed43e --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/loader.Rd @@ -0,0 +1,37 @@ +\name{loader} +\alias{.jaddClassPath} +\alias{.jclassPath} +\title{ + Java class loader +} +\description{ + \code{.jaddClassPath} adds directories or JAR files to the class + path. + + \code{.jclassPath} returns a vector containg the current entries in + the class path +} +\usage{ +.jaddClassPath(path) +.jclassPath() +} +\arguments{ + \item{path}{character string vector listing the paths to add to the + class path} +} +\value{ + \code{.jclassPath} returns a character vector listing the class path sequence. +} +%\details{ +% +%} +%\seealso{ +% \code{\link{.jpackage}} +%} +\examples{ +\dontrun{ +.jaddClassPath("/my/jars/foo.jar","/my/classes/") +print(.jclassPath()) +} +} +\keyword{interface} diff --git a/com.oracle.truffle.r.pkgs/rJava/man/new.Rd b/com.oracle.truffle.r.pkgs/rJava/man/new.Rd new file mode 100644 index 0000000000000000000000000000000000000000..8d76bfbb87c99cfca54a4425ba1a0c926c1928d2 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/new.Rd @@ -0,0 +1,33 @@ +\name{new} +\alias{new,jclassName-method} +\title{ + Create a new Java object +} +\description{ + Creates a new Java object and invokes the constructor with given arguments. +} +\section{Methods}{ + \describe{ + \item{\code{new}}{\code{signature(Class = "jclassName")}: ... } + } +} +\details{ + The \code{new} method is used as the high-level API to create new + Java objects (for low-level access see \code{\link{.jnew}}). It + returns the newly created Java object. + + \code{...} arguments are passed to the constructor of the class + specified as \code{J("class.name")}. +} +\seealso{ + \code{\link{.jnew}}, \code{\link{jclassName-class}} +} +\examples{ +\dontrun{ +v <- new(J("java.lang.String"), "Hello World!") +v$length() +v$indexOf("World") +names(v) +} +} +\keyword{interface} diff --git a/com.oracle.truffle.r.pkgs/rJava/man/rep.Rd b/com.oracle.truffle.r.pkgs/rJava/man/rep.Rd new file mode 100644 index 0000000000000000000000000000000000000000..afd95df3f6f6331770018af57e31c3938860d1a6 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/rep.Rd @@ -0,0 +1,32 @@ +\name{rep} +\alias{rep,jarrayRef-method} +\alias{rep,jobjRef-method} +\alias{rep,jrectRef-method} + +\title{Creates java arrays by cloning} +\description{ + Creates a java array by cloning a reference several times +} +\section{Methods}{ + \describe{ + \item{rep}{\code{signature(object = "jobjRef")}: ... } + \item{rep}{\code{signature(object = "jarrayRef")}: ... } + \item{rep}{\code{signature(object = "jrectRef")}: ... } + } +} + +\seealso{ + \code{\link[base]{rep}} or \code{\link{.jarray}} +} + +\examples{ +\dontshow{.jinit()} +if (!nzchar(Sys.getenv("NOAWT"))) { + p <- .jnew( "java.awt.Point" ) + a <- rep( p, 10 ) + + stopifnot( dim(a) == c(10L ) ) + a[[1]]$move( 10L, 50L ) + stopifnot( a[[2]]$getX() == 0.0 ) +} +} diff --git a/com.oracle.truffle.r.pkgs/rJava/man/show.Rd b/com.oracle.truffle.r.pkgs/rJava/man/show.Rd new file mode 100644 index 0000000000000000000000000000000000000000..4e0c5ab3607c4c31a568dc8e4c9bcdd28bdaf3ba --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/show.Rd @@ -0,0 +1,21 @@ +\name{show} +\alias{show,jobjRef-method} +\alias{str,jobjRef-method} +\alias{show,jarrayRef-method} +\alias{show,jclassName-method} +\title{Show a Java Object Reference} +\description{ + Display a Java object reference in a descriptive, textual form. The + default implementation calls \code{toString} Java method to obtain + object's printable value and uses calls \code{show} on the resulting + string garnished with additional details. +} +\section{Methods}{ + \describe{ + \item{show}{\code{signature(object = "jobjRef")}: ... } + \item{show}{\code{signature(object = "jarrayRef")}: ... } + \item{show}{\code{signature(object = "jclassName")}: ... } + \item{str}{\code{signature(object = "jobjRef")}: currently identical to show } + } +} +\keyword{interface} diff --git a/com.oracle.truffle.r.pkgs/rJava/man/with.Rd b/com.oracle.truffle.r.pkgs/rJava/man/with.Rd new file mode 100644 index 0000000000000000000000000000000000000000..86c5e301d585d8a99dc80f28961de14c38563366 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/man/with.Rd @@ -0,0 +1,106 @@ +\name{with.jobjRef} +\alias{with.jobjRef} +\alias{within.jobjRef} +\alias{with.jarrayRef} +\alias{within.jarrayRef} +\alias{with.jclassName} +\alias{within.jclassName} +\title{ +with and within methods for Java objects and class names +} +\description{ +Convenience wrapper that allow calling methods of +Java object and classes from within the object (or class). +} +\usage{ +\S3method{with}{jobjRef}(data, expr, ...) +\S3method{within}{jobjRef}(data, expr, ...) + +\S3method{with}{jarrayRef}(data, expr, ...) +\S3method{within}{jarrayRef}(data, expr, ...) + +\S3method{with}{jclassName}(data, expr, ...) +\S3method{within}{jclassName}(data, expr, ...) +} +\arguments{ + \item{data}{ + A Java object reference or a java class name. See \code{\link{J}} +} + \item{expr}{ +R expression to evaluate +} + \item{\dots}{ +ignored +} +} +\details{ +The expression is evaluated in an environment +that contains a mapping between the public fields +and methods of the object. + +The methods of the object are mapped to standard R functions +in the environment. In case of classes, only static methods +are used. + +The fields of the object are mapped to active bindings +(see \link{makeActiveBinding}) so that they can be accessed +and modified from within the environment. For classes, only +static fields are used. +} +\value{ + \code{with} returns the value of the expression and + \code{within} returns the \code{data} argument +} +\author{ +Romain Francois <francoisromain@free.fr> +} +\references{ + the \code{java.lang.reflect} package: + \url{http://java.sun.com/j2se/1.5.0/docs/api/java/lang/reflect/package-summary.html} +} +\examples{ +\dontshow{.jinit()} + +if (!nzchar(Sys.getenv("NOAWT"))) { + p <- .jnew( "java/awt/Point", 0L, 0L ) + with( p, { + # x and y and now 0 + move( 10L, 10L ) + # x and y are now 10 + x <- x + y + } ) + + f <- within( .jnew( "javax/swing/JFrame" ) , { + layout <- .jnew( "java/awt/BorderLayout" ) + setLayout( layout ) + add( .jnew( "javax/swing/JLabel", "north" ), layout$NORTH ) + add( .jnew( "javax/swing/JLabel", "south" ), layout$SOUTH ) + add( .jnew( "javax/swing/JLabel", "west" ), layout$WEST ) + add( .jnew( "javax/swing/JLabel", "east" ), layout$EAST ) + setSize( .jnew( "java/awt/Dimension", 400L, 400L ) ) + setVisible( TRUE ) + } ) +} + +Double <- J("java.lang.Double") +with( Double, MIN_VALUE ) +with( Double, parseDouble( "10.2" ) ) + +\dontrun{ +# inner class example +% TODO: find a better example +HashMap <- J("java.util.HashMap") +with( HashMap, new( SimpleEntry, "key", "value" ) ) +with( HashMap, SimpleEntry ) +} + +with( J("java.lang.System"), getProperty("java.home") ) + +\dontshow{ +stopifnot( with( Double, parseDouble("10.0") ) == 10.0 ) +d <- new( Double, "10.0") +stopifnot( with( d, doubleValue() ) == 10.0 ) +} + +} +\keyword{ classes } diff --git a/com.oracle.truffle.r.pkgs/rJava/src/Makevars b/com.oracle.truffle.r.pkgs/rJava/src/Makevars new file mode 100644 index 0000000000000000000000000000000000000000..238e93d69d420cb6688da8529b34dc0ec0f53409 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/src/Makevars @@ -0,0 +1,14 @@ +JAVA_SRC=$(wildcard java/*.java) +JFLAGS=-source 1.6 -target 1.6 +JAVAC=javac + +all: $(SHLIB) +$(SHLIB): java + +.PHONY: all java + +java: $(JAVA_SRC) + $(JAVAC) $(JFLAGS) $(JAVA_SRC) + rm -rfv ../inst/java + mkdir -p ../inst/java + mv java/*.class ../inst/java diff --git a/com.oracle.truffle.r.pkgs/rJava/src/java/ArrayDimensionException.java b/com.oracle.truffle.r.pkgs/rJava/src/java/ArrayDimensionException.java new file mode 100644 index 0000000000000000000000000000000000000000..56f86de3874fd5551b775db020de677016ffefa8 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/src/java/ArrayDimensionException.java @@ -0,0 +1,15 @@ +/* + * This material is distributed under the GNU General Public License + * Version 2. You may review the terms of this license at + * http://www.gnu.org/licenses/gpl-2.0.html + * + * Copyright (c) 2006, Simon Urbanek + * Copyright (c) 2018, Oracle and/or its affiliates + * + * All rights reserved. + */ +public class ArrayDimensionException extends Exception{ + public ArrayDimensionException(String message){ + super( message ) ; + } +} diff --git a/com.oracle.truffle.r.pkgs/rJava/src/java/ArrayWrapper.java b/com.oracle.truffle.r.pkgs/rJava/src/java/ArrayWrapper.java new file mode 100644 index 0000000000000000000000000000000000000000..388aadc5c14e948c12822938be8aa275c61050c5 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/src/java/ArrayWrapper.java @@ -0,0 +1,386 @@ +/* + * This material is distributed under the GNU General Public License + * Version 2. You may review the terms of this license at + * http://www.gnu.org/licenses/gpl-2.0.html + * + * Copyright (c) 2006, Simon Urbanek + * Copyright (c) 2018, Oracle and/or its affiliates + * + * All rights reserved. + */ +// :tabSize=2:indentSize=2:noTabs=false:folding=explicit:collapseFolds=1: + +import java.lang.reflect.Array ; + +/** + * Utility class to deal with arrays + */ +public class ArrayWrapper extends RJavaArrayIterator { + + /** + * is this array rectangular + */ + private boolean isRect ; + + /** + * The type name of the objects stored + */ + private String typeName ; + + /** + * true if the array stores primitive types + */ + private boolean primitive ; + + private int length ; + + /** + * Constructor + * + * @param array the array to check + * @throws NotAnArrayException if array is not an array + */ + public ArrayWrapper(Object array) throws NotAnArrayException { + super( RJavaArrayTools.getDimensions(array) ); + this.array = array ; + typeName = RJavaArrayTools.getObjectTypeName(array ); + primitive = RJavaArrayTools.isPrimitiveTypeName( typeName ) ; + if( dimensions.length == 1){ + isRect = true ; + } else{ + isRect = isRectangular_( array, 0 ); + } + // reset the dimensions if the array is not rectangular + if( !isRect ){ + dimensions = null ; + length = -1; + } else{ + length = 1; + for( int i=0; i<dimensions.length; i++) { + length *= dimensions[i] ; + } + } + } + + // making java < 1.5 happy + public ArrayWrapper(int x) throws NotAnArrayException { throw new NotAnArrayException("primitive type") ; } + public ArrayWrapper(boolean x) throws NotAnArrayException { throw new NotAnArrayException("primitive type") ; } + public ArrayWrapper(byte x) throws NotAnArrayException { throw new NotAnArrayException("primitive type") ; } + public ArrayWrapper(long x) throws NotAnArrayException { throw new NotAnArrayException("primitive type") ; } + public ArrayWrapper(short x) throws NotAnArrayException { throw new NotAnArrayException("primitive type") ; } + public ArrayWrapper(double x) throws NotAnArrayException { throw new NotAnArrayException("primitive type") ; } + public ArrayWrapper(char x) throws NotAnArrayException { throw new NotAnArrayException("primitive type") ; } + public ArrayWrapper(float x) throws NotAnArrayException { throw new NotAnArrayException("primitive type") ; } + + + /** + * @return true if the array is rectangular + */ + public boolean isRectangular( ){ + return isRect ; + } + + /** + * Recursively check all dimensions to see if an array is rectangular + */ + private boolean isRectangular_(Object o, int depth){ + if( depth == dimensions.length ) return true ; + int n = Array.getLength(o) ; + if( n != dimensions[depth] ) return false ; + for( int i=0; i<n; i++){ + if( !isRectangular_(Array.get(o, i), depth+1) ){ + return false; + } + } + return true ; + } + + /** + * @return the type name of the objects stored in the wrapped array + */ + public String getObjectTypeName(){ + return typeName; + } + + /** + * @return true if the array contains java primitive types + */ + public boolean isPrimitive(){ + return primitive ; + } + + // {{{ flat_* methods + + // {{{ flat_int + /** + * Flattens the array into a single dimensionned int array + */ + public int[] flat_int() throws PrimitiveArrayException,FlatException { + + if( ! "I".equals(typeName) ) throw new PrimitiveArrayException("int"); + if( !isRect ) throw new FlatException(); + if( dimensions.length == 1 ){ + return (int[])array ; + } else{ + int[] payload = new int[length] ; + + int k; + while( hasNext() ){ + int[] current = (int[])next() ; + k = start ; + for( int j=0; j<current.length; j++, k+=increment){ + payload[k] = current[j] ; + } + } + return payload ; + } + } + // }}} + + // {{{ flat_boolean + /** + * Flattens the array into a single dimensionned boolean array + * + */ + public boolean[] flat_boolean() throws PrimitiveArrayException,FlatException { + + if( ! "Z".equals(typeName) ) throw new PrimitiveArrayException("boolean"); + if( !isRect ) throw new FlatException(); + if( dimensions.length == 1 ){ + return (boolean[])array ; + } else{ + boolean[] payload = new boolean[length] ; + + int k; + while( hasNext() ){ + boolean[] current = (boolean[])next() ; + k = start ; + for( int j=0; j<current.length; j++, k+=increment){ + payload[k] = current[j] ; + } + } + return payload ; + } + } + // }}} + + // {{{ flat_byte + /** + * Flattens the array into a single dimensionned byte array + * + */ + public byte[] flat_byte() throws PrimitiveArrayException,FlatException { + + if( ! "B".equals(typeName) ) throw new PrimitiveArrayException("byte"); + if( !isRect ) throw new FlatException(); + if( dimensions.length == 1 ){ + return (byte[])array ; + } else{ + byte[] payload = new byte[length] ; + int k; + while( hasNext() ){ + byte[] current = (byte[])next() ; + k = start ; + for( int j=0; j<current.length; j++, k+=increment){ + payload[k] = current[j] ; + } + } + return payload ; + } + } + + // }}} + + // {{{ flat_long + /** + * Flattens the array into a single dimensionned long array + * + */ + public long[] flat_long() throws PrimitiveArrayException,FlatException { + + if( ! "J".equals(typeName) ) throw new PrimitiveArrayException("long"); + if( !isRect ) throw new FlatException(); + if( dimensions.length == 1 ){ + return (long[])array ; + } else{ + long[] payload = new long[length] ; + int k; + while( hasNext() ){ + long[] current = (long[])next() ; + k = start ; + for( int j=0; j<current.length; j++, k+=increment){ + payload[k] = current[j] ; + } + } + return payload ; + } + } + + // }}} + + // {{{ flat_short + /** + * Flattens the array into a single dimensionned short array + * + */ + public short[] flat_short() throws PrimitiveArrayException,FlatException { + + if( ! "S".equals(typeName) ) throw new PrimitiveArrayException("short"); + if( !isRect ) throw new FlatException(); + if( dimensions.length == 1 ){ + return (short[])array ; + } else{ + short[] payload = new short[length] ; + int k; + while( hasNext() ){ + short[] current = (short[])next() ; + k = start ; + for( int j=0; j<current.length; j++, k+=increment){ + payload[k] = current[j] ; + } + } + return payload ; + } + } +// }}} + + // {{{ flat_double + /** + * Flattens the array into a single dimensionned double array + * + */ + public double[] flat_double() throws PrimitiveArrayException,FlatException { + + if( ! "D".equals(typeName) ) throw new PrimitiveArrayException("double"); + if( !isRect ) throw new FlatException(); + if( dimensions.length == 1 ){ + return (double[])array ; + } else{ + double[] payload= new double[length] ; + int k; + while( hasNext() ){ + double[] current = (double[])next() ; + k = start ; + for( int j=0; j<current.length; j++, k+=increment){ + payload[k] = current[j] ; + } + } + return payload ; + } + } + + // }}} + + // {{{ flat_char + /** + * Flattens the array into a single dimensionned double array + * + */ + public char[] flat_char() throws PrimitiveArrayException,FlatException { + + if( ! "C".equals(typeName) ) throw new PrimitiveArrayException("char"); + if( !isRect ) throw new FlatException(); + if( dimensions.length == 1 ){ + return (char[])array ; + } else{ + char[] payload = new char[length] ; + int k; + while( hasNext() ){ + char[] current = (char[])next() ; + k = start ; + for( int j=0; j<current.length; j++, k+=increment){ + payload[k] = current[j] ; + } + } + return payload ; + } + } + + // }}} + + // {{{ flat_float + /** + * Flattens the array into a single dimensionned float array + * + */ + public float[] flat_float() throws PrimitiveArrayException,FlatException { + + if( ! "F".equals(typeName) ) throw new PrimitiveArrayException("float"); + if( !isRect ) throw new FlatException(); + if( dimensions.length == 1 ){ + return (float[])array ; + } else{ + float[] payload = new float[length] ; + int k; + while( hasNext() ){ + float[] current = (float[])next() ; + k = start ; + for( int j=0; j<current.length; j++, k+=increment){ + payload[k] = current[j] ; + } + } + return payload ; + } + } + + // }}} + + // {{{ flat_Object + public Object[] flat_Object() throws FlatException, ObjectArrayException { + if( isPrimitive() ) throw new ObjectArrayException( typeName) ; + if( !isRect ) throw new FlatException(); + if( dimensions.length == 1 ){ + return (Object[])array ; + } else{ + ClassLoader loader = array.getClass().getClassLoader() ; + Class type = Object.class; + try{ + type = Class.forName( typeName, true, array.getClass().getClassLoader() ); + } catch( ClassNotFoundException e){} + + Object[] payload = (Object[])Array.newInstance( type, length ) ; + int k; + while( hasNext() ){ + Object[] current = (Object[])next() ; + k = start ; + for( int j=0; j<current.length; j++, k+=increment){ + payload[k] = type.cast( current[j] ); + } + } + return payload ; + } + } + // }}} + + // {{{ flat_String + /** + * Flattens the array into a single dimensionned String array + * + */ + // this is technically not required as this can be handled + // by flat_Object but this is slightly more efficient so ... + public String[] flat_String() throws PrimitiveArrayException,FlatException { + + if( ! "java.lang.String".equals(typeName) ) throw new PrimitiveArrayException("java.lang.String"); + if( !isRect ) throw new FlatException(); + if( dimensions.length == 1 ){ + return (String[])array ; + } else{ + String[] payload = new String[length] ; + int k; + while( hasNext() ){ + String[] current = (String[])next() ; + k = start ; + for( int j=0; j<current.length; j++, k+=increment){ + payload[k] = current[j] ; + } + } + return payload ; + } + } + // }}} + + // }}} + +} + diff --git a/com.oracle.truffle.r.pkgs/rJava/src/java/DummyPoint.java b/com.oracle.truffle.r.pkgs/rJava/src/java/DummyPoint.java new file mode 100644 index 0000000000000000000000000000000000000000..ec0f600b7e297d65d7c4fb7bc3479e56a789ae18 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/src/java/DummyPoint.java @@ -0,0 +1,30 @@ +/* + * This material is distributed under the GNU General Public License + * Version 2. You may review the terms of this license at + * http://www.gnu.org/licenses/gpl-2.0.html + * + * Copyright (c) 2006, Simon Urbanek + * Copyright (c) 2018, Oracle and/or its affiliates + * + * All rights reserved. + */ + +public class DummyPoint implements Cloneable { + public int x; + public int y ; + public DummyPoint(){ + this( 0, 0 ) ; + } + public DummyPoint( int x, int y){ + this.x = x ; + this.y = y ; + } + public double getX(){ + return (double)x ; + } + public void move(int x, int y){ + this.x += x ; + this.y += y ; + } + +} diff --git a/com.oracle.truffle.r.pkgs/rJava/src/java/FlatException.java b/com.oracle.truffle.r.pkgs/rJava/src/java/FlatException.java new file mode 100644 index 0000000000000000000000000000000000000000..91ce8a08f77268c43789b43a3fec6fd28e39ca26 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/src/java/FlatException.java @@ -0,0 +1,19 @@ +/* + * This material is distributed under the GNU General Public License + * Version 2. You may review the terms of this license at + * http://www.gnu.org/licenses/gpl-2.0.html + * + * Copyright (c) 2006, Simon Urbanek + * Copyright (c) 2018, Oracle and/or its affiliates + * + * All rights reserved. + */ + +/** + * Generated when one attemps to flatten an array that is not rectangular + */ +public class FlatException extends Exception{ + public FlatException(){ + super( "Can only flatten rectangular arrays" ); + } +} diff --git a/com.oracle.truffle.r.pkgs/rJava/src/java/Makefile b/com.oracle.truffle.r.pkgs/rJava/src/java/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..a476c847334dd6a103e70c02d9938cdc50546615 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/src/java/Makefile @@ -0,0 +1,45 @@ +## + # This material is distributed under the GNU General Public License + # Version 2. You may review the terms of this license at + # http://www.gnu.org/licenses/gpl-2.0.html + # + # Copyright (c) 2006, Simon Urbanek + # Copyright (c) 2018, Oracle and/or its affiliates + # + # All rights reserved. +## + +JAVA_SRC=$(wildcard *.java) +JFLAGS=-source 1.6 -target 1.6 +JAVAC=javac +JAVA=java +JAVADOC=javadoc +JAVADOCFLAGS=-author -version -breakiterator -link http://java.sun.com/j2se/1.4.2/docs/api + +all: compile test + +compile: $(JAVA_SRC) + $(JAVAC) $(JFLAGS) $(JAVA_SRC) + +test_RJavaTools: compile + $(JAVA) RJavaTools_Test + +test_RJavaArrayTools: compile + $(JAVA) RJavaArrayTools_Test + +test_ArrayWrapper: + $(JAVA) ArrayWrapper_Test + +test_RectangularArrayBuilder: + $(JAVA) RectangularArrayBuilder_Test + +test: compile test_RJavaTools test_RJavaArrayTools test_ArrayWrapper test_RectangularArrayBuilder + +javadoc: + $(JAVADOC) $(JAVADOCFLAGS) -d javadoc $(JAVA_SRC) + +clean: + rm -rfv *.class *~ + +.PHONY: all clean + diff --git a/com.oracle.truffle.r.pkgs/rJava/src/java/NotAnArrayException.java b/com.oracle.truffle.r.pkgs/rJava/src/java/NotAnArrayException.java new file mode 100644 index 0000000000000000000000000000000000000000..7f9cf8f23f233c9eea9533c92f7dec2099c66e85 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/src/java/NotAnArrayException.java @@ -0,0 +1,23 @@ +/* + * This material is distributed under the GNU General Public License + * Version 2. You may review the terms of this license at + * http://www.gnu.org/licenses/gpl-2.0.html + * + * Copyright (c) 2006, Simon Urbanek + * Copyright (c) 2018, Oracle and/or its affiliates + * + * All rights reserved. + */ + +/** + * Exception indicating that an object is not a java array + */ +public class NotAnArrayException extends Exception{ + public NotAnArrayException(Class clazz){ + super( "not an array : " + clazz.getName() ) ; + } + public NotAnArrayException(String message){ + super( message ) ; + } +} + diff --git a/com.oracle.truffle.r.pkgs/rJava/src/java/NotComparableException.java b/com.oracle.truffle.r.pkgs/rJava/src/java/NotComparableException.java new file mode 100644 index 0000000000000000000000000000000000000000..73eac55972345735d33d9627354279949fc59217 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/src/java/NotComparableException.java @@ -0,0 +1,35 @@ +/* + * This material is distributed under the GNU General Public License + * Version 2. You may review the terms of this license at + * http://www.gnu.org/licenses/gpl-2.0.html + * + * Copyright (c) 2006, Simon Urbanek + * Copyright (c) 2018, Oracle and/or its affiliates + * + * All rights reserved. + */ + +/** + * Exception generated when two objects cannot be compared + * + * Such cases happen when an object does not implement the Comparable + * interface or when the comparison produces a ClassCastException + */ +public class NotComparableException extends Exception{ + public NotComparableException(Object a, Object b){ + super( "objects of class " + a.getClass().getName() + + " and " + b.getClass().getName() + " are not comparable" ) ; + } + public NotComparableException( Object o){ + this( o.getClass().getName() ) ; + } + + public NotComparableException( Class cl){ + this( cl.getName() ) ; + } + + public NotComparableException( String type ){ + super( "class " + type + " does not implement java.util.Comparable" ) ; + } + +} diff --git a/com.oracle.truffle.r.pkgs/rJava/src/java/ObjectArrayException.java b/com.oracle.truffle.r.pkgs/rJava/src/java/ObjectArrayException.java new file mode 100644 index 0000000000000000000000000000000000000000..9333fd07470dee580940dc77c2cf884fb66d4858 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/src/java/ObjectArrayException.java @@ -0,0 +1,21 @@ +/* + * This material is distributed under the GNU General Public License + * Version 2. You may review the terms of this license at + * http://www.gnu.org/licenses/gpl-2.0.html + * + * Copyright (c) 2006, Simon Urbanek + * Copyright (c) 2018, Oracle and/or its affiliates + * + * All rights reserved. + */ + +/** + * Generated when one tries to access an array of primitive + * values as an array of Objects + */ +public class ObjectArrayException extends Exception{ + public ObjectArrayException(String type){ + super( "array is of primitive type : " + type ) ; + } + +} diff --git a/com.oracle.truffle.r.pkgs/rJava/src/java/PrimitiveArrayException.java b/com.oracle.truffle.r.pkgs/rJava/src/java/PrimitiveArrayException.java new file mode 100644 index 0000000000000000000000000000000000000000..a4f60fcdf572ea584d5a84fd8b977279986134a7 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/src/java/PrimitiveArrayException.java @@ -0,0 +1,21 @@ +/* + * This material is distributed under the GNU General Public License + * Version 2. You may review the terms of this license at + * http://www.gnu.org/licenses/gpl-2.0.html + * + * Copyright (c) 2006, Simon Urbanek + * Copyright (c) 2018, Oracle and/or its affiliates + * + * All rights reserved. + */ + +/** + * Generated when one tries to convert an arrays into + * a primitive array of the wrong type + */ +public class PrimitiveArrayException extends Exception{ + public PrimitiveArrayException(String type){ + super( "cannot convert to single dimension array of primitive type" + type ) ; + } + +} diff --git a/com.oracle.truffle.r.pkgs/rJava/src/java/RJavaArrayIterator.java b/com.oracle.truffle.r.pkgs/rJava/src/java/RJavaArrayIterator.java new file mode 100644 index 0000000000000000000000000000000000000000..0b4dfd1e7254dae4fa72d203301bff14d5daf948 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/src/java/RJavaArrayIterator.java @@ -0,0 +1,96 @@ +/* + * This material is distributed under the GNU General Public License + * Version 2. You may review the terms of this license at + * http://www.gnu.org/licenses/gpl-2.0.html + * + * Copyright (c) 2006, Simon Urbanek + * Copyright (c) 2018, Oracle and/or its affiliates + * + * All rights reserved. + */ +import java.lang.reflect.Array ; + +public abstract class RJavaArrayIterator { + + protected int[] dimensions; + protected int nd ; + protected int[] index ; + protected int[] dimprod ; + protected Object array ; + protected int increment; + protected int position ; + protected int start ; + + public Object getArray(){ + return array ; + } + + /** + * @return the class name of the array + */ + public String getArrayClassName(){ + return array.getClass().getName(); + } + + public int[] getDimensions(){ + return dimensions; + } + + public RJavaArrayIterator(){ + dimensions = null; + index = null ; + dimprod = null ; + array = null ; + } + + public RJavaArrayIterator(int[] dimensions){ + this.dimensions = dimensions ; + nd = dimensions.length ; + if( nd > 1){ + index = new int[ nd-1 ] ; + dimprod = new int[ nd-1 ] ; + for( int i=0; i<(nd-1); i++){ + index[i] = 0 ; + dimprod[i] = (i==0) ? dimensions[i] : ( dimensions[i]*dimprod[i-1] ); + increment = dimprod[i] ; + } + } + position = 0 ; + start = 0; + } + public RJavaArrayIterator(int d1){ + this( new int[]{ d1} ) ; + } + + protected Object next( ){ + + /* get the next array and the position of the first elemtn in the flat array */ + Object o = array ; + for( int i=0; i<index.length; i++){ + o = Array.get( o, index[i] ) ; + if( i == 0 ) { + start = index[i]; + } else { + start += index[i] * dimprod[i-1] ; + } + } + + /* increment the index */ + for( int i=index.length-1; i>=0; i--){ + if( (index[i] + 1) == dimensions[i] ){ + index[i] = 0 ; + } else{ + index[i] = index[i] + 1 ; + } + } + + position++ ; + return o; + } + + protected boolean hasNext( ){ + return position < increment ; + } + + +} diff --git a/com.oracle.truffle.r.pkgs/rJava/src/java/RJavaArrayTools.java b/com.oracle.truffle.r.pkgs/rJava/src/java/RJavaArrayTools.java new file mode 100644 index 0000000000000000000000000000000000000000..87658c8a8094d1c3b697d30a4111d6205949974a --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/src/java/RJavaArrayTools.java @@ -0,0 +1,738 @@ +/* + * This material is distributed under the GNU General Public License + * Version 2. You may review the terms of this license at + * http://www.gnu.org/licenses/gpl-2.0.html + * + * Copyright (c) 2009-2010, Simon Urbanek and Romain Francois + * Copyright (c) 2018, Oracle and/or its affiliates + * + * All rights reserved. + */ + +import java.lang.reflect.Array ; +import java.util.Map; +import java.util.HashMap; +import java.util.Vector ; +import java.util.Arrays ; +import java.util.Iterator; + +import java.lang.reflect.Method ; +import java.lang.reflect.InvocationTargetException ; + +public class RJavaArrayTools { + + // TODO: maybe factor this out of this class + private static Map primitiveClasses = initPrimitiveClasses() ; + private static Map initPrimitiveClasses(){ + Map primitives = new HashMap(); + primitives.put( "I", Integer.TYPE ); + primitives.put( "Z", Boolean.TYPE ); + primitives.put( "B", Byte.TYPE ); + primitives.put( "J", Long.TYPE ); + primitives.put( "S", Short.TYPE ); + primitives.put( "D", Double.TYPE ); + primitives.put( "C", Character.TYPE ); + primitives.put( "F", Float.TYPE ); + return primitives; + } + + // {{{ getObjectTypeName + /** + * Get the object type name of an multi dimensional array. + * + * @param o object + * @throws NotAnArrayException if the object is not an array + */ + public static String getObjectTypeName(Object o) throws NotAnArrayException { + Class o_clazz = o.getClass(); + if( !o_clazz.isArray() ) throw new NotAnArrayException( o_clazz ); + + String cl = o_clazz.getName(); + return cl.replaceFirst("\\[+L?", "").replace(";", "") ; + } + public static int getObjectTypeName(int x) throws NotAnArrayException { throw new NotAnArrayException("primitive type : int ") ; } + public static int getObjectTypeName(boolean x) throws NotAnArrayException { throw new NotAnArrayException("primitive type : boolean ") ; } + public static int getObjectTypeName(byte x) throws NotAnArrayException { throw new NotAnArrayException("primitive type : byte ") ; } + public static int getObjectTypeName(long x) throws NotAnArrayException { throw new NotAnArrayException("primitive type : long ") ; } + public static int getObjectTypeName(short x) throws NotAnArrayException { throw new NotAnArrayException("primitive type : short ") ; } + public static int getObjectTypeName(double x) throws NotAnArrayException { throw new NotAnArrayException("primitive type : double ") ; } + public static int getObjectTypeName(char x) throws NotAnArrayException { throw new NotAnArrayException("primitive type : char ") ; } + public static int getObjectTypeName(float x) throws NotAnArrayException { throw new NotAnArrayException("primitive type : float ") ; } + // }}} + + // {{{ makeArraySignature + // TODO: test + public static String makeArraySignature( String typeName, int depth ){ + StringBuffer buffer = new StringBuffer() ; + for( int i=0; i<depth; i++){ + buffer.append( '[' ) ; + } + buffer.append( typeName ); + if( ! isPrimitiveTypeName( typeName ) ){ + buffer.append( ';') ; + } + return buffer.toString(); + } + // }}} + + // {{{ getClassForSignature + public static Class getClassForSignature(String signature, ClassLoader loader) throws ClassNotFoundException { + if( primitiveClasses.containsKey(signature) ){ + return (Class)primitiveClasses.get( signature ) ; + } + return Class.forName(signature, true, loader) ; + } + // }}} + + // {{{ isSingleDimensionArray + public static boolean isSingleDimensionArray( Object o) throws NotAnArrayException{ + if( !isArray(o) ) throw new NotAnArrayException( o.getClass() ) ; + + String cn = o.getClass().getName() ; + if( cn.lastIndexOf('[') != 0 ) return false; + return true ; + } + // }}} + + // {{{ isPrimitiveTypeName + public static boolean isPrimitiveTypeName(String name){ + if( name.length() > 1 ) return false; + if( name.equals("I") ) return true ; + if( name.equals("Z") ) return true ; + if( name.equals("B") ) return true ; + if( name.equals("J") ) return true ; + if( name.equals("S") ) return true ; + if( name.equals("D") ) return true ; + if( name.equals("C") ) return true ; + if( name.equals("F") ) return true ; + return false; + } + // }}} + + // {{{ isRectangularArray + /** + * Indicates if o is a rectangular array + * + * @param o an array + * @deprecated use new ArrayWrapper(o).isRectangular() instead + */ + public static boolean isRectangularArray(Object o) { + if( !isArray(o) ) return false; + boolean res = false; + try{ + if( getDimensionLength( o ) == 1 ) return true ; + res = ( new ArrayWrapper(o) ).isRectangular() ; + } catch( NotAnArrayException e){ + res = false; + } + return res ; + } + + + // thoose below make java < 1.5 happy and me unhappy ;-) + public static boolean isRectangularArray(int x) { return false ; } + public static boolean isRectangularArray(boolean x) { return false ; } + public static boolean isRectangularArray(byte x) { return false ; } + public static boolean isRectangularArray(long x) { return false ; } + public static boolean isRectangularArray(short x) { return false ; } + public static boolean isRectangularArray(double x) { return false ; } + public static boolean isRectangularArray(char x) { return false ; } + public static boolean isRectangularArray(float x) { return false ; } + + // }}} + + // {{{ getDimensionLength + /** + * Returns the number of dimensions of an array + * + * @param o an array + * @throws NotAnArrayException if this is not an array + */ + public static int getDimensionLength( Object o) throws NotAnArrayException, NullPointerException { + if( o == null ) throw new NullPointerException( "array is null" ) ; + Class clazz = o.getClass(); + if( !clazz.isArray() ) throw new NotAnArrayException(clazz) ; + int n = 0; + while( clazz.isArray() ){ + n++ ; + clazz = clazz.getComponentType() ; + } + return n ; + } + // thoose below make java < 1.5 happy and me unhappy ;-) + public static int getDimensionLength(int x) throws NotAnArrayException { throw new NotAnArrayException("primitive type : int ") ; } + public static int getDimensionLength(boolean x) throws NotAnArrayException { throw new NotAnArrayException("primitive type : boolean ") ; } + public static int getDimensionLength(byte x) throws NotAnArrayException { throw new NotAnArrayException("primitive type : byte ") ; } + public static int getDimensionLength(long x) throws NotAnArrayException { throw new NotAnArrayException("primitive type : long ") ; } + public static int getDimensionLength(short x) throws NotAnArrayException { throw new NotAnArrayException("primitive type : short ") ; } + public static int getDimensionLength(double x) throws NotAnArrayException { throw new NotAnArrayException("primitive type : double ") ; } + public static int getDimensionLength(char x) throws NotAnArrayException { throw new NotAnArrayException("primitive type : char ") ; } + public static int getDimensionLength(float x) throws NotAnArrayException { throw new NotAnArrayException("primitive type : float ") ; } + // }}} + + // {{{ getDimensions + /** + * Returns the dimensions of an array + * + * @param o an array + * @throws NotAnArrayException if this is not an array + * @return the dimensions of the array or null if the object is null + */ + public static int[] getDimensions( Object o) throws NotAnArrayException, NullPointerException { + if( o == null ) throw new NullPointerException( "array is null" ) ; + + Class clazz = o.getClass(); + if( !clazz.isArray() ) throw new NotAnArrayException(clazz) ; + Object a = o ; + + int n = getDimensionLength( o ) ; + int[] dims = new int[n] ; + int i=0; + int current ; + while( clazz.isArray() ){ + current = Array.getLength( a ) ; + dims[i] = current ; + i++; + if( current == 0 ){ + break ; // the while loop + } else { + a = Array.get( a, 0 ) ; + clazz = clazz.getComponentType() ; + } + } + + /* in case of premature stop, we fill the rest of the array with 0 */ + // this might not be true: + // Object[][] = new Object[0][10] will return c(0,0) + while( i < dims.length){ + dims[i] = 0 ; + i++ ; + } + return dims ; + } + // thoose below make java < 1.5 happy and me unhappy ;-) + public static int[] getDimensions(int x) throws NotAnArrayException { throw new NotAnArrayException("primitive type : int ") ; } + public static int[] getDimensions(boolean x) throws NotAnArrayException { throw new NotAnArrayException("primitive type : boolean ") ; } + public static int[] getDimensions(byte x) throws NotAnArrayException { throw new NotAnArrayException("primitive type : byte ") ; } + public static int[] getDimensions(long x) throws NotAnArrayException { throw new NotAnArrayException("primitive type : long ") ; } + public static int[] getDimensions(short x) throws NotAnArrayException { throw new NotAnArrayException("primitive type : short ") ; } + public static int[] getDimensions(double x) throws NotAnArrayException { throw new NotAnArrayException("primitive type : double ") ; } + public static int[] getDimensions(char x) throws NotAnArrayException { throw new NotAnArrayException("primitive type : char ") ; } + public static int[] getDimensions(float x) throws NotAnArrayException { throw new NotAnArrayException("primitive type : float ") ; } + // }}} + + // {{{ getTrueLength + /** + * Returns the true length of an array (the product of its dimensions) + * + * @param o an array + * @throws NotAnArrayException if this is not an array + * @return the number of objects in the array (the product of its dimensions). + */ + public static int getTrueLength( Object o) throws NotAnArrayException, NullPointerException { + if( o == null ) throw new NullPointerException( "array is null" ) ; + + Class clazz = o.getClass(); + if( !clazz.isArray() ) throw new NotAnArrayException(clazz) ; + Object a = o ; + + int len = 1 ; + int i = 0; + while( clazz.isArray() ){ + len = len * Array.getLength( a ) ; + if( len == 0 ) return 0 ; /* no need to go further */ + i++; + a = Array.get( a, 0 ) ; + clazz = clazz.getComponentType() ; + } + return len ; + } + // thoose below make java < 1.5 happy and me unhappy ;-) + public static int getTrueLength(int x) throws NotAnArrayException { throw new NotAnArrayException("primitive type : int ") ; } + public static int getTrueLength(boolean x) throws NotAnArrayException { throw new NotAnArrayException("primitive type : boolean ") ; } + public static int getTrueLength(byte x) throws NotAnArrayException { throw new NotAnArrayException("primitive type : byte ") ; } + public static int getTrueLength(long x) throws NotAnArrayException { throw new NotAnArrayException("primitive type : long ") ; } + public static int getTrueLength(short x) throws NotAnArrayException { throw new NotAnArrayException("primitive type : short ") ; } + public static int getTrueLength(double x) throws NotAnArrayException { throw new NotAnArrayException("primitive type : double ") ; } + public static int getTrueLength(char x) throws NotAnArrayException { throw new NotAnArrayException("primitive type : char ") ; } + public static int getTrueLength(float x) throws NotAnArrayException { throw new NotAnArrayException("primitive type : float ") ; } + // }}} + + // {{{ isArray + /** + * Indicates if a java object is an array + * + * @param o object + * @return true if the object is an array + * @deprecated use RJavaArrayTools#isArray + */ + public static boolean isArray(Object o){ + if( o == null) return false ; + return o.getClass().isArray() ; + } + // thoose below make java < 1.5 happy and me unhappy ;-) + public static boolean isArray(int x){ return false ; } + public static boolean isArray(boolean x){ return false ; } + public static boolean isArray(byte x){ return false ; } + public static boolean isArray(long x){ return false ; } + public static boolean isArray(short x){ return false ; } + public static boolean isArray(double x){ return false ; } + public static boolean isArray(char x){ return false ; } + public static boolean isArray(float x){ return false ; } + // }}} + + // {{{ ArrayDimensionMismatchException + public static class ArrayDimensionMismatchException extends Exception { + public ArrayDimensionMismatchException( int index_dim, int actual_dim ){ + super( "dimension of indexer (" + index_dim + ") too large for array (depth ="+ actual_dim+ ")") ; + } + } + // }}} + + // {{{ get + /** + * Gets a single object from a multi dimensional array + * + * @param array java array + * @param position + */ + public static Object get( Object array, int[] position ) throws NotAnArrayException, ArrayDimensionMismatchException { + return Array.get( getArray( array, position ), position[ position.length -1] ); + } + + public static int getInt( Object array, int[] position ) throws NotAnArrayException, ArrayDimensionMismatchException { + return Array.getInt( getArray( array, position ), position[ position.length -1] ); + } + public static boolean getBoolean( Object array, int[] position ) throws NotAnArrayException, ArrayDimensionMismatchException { + return Array.getBoolean( getArray( array, position ), position[ position.length -1] ); + } + public static byte getByte( Object array, int[] position ) throws NotAnArrayException, ArrayDimensionMismatchException { + return Array.getByte( getArray( array, position ), position[ position.length -1] ); + } + public static long getLong( Object array, int[] position ) throws NotAnArrayException, ArrayDimensionMismatchException { + return Array.getLong( getArray( array, position ), position[ position.length -1] ); + } + public static short getShort( Object array, int[] position ) throws NotAnArrayException, ArrayDimensionMismatchException { + return Array.getShort( getArray( array, position ), position[ position.length -1] ); + } + public static double getDouble( Object array, int[] position ) throws NotAnArrayException, ArrayDimensionMismatchException { + return Array.getDouble( getArray( array, position ), position[ position.length -1] ); + } + public static char getChar( Object array, int[] position ) throws NotAnArrayException, ArrayDimensionMismatchException { + return Array.getChar( getArray( array, position ), position[ position.length -1] ); + } + public static float getFloat( Object array, int[] position ) throws NotAnArrayException, ArrayDimensionMismatchException { + return Array.getFloat( getArray( array, position ), position[ position.length -1] ); + } + + + public static Object get( Object array, int position ) throws NotAnArrayException, ArrayDimensionMismatchException { + return get( array, new int[]{position} ) ; + } + public static int getInt( Object array, int position ) throws NotAnArrayException, ArrayDimensionMismatchException { + return getInt( array, new int[]{position} ) ; + } + public static boolean getBoolean( Object array, int position ) throws NotAnArrayException, ArrayDimensionMismatchException { + return getBoolean( array, new int[]{position} ) ; + } + public static byte getByte( Object array, int position ) throws NotAnArrayException, ArrayDimensionMismatchException { + return getByte( array, new int[]{position} ) ; + } + public static long getLong( Object array, int position ) throws NotAnArrayException, ArrayDimensionMismatchException { + return getLong( array, new int[]{position} ) ; + } + public static short getShort( Object array, int position ) throws NotAnArrayException, ArrayDimensionMismatchException { + return getShort( array, new int[]{position} ) ; + } + public static double getDouble( Object array, int position ) throws NotAnArrayException, ArrayDimensionMismatchException { + return getDouble( array, new int[]{position} ) ; + } + public static char getChar( Object array, int position ) throws NotAnArrayException, ArrayDimensionMismatchException { + return getChar( array, new int[]{position} ) ; + } + public static float getFloat( Object array, int position ) throws NotAnArrayException, ArrayDimensionMismatchException { + return getFloat( array, new int[]{position} ) ; + } + + private static void checkDimensions(Object array, int[] position) throws NotAnArrayException, ArrayDimensionMismatchException { + int poslength = position.length ; + int actuallength = getDimensionLength(array); + if( poslength > actuallength ){ + throw new ArrayDimensionMismatchException( poslength, actuallength ) ; + } + } + + // }}} + + // {{{ set + /** + * Replaces a single value of the array + * + * @param array array + * @param position index + * @param value the new value + * + * @throws NotAnArrayException if array is not an array + * @throws ArrayDimensionMismatchException if the length of position is too big + */ + public static void set( Object array, int[] position, Object value ) throws NotAnArrayException, ArrayDimensionMismatchException{ + Array.set( getArray( array, position ), position[ position.length - 1], value ) ; + } + + /* primitive versions */ + public static void set( Object array, int[] position, int value ) throws NotAnArrayException, ArrayDimensionMismatchException{ + Array.setInt( getArray( array, position ), position[ position.length - 1], value ) ; + } + public static void set( Object array, int[] position, boolean value ) throws NotAnArrayException, ArrayDimensionMismatchException{ + Array.setBoolean( getArray( array, position ), position[ position.length - 1], value ) ; + } + public static void set( Object array, int[] position, byte value ) throws NotAnArrayException, ArrayDimensionMismatchException{ + Array.setByte( getArray( array, position ), position[ position.length - 1], value ) ; + } + public static void set( Object array, int[] position, long value ) throws NotAnArrayException, ArrayDimensionMismatchException{ + Array.setLong( getArray( array, position ), position[ position.length - 1], value ) ; + } + public static void set( Object array, int[] position, short value ) throws NotAnArrayException, ArrayDimensionMismatchException{ + Array.setShort( getArray( array, position ), position[ position.length - 1], value ) ; + } + public static void set( Object array, int[] position, double value ) throws NotAnArrayException, ArrayDimensionMismatchException{ + Array.setDouble( getArray( array, position ), position[ position.length - 1], value ) ; + } + public static void set( Object array, int[] position, char value ) throws NotAnArrayException, ArrayDimensionMismatchException{ + Array.setChar( getArray( array, position ), position[ position.length - 1], value ) ; + } + public static void set( Object array, int[] position, float value ) throws NotAnArrayException, ArrayDimensionMismatchException{ + Array.setFloat( getArray( array, position ), position[ position.length - 1], value ) ; + } + + + public static void set( Object array, int position, Object value ) throws NotAnArrayException, ArrayDimensionMismatchException{ + set( array, new int[]{ position }, value ); + } + public static void set( Object array, int position, int value ) throws NotAnArrayException, ArrayDimensionMismatchException{ + set( array, new int[]{ position }, value ); + } + public static void set( Object array, int position, boolean value ) throws NotAnArrayException, ArrayDimensionMismatchException{ + set( array, new int[]{ position }, value ); + } + public static void set( Object array, int position, byte value ) throws NotAnArrayException, ArrayDimensionMismatchException{ + set( array, new int[]{ position }, value ); + } + public static void set( Object array, int position, long value ) throws NotAnArrayException, ArrayDimensionMismatchException{ + set( array, new int[]{ position }, value ); + } + public static void set( Object array, int position, short value ) throws NotAnArrayException, ArrayDimensionMismatchException{ + set( array, new int[]{ position }, value ); + } + public static void set( Object array, int position, double value ) throws NotAnArrayException, ArrayDimensionMismatchException{ + set( array, new int[]{ position }, value ); + } + public static void set( Object array, int position, char value ) throws NotAnArrayException, ArrayDimensionMismatchException{ + set( array, new int[]{ position }, value ); + } + public static void set( Object array, int position, float value ) throws NotAnArrayException, ArrayDimensionMismatchException{ + set( array, new int[]{ position }, value ); + } + + + + private static Object getArray( Object array, int[] position ) throws NotAnArrayException, ArrayDimensionMismatchException{ + checkDimensions( array, position ) ; + int poslength = position.length ; + + Object o = array ; + int i=0 ; + if( poslength > 1 ){ + while( i< (poslength-1) ){ + o = Array.get( o, position[i] ) ; + i++ ; + } + } + return o ; + } + + // TODO: also have primitive types in value + // }}} + + + // {{{ unique + // TODO: cannot use LinkedHashSet because it first was introduced in 1.4 + // and code in this area needs to work on 1.2 jvm + public static Object[] unique( Object[] array ){ + int n = array.length ; + boolean[] unique = new boolean[ array.length ]; + for( int i=0; i<array.length; i++){ + unique[i] = true ; + } + + Vector res = new Vector(); + boolean added ; + for( int i=0; i<n; i++){ + if( !unique[i] ) continue ; + Object current = array[i]; + added = false; + + for( int j=i+1; j<n; j++){ + Object o_j = array[j] ; + if( unique[j] && current.equals( o_j ) ){ + if( !added ){ + unique[i] = false; + res.add( current ); + added = true ; + } + unique[j] = false; + } + } + } + // build the array using newInstance so that it has the same + // component type as the original array and not just Object + Object[] res_array = (Object[])Array.newInstance( array.getClass().getComponentType(), res.size() ) ; + res.toArray( res_array ); + return res_array ; + } + // }}} + + // {{{ duplicated + public static boolean[] duplicated( Object[] array ){ + int n = array.length ; + boolean[] duplicated = new boolean[ array.length ]; + for( int i=0; i<array.length; i++){ + duplicated[i] = false ; + } + + for( int i=0; i<n; i++){ + if( duplicated[i] ) continue ; + Object current = array[i]; + + for( int j=i+1; j<n; j++){ + Object o_j = array[j] ; + if( !duplicated[j] && current.equals( o_j ) ){ + duplicated[j] = true; + } + } + } + + return duplicated ; + } + // }}} + + // {{{ anyDuplicated + public static int anyDuplicated( Object[] array ){ + int n = array.length ; + + for( int i=0; i<n; i++){ + Object current = array[i]; + + for( int j=i+1; j<n; j++){ + Object o_j = array[j] ; + if( current.equals( o_j ) ){ + return j ; + } + } + } + + return -1 ; + } + // }}} + + // {{{ sort + /** + * Returns a copy of the array where elements are sorted + * + * @param array array of Objects. + * @param decreasing if true the sort is in decreasing order + * + * @throws NotComparableException if the component type of the array does not + * implement the Comparable interface + */ + public static Object[] sort( Object[] array, boolean decreasing ) throws NotComparableException { + Class ct = array.getClass().getComponentType() ; + if( Comparable.class.isAssignableFrom( ct ) ){ + throw new NotComparableException( ct ) ; + } + int n = array.length ; + Object[] res = copy( array ) ; + Arrays.sort( res ) ; + + if( !decreasing ){ + return res ; + } else{ + Object current ; + int top = (res.length) / 2 ; + for( int i=0; i<top ; i++ ){ + current = res[i] ; + res[ i ] = res[ n-i-1 ] ; + res[ n-i-1 ] = current ; + } + } + return res ; + } + // }}} + + // {{{ rev + /** + * Returns a copy of the input array with elements in + * reverse order + * + * @param original input array + */ + public static Object[] rev( Object[] original ){ + int n = original.length ; + Object[] copy = (Object[])Array.newInstance( original.getClass().getComponentType() , n ); + for( int i=0; i<n ; i++ ){ + copy[n-i-1] = original[i] ; + } + return copy ; + } + // }}} + + // {{{ copy + public static Object[] copy( Object[] original ){ + int n = original.length ; + Object[] copy = (Object[])Array.newInstance( original.getClass().getComponentType() , n ); + for( int i=0; i<n ; i++ ){ + copy[i] = original[i] ; + } + return copy ; + } + // }}} + + // {{{ getIterableContent + public static Object[] getIterableContent( Iterable o){ + Vector v = new Vector(); + Iterator iterator = o.iterator(); + while( iterator.hasNext() ){ + v.add( iterator.next() ); + } + return v.toArray(); + } + // }}} + + // {{{ rep + /** + * Creates a java array by cloning o several times + * + * @param o object to clone + * @param size number of times to replicate the object + */ + public static Object[] rep( Object o, int size ) throws Throwable { + Object[] res = (Object[])Array.newInstance( o.getClass(), size ) ; + if( ! ( o instanceof Cloneable )){ + return res ; + } + + Method m = getCloneMethod( o.getClass() ) ; + boolean access = m.isAccessible() ; + m.setAccessible( true ) ; + try{ + for( int i=0; i<size; i++){ + Object cloned = o.getClass().cast( m.invoke( o, (Object[])null ) ); + res[i] = cloned ; + } + } catch( IllegalAccessException e) { + m.setAccessible( access ); + /* should not happen */ + } catch( InvocationTargetException e){ + m.setAccessible( access ); + throw e.getCause() ; + } + return res ; + } + + private static Method getCloneMethod(Class cl){ + Method[] methodz ; + Method m = null ; + while( cl != null ){ + methodz = cl.getDeclaredMethods( ) ; + for( int i=0; i<methodz.length; i++){ + m = methodz[i]; + if( "clone".equals( m.getName() ) && m.getParameterTypes().length == 0 ){ + return m ; + } + } + cl = cl.getSuperclass(); + } + return null ; /* never happens */ + } + // }}} + + // {{{ cloneObject + public static Object cloneObject( Object o) throws Throwable { + Method m = getCloneMethod( o.getClass() ) ; + boolean access = m.isAccessible() ; + m.setAccessible( true ) ; + + Object copy = null ; + + try{ + copy = o.getClass().cast( m.invoke( o, (Object[])null ) ); + } catch( IllegalAccessException e) { + m.setAccessible( access ); + /* should not happen */ + } catch( InvocationTargetException e){ + m.setAccessible( access ); + throw e.getCause() ; + } + return copy ; + } + // }}} + + /// boxing and unboxing for Double[] and Integer[] + public static final int NA_INTEGER = -2147483648; + public static final double NA_REAL = Double.longBitsToDouble(0x7ff00000000007a2L); + static final long NA_bits = Double.doubleToRawLongBits(Double.longBitsToDouble(0x7ff00000000007a2L)); + + public static double[] unboxDoubles(Double[] o) { + if (o == null) return null; + int i = 0, n = o.length; + double d[] = new double[n]; + for (i = 0; i < n; i++) d[i] = (o[i] == null) ? NA_REAL : o[i].doubleValue(); + return d; + } + + public static int[] unboxIntegers(Integer[] o) { + if (o == null) return null; + int i = 0, n = o.length; + int d[] = new int[n]; + for (i = 0; i < n; i++) d[i] = (o[i] == null) ? NA_INTEGER : o[i].intValue(); + return d; + } + + public static int[] unboxBooleans(Boolean[] o) { + if (o == null) return null; + int i = 0, n = o.length; + int d[] = new int[n]; + for (i = 0; i < n; i++) d[i] = (o[i] == null) ? NA_INTEGER : (o[i].booleanValue() ? 1 : 0); + return d; + } + + public static boolean isNA(double value) { + /* on OS X i386 the MSB of the fraction is set even though R doesn't set it. + Although this is technically a good idea (to make it a QNaN) it's not what R does and thus makes the comparison tricky */ + return (Double.doubleToRawLongBits(value) & 0xfff7ffffffffffffL) == (NA_bits & 0xfff7ffffffffffffL); + } + + public static Double[] boxDoubles(double[] d) { + if (d == null) return null; + int i = 0, n = d.length; + Double o[] = new Double[i]; + for (i = 0; i < n; i++) if (!isNA(d[i])) o[i] = new Double(d[i]); + return o; + } + + public static Integer[] boxIntegers(int[] d) { + if (d == null) return null; + int i = 0, n = d.length; + Integer o[] = new Integer[i]; + for (i = 0; i < n; i++) if (d[i] != NA_INTEGER) o[i] = new Integer(d[i]); + return o; + } + + public static Boolean[] boxBooleans(int[] d) { + if (d == null) return null; + int i = 0, n = d.length; + Boolean o[] = new Boolean[i]; + for (i = 0; i < n; i++) if (d[i] != NA_INTEGER) o[i] = new Boolean((d[i] == 0) ? false : true); + return o; + } +} diff --git a/com.oracle.truffle.r.pkgs/rJava/src/java/RJavaClassLoader.java b/com.oracle.truffle.r.pkgs/rJava/src/java/RJavaClassLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..9823e602e7c6308445c7bbb3ea7e0795cfc1ad06 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/src/java/RJavaClassLoader.java @@ -0,0 +1,675 @@ +/* + * This material is distributed under the GNU General Public License + * Version 2. You may review the terms of this license at + * http://www.gnu.org/licenses/gpl-2.0.html + * + * Copyright (c) 2006, Simon Urbanek + * Copyright (c) 2018, Oracle and/or its affiliates + * + * All rights reserved. + */ + +// :tabSize=4:indentSize=4:noTabs=false:folding=explicit:collapseFolds=1: + +// {{{ imports +import java.io.*; +import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.HashMap; +import java.util.Vector; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.StringTokenizer; +import java.util.zip.*; +// }}} + +/** + * Class loader used internally by rJava + * + * The class manages the class paths and the native libraries (jri, ...) + */ +public class RJavaClassLoader extends URLClassLoader { + + // {{{ fields + /** + * path of RJava + */ + String rJavaPath ; + + /** + * lib sub directory of rJava + */ + String rJavaLibPath; + + /** + * map of libraries + */ + HashMap/*<String,UnixFile>*/ libMap; + + /** + * The class path vector + */ + Vector/*<UnixFile>*/ classPath; + + /** + * singleton + */ + public static RJavaClassLoader primaryLoader = null; + + /** + * Print debug messages if is set to <code>true</code> + */ + public static boolean verbose = false; + + /** + * Should the system class loader be used to resolve classes + * as well as this class loader + */ + public boolean useSystem = true; + // }}} + + // {{{ UnixFile class + /** + * Light extension of File that handles file separators and updates + */ + class UnixFile extends File { + + /** + * cached "last time modified" stamp + */ + long lastModStamp; + + /** + * Constructor. Modifies the path so that + * the proper path separator is used (most useful on windows) + */ + public UnixFile(String fn) { + super( u2w(fn) ) ; + lastModStamp=0; + } + + /** + * @return whether the file modified since last time the update method was called + */ + public boolean hasChanged() { + long curMod = lastModified(); + return (curMod != lastModStamp); + } + + /** + * Cache the result of the lastModified stamp + */ + public void update() { + lastModStamp = lastModified(); + } + } + // }}} + + // {{{ UnixJarFile + /** + * Specialization of UnixFile that deals with jar files + */ + class UnixJarFile extends UnixFile { + + /** + * The cached jar file + */ + private ZipFile zfile ; + + /** + * common prefix for all URLs within this jar file + */ + private String urlPrefix ; + + public UnixJarFile( String filename ){ + super( filename ); + } + + /* @Override */ + public void update(){ + try { + if (zfile != null){ + zfile.close(); + } + zfile = new ZipFile( this ) ; + } catch (Exception tryCloseX) {} + + /* time stamp */ + super.update( ) ; + } + + /** + * Get an input stream for a resource contained in the jar file + * + * @param name file name of the resource within the jar file + * @return an input stream representing the resouce if it exists or null + */ + public InputStream getResourceAsStream( String name ){ + + if (zfile==null || hasChanged()) { + update(); + } + try { + if (zfile == null) return null; + ZipEntry e = zfile.getEntry(name); + if (e != null) + return zfile.getInputStream(e); + } catch(Exception e) { + if (verbose) System.err.println("RJavaClassLoader$UnixJarFile: exception: "+e.getMessage()); + } + return null; + } + + + public URL getResource(String name ){ + if( zfile == null || zfile.getEntry( name ) == null ){ + return null ; + } + + URL u = null ; + if( urlPrefix == null ){ + try{ + urlPrefix = "jar:" + toURL().toString() + "!" ; + } catch( java.net.MalformedURLException ex){ + } catch( java.io.IOException ex){ + } + } + + try{ + u = new URL( urlPrefix + name ) ; + } catch( java.net.MalformedURLException ex ){ + /* not to worry */ + } + return u ; + } + + } + // }}} + + // {{{ UnixDirectory class + /** + * Specialization of UnixFile representing a directory + */ + /* it is not really a specialization but makes possible to dispatch on instanceof*/ + class UnixDirectory extends UnixFile { + public UnixDirectory( String dirname ){ + super( dirname ) ; + } + } + // }}} + + + // {{{ getPrimaryLoader + /** + * Returns the singleton instance of RJavaClassLoader + */ + public static RJavaClassLoader getPrimaryLoader() { + return primaryLoader; + } + // }}} + + // {{{ constructor + /** + * Constructor. The first time an RJavaClassLoader is created, it is + * cached as the primary loader. + * + * @param path path of the rJava package + * @param libpath lib sub directory of the rJava package + */ + public RJavaClassLoader(String path, String libpath) { + super(new URL[] {}); + // respect rJava.debug level + String rjd = System.getProperty("rJava.debug"); + if (rjd != null && rjd.length() > 0 && !rjd.equals("0")) verbose = true; + if (verbose) System.out.println("RJavaClassLoader(\""+path+"\",\""+libpath+"\")"); + if (primaryLoader==null) { + primaryLoader = this; + if (verbose) System.out.println(" - primary loader"); + } else { + if (verbose) System.out.println(" - NOT primrary (this="+this+", primary="+primaryLoader+")"); + } + libMap = new HashMap/*<String,UnixFile>*/(); + + classPath = new Vector/*<UnixFile>*/(); + classPath.add(new UnixDirectory(path+"/java")); + + rJavaPath = path; + rJavaLibPath = libpath; + + /* load the rJava library */ + UnixFile so = new UnixFile(rJavaLibPath+"/rJava.so"); + if (!so.exists()) + so = new UnixFile(rJavaLibPath+"/rJava.dll"); + if (so.exists()) + libMap.put("rJava", so); + + /* load the jri library */ + UnixFile jri = new UnixFile(path+"/jri/libjri.so"); + String rarch = System.getProperty("r.arch"); + if (rarch != null && rarch.length()>0) { + UnixFile af = new UnixFile(path+"/jri"+rarch+"/libjri.so"); + if (af.exists()) + jri = af; + else { + af = new UnixFile(path+"/jri"+rarch+"/jri.dll"); + if (af.exists()) + jri = af; + } + } + if (!jri.exists()) + jri = new UnixFile(path+"/jri/libjri.jnilib"); + if (!jri.exists()) + jri = new UnixFile(path+"/jri/jri.dll"); + if (jri.exists()) { + libMap.put("jri", jri); + if (verbose) System.out.println(" - registered JRI: "+jri); + } + + /* if we are the primary loader, make us the context loader so + projects that rely on the context loader pick us */ + if (primaryLoader == this) + Thread.currentThread().setContextClassLoader(this); + + if (verbose) { + System.out.println("RJavaClassLoader initialized.\n\nRegistered libraries:"); + for(Iterator entries = libMap.keySet().iterator(); entries.hasNext(); ) { + Object key = entries.next(); System.out.println(" " + key + ": '" + libMap.get(key) + "'"); + } + System.out.println("\nRegistered class paths:"); + for (Enumeration e = classPath.elements() ; e.hasMoreElements() ;) + System.out.println(" '"+e.nextElement()+"'"); + System.out.println("\n-- end of class loader report --"); + } + } + // }}} + + // {{{ classNameToFile + /** + * convert . to / + */ + String classNameToFile(String cls) { + return cls.replace('.','/'); + } + // }}} + + // {{{ findClass + protected Class findClass(String name) throws ClassNotFoundException { + Class cl = null; + if (verbose) System.out.println(""+this+".findClass("+name+")"); + if ("RJavaClassLoader".equals(name)) return getClass(); + + // {{{ use the usual method of URLClassLoader + if (useSystem) { + try { + cl = super.findClass(name); + if (cl != null) { + if (verbose) System.out.println("RJavaClassLoader: found class "+name+" using URL loader"); + return cl; + } + } catch (Exception fnf) { + if (verbose) System.out.println(" - URL loader did not find it: " + fnf); + } + } + if (verbose) System.out.println("RJavaClassLoader.findClass(\""+name+"\")"); + // }}} + + // {{{ iterate through the elements of the class path + InputStream ins = null; + Exception defineException = null; + Enumeration/*<UnixFile>*/ e = classPath.elements() ; + while( e.hasMoreElements() ){ + UnixFile cp = (UnixFile) e.nextElement(); + + if (verbose) System.out.println(" - trying class path \""+cp+"\""); + try { + ins = null; + /* a file - assume it is a jar file */ + if (cp instanceof UnixJarFile){ + ins = ((UnixJarFile)cp).getResourceAsStream( classNameToFile(name) + ".class" ) ; + if (verbose) System.out.println(" JAR file, can get '" + classNameToFile(name) + "'? " + ((ins == null) ? "NO" : "YES")); + } else if ( cp instanceof UnixDirectory ){ + UnixFile class_f = new UnixFile(cp.getPath()+"/"+classNameToFile(name)+".class"); + if (class_f.isFile() ) { + ins = new FileInputStream(class_f); + } + if (verbose) System.out.println(" Directory, can get '" + class_f + "'? " + ((ins == null) ? "NO" : "YES")); + } + + /* some comments on the following : + + we could call ZipEntry.getSize in case of a jar file to + find out the size of the byte[] directly + + also ByteBuffer seems more efficient, but the ByteBuffer class + is java >= 1.4 and the defineClass method that uses the class + is java >= 1.5 + */ + + if (ins != null) { + int al = 128*1024; + byte fc[] = new byte[al]; + int n = ins.read(fc); + int rp = n; + if( verbose ) System.out.println(" loading class file, initial n = "+n); + while (n > 0) { + if (rp == al) { + int nexa = al*2; + if (nexa<512*1024) nexa=512*1024; + byte la[] = new byte[nexa]; + System.arraycopy(fc, 0, la, 0, al); + fc = la; + al = nexa; + } + n = ins.read(fc, rp, fc.length-rp); + if( verbose ) System.out.println(" next n = "+n+" (rp="+rp+", al="+al+")"); + if (n>0) rp += n; + } + ins.close(); + n = rp; + if (verbose) System.out.println("RJavaClassLoader: loaded class "+name+", "+n+" bytes"); + try { + cl = defineClass(name, fc, 0, n); + } catch (Exception dce) { + // we want to save this one so we can pass it on + defineException = dce; + break; + } + if (verbose) System.out.println(" defineClass('" + name +"') returned " + cl); + // System.out.println(" - class = "+cl); + return cl; + } + } catch (Exception ex) { + // System.out.println(" * won't work: "+ex.getMessage()); + } + } + // }}} + + if (defineException != null) // we bailed out on class interpretation, re-throw it + throw (new ClassNotFoundException("Class not found - candidate class binary found but could not be loaded", defineException)); + + // giving up + if( verbose ) System.out.println(" >> ClassNotFoundException "); + if (cl == null) { + throw (new ClassNotFoundException()); + } + return cl; + } + // }}} + + // {{{ findResource + public URL findResource(String name) { + if (verbose) System.out.println("RJavaClassLoader: findResource('"+name+"')"); + + // {{{ use the standard way + if (useSystem) { + try { + URL u = super.findResource(name); + if (u != null) { + if (verbose) System.out.println("RJavaClassLoader: found resource in "+u+" using URL loader."); + return u; + } + } catch (Exception fre) { + } + } + // }}} + + // {{{ iterate through the classpath + if (verbose) System.out.println(" - resource not found with URL loader, trying alternative"); + Enumeration/*<UnixFile>*/ e = classPath.elements() ; + while( e.hasMoreElements()) { + UnixFile cp = (UnixFile) e.nextElement(); + + try { + /* is a file - assume it is a jar file */ + if (cp instanceof UnixJarFile ) { + URL u = ( (UnixJarFile)cp ).getResource( name ) ; + if (u != null) { + if (verbose) System.out.println(" - found in a JAR file, URL "+u); + return u; + } + } else if(cp instanceof UnixDirectory ) { + UnixFile res_f = new UnixFile(cp.getPath()+"/"+name); + if (res_f.isFile()) { + if (verbose) System.out.println(" - find as a file: "+res_f); + return res_f.toURL(); + } + } + } catch (Exception iox) { + } + } + // }}} + return null; + } + // }}} + + // {{{ addRLibrary + /** add a library to path mapping for a native library */ + public void addRLibrary(String name, String path) { + libMap.put(name, new UnixFile(path)); + } + // }}} + + // {{{ addClassPath + /** + * adds an entry to the class path + */ + public void addClassPath(String cp) { + UnixFile f = new UnixFile(cp); + + // use the URLClassLoader + if (useSystem) { + try { + addURL(f.toURL()); + if (verbose) System.out.println("RJavaClassLoader: added '" + cp + "' to the URL class path loader"); + //return; // we need to add it anyway so it appears in .jclassPath() + } catch (Exception ufe) { + } + } + + UnixFile g = null ; + if( f.isFile() && (f.getName().endsWith(".jar") || f.getName().endsWith(".JAR"))) { + g = new UnixJarFile(cp) ; + if (verbose) System.out.println("RJavaClassLoader: adding Java archive file '"+cp+"' to the internal class path"); + } else if( f.isDirectory() ){ + g = new UnixDirectory(cp) ; + if (verbose) System.out.println("RJavaClassLoader: adding class directory '"+cp+"' to the internal class path"); + } else if (verbose) + System.err.println(f.exists() ? + ("WARNING: the path '"+cp+"' is neither a directory nor a .jar file, it will NOT be added to the internal class path!") : + ("WARNING: the path '"+cp+"' does NOT exist, it will NOT be added to the internal class path!")); + + if (g != null && !classPath.contains(g)) { + // this is the real meat - add it to our internal list + classPath.add(g); + // this is just cosmetics - it doesn't really have any meaning + System.setProperty("java.class.path", + System.getProperty("java.class.path")+File.pathSeparator+g.getPath()); + } + } + + /** + * adds several entries to the class path + */ + public void addClassPath(String[] cp) { + int i = 0; + while (i < cp.length) addClassPath(cp[i++]); + } + // }}} + + // {{{ getClassPath + /** + * @return the array of class paths used by this class loader + */ + public String[] getClassPath() { + int j = classPath.size(); + String[] s = new String[j]; + int i = 0; + while (i < j) { + s[i] = ((UnixFile) classPath.elementAt(i)).getPath(); + i++; + } + return s; + } + // }}} + + // {{{ findLibrary + protected String findLibrary(String name) { + if (verbose) System.out.println("RJavaClassLoader.findLibrary(\""+name+"\")"); + //if (name.equals("rJava")) + // return rJavaLibPath+"/"+name+".so"; + + UnixFile u = (UnixFile) libMap.get(name); + String s = null; + if (u!=null && u.exists()) s=u.getPath(); + if (verbose) System.out.println(" - mapping to "+((s==null)?"<none>":s)); + + return s; + } + // }}} + + // {{{ bootClass + /** + * Boots the specified method of the specified class + * + * @param cName class to boot + * @param mName method to boot (typically main). The method must take a String[] as parameter + * @param args arguments to pass to the method + */ + public void bootClass(String cName, String mName, String[] args) throws java.lang.IllegalAccessException, java.lang.reflect.InvocationTargetException, java.lang.NoSuchMethodException, java.lang.ClassNotFoundException { + Class c = findClass(cName); + resolveClass(c); + java.lang.reflect.Method m = c.getMethod(mName, new Class[] { String[].class }); + m.invoke(null, new Object[] { args }); + } + // }}} + + // {{{ setDebug + /** + * Set the debug level. At the moment, there is only verbose (level > 0) + * or quiet + * + * @param level debug level. verbose (>0), quiet otherwise + */ + public static void setDebug(int level) { + verbose=(level>0); + } + // }}} + + // {{{ u2w + /** + * Utility to convert paths for windows. Converts / to the path separator in use + * + * @param fn file name + */ + public static String u2w(String fn) { + return (File.separatorChar != '/') ? fn.replace('/', File.separatorChar) : fn ; + } + // }}} + + // {{{ main + /** + * main method + * + * This uses the system properties: + * <ul> + * <li><code>rjava.path</code> : path of the rJava package</li> + * <li><code>rjava.lib</code> : lib sub directory of the rJava package</li> + * <li><code>main.class</code> : main class to "boot", assumes Main if not specified</li> + * <li><code>rjava.class.path</code> : set of paths to populate the initiate the class path</li> + * </ul> + * + * <p>and boots the "main" method of the specified <code>main.class</code>, + * passing the args down to the booted class</p> + * + * <p>This makes sure R and rJava are known by the class loader</p> + */ + public static void main(String[] args) { + String rJavaPath = System.getProperty("rjava.path"); + if (rJavaPath == null) { + System.err.println("ERROR: rjava.path is not set"); + System.exit(2); + } + String rJavaLib = System.getProperty("rjava.lib"); + if (rJavaLib == null) { // it is not really used so far, just for rJava.so, so we can guess + rJavaLib = rJavaPath + File.separator + "libs"; + } + RJavaClassLoader cl = new RJavaClassLoader(u2w(rJavaPath), u2w(rJavaLib)); + String mainClass = System.getProperty("main.class"); + if (mainClass == null || mainClass.length()<1) { + System.err.println("WARNING: main.class not specified, assuming 'Main'"); + mainClass = "Main"; + } + String classPath = System.getProperty("rjava.class.path"); + if (classPath != null) { + StringTokenizer st = new StringTokenizer(classPath, File.pathSeparator); + while (st.hasMoreTokens()) { + String dirname = u2w(st.nextToken()); + cl.addClassPath(dirname); + } + } + try { + cl.bootClass(mainClass, "main", args); + } catch (Exception ex) { + System.err.println("ERROR: while running main method: "+ex); + ex.printStackTrace(); + } + } + // }}} + + //----- tools ----- + + // {{{ RJavaObjectInputStream class + class RJavaObjectInputStream extends ObjectInputStream { + public RJavaObjectInputStream(InputStream in) throws IOException { + super(in); + } + protected Class resolveClass(ObjectStreamClass desc) throws ClassNotFoundException { + return Class.forName(desc.getName(), false, RJavaClassLoader.getPrimaryLoader()); + } + } + // }}} + + // {{{ toByte + /** + * Serialize an object to a byte array. (code by CB) + * + * @param object object to serialize + * @return byte array that represents the object + * @throws Exception + */ + public static byte[] toByte(Object object) throws Exception { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream((OutputStream) os); + oos.writeObject(object); + oos.close(); + return os.toByteArray(); + } + // }}} + + // {{{ toObject + /** + * Deserialize an object from a byte array. (code by CB) + * + * @param byteArray + * @return the object that is represented by the byte array + * @throws Exception + */ + public Object toObject(byte[] byteArray) throws Exception { + InputStream is = new ByteArrayInputStream(byteArray); + RJavaObjectInputStream ois = new RJavaObjectInputStream(is); + Object o = (Object) ois.readObject(); + ois.close(); + return o; + } + // }}} + + // {{{ toObjectPL + /** + * converts the byte array into an Object using the primary RJavaClassLoader + */ + public static Object toObjectPL(byte[] byteArray) throws Exception{ + return RJavaClassLoader.getPrimaryLoader().toObject(byteArray); + } + // }}} +} diff --git a/com.oracle.truffle.r.pkgs/rJava/src/java/RJavaComparator.java b/com.oracle.truffle.r.pkgs/rJava/src/java/RJavaComparator.java new file mode 100644 index 0000000000000000000000000000000000000000..a220269cdbe713b49550df26576e5c960c04d3af --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/src/java/RJavaComparator.java @@ -0,0 +1,62 @@ +/* + * This material is distributed under the GNU General Public License + * Version 2. You may review the terms of this license at + * http://www.gnu.org/licenses/gpl-2.0.html + * + * Copyright (c) 2006, Simon Urbanek + * Copyright (c) 2018, Oracle and/or its affiliates + * + * All rights reserved. + */ +import java.lang.Comparable ; + +/** + * Utility class to compare two objects in the sense + * of the java.lang.Comparable interface + * + */ +public class RJavaComparator { + + /** + * compares a and b in the sense of the java.lang.Comparable if possible + * + * <p>instances of the Number interface are treated specially, in order to + * allow comparing Numbers of different classes, for example it is allowed + * to compare a Double with an Integer. if the Numbers have the same class, + * they are compared normally, otherwise they are first converted to Doubles + * and then compared</p> + * + * @param a an object + * @param b another object + * + * @return the result of <code>a.compareTo(b)</code> if this makes sense + * @throws NotComparableException if the two objects are not comparable + */ + public static int compare( Object a, Object b ) throws NotComparableException{ + int res ; + if( a.equals( b ) ) return 0 ; + + // treat Number s separately + if( a instanceof Number && b instanceof Number && !( a.getClass() == b.getClass() ) ){ + Double _a = new Double( ((Number)a).doubleValue() ); + Double _b = new Double( ((Number)b).doubleValue() ); + return _a.compareTo( _b ); + } + + if( ! ( a instanceof Comparable ) ) throw new NotComparableException( a ); + if( ! ( b instanceof Comparable ) ) throw new NotComparableException( b ); + + try{ + res = ( (Comparable)a ).compareTo( b ) ; + } catch( ClassCastException e){ + try{ + res = - ((Comparable)b).compareTo( a ) ; + } catch( ClassCastException f){ + throw new NotComparableException( a, b ); + } + } + return res ; + } + +} + diff --git a/com.oracle.truffle.r.pkgs/rJava/src/java/RJavaImport.java b/com.oracle.truffle.r.pkgs/rJava/src/java/RJavaImport.java new file mode 100644 index 0000000000000000000000000000000000000000..9b5f664808b26bb87accc5af55b8eac418a1f917 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/src/java/RJavaImport.java @@ -0,0 +1,182 @@ +/* + * This material is distributed under the GNU General Public License + * Version 2. You may review the terms of this license at + * http://www.gnu.org/licenses/gpl-2.0.html + * + * Copyright (c) 2006, Simon Urbanek + * Copyright (c) 2018, Oracle and/or its affiliates + * + * All rights reserved. + */ + +import java.util.regex.Pattern ; +import java.util.regex.Matcher ; + +import java.util.Vector; +import java.util.HashMap; +import java.util.Map; +import java.util.Collection; +import java.util.Set ; +import java.util.Iterator ; + +import java.io.Serializable; + +/** + * Utilities to manage java packages and how they are "imported" to R + * databases. This is the back door of the <code>javaImport</code> + * system in the R side + * + * @author Romain Francois <francoisromain@free.fr> + */ +public class RJavaImport implements Serializable { + + /** + * Debug flag. Prints some messages if it is set to TRUE + */ + public static boolean DEBUG = false ; + + /** + * list of imported packages + */ + /* TODO: vector is not good enough, we need to control the order + in which the packages appear */ + private Vector/*<String>*/ importedPackages ; + + /** + * maps a simple name to a fully qualified name + */ + /* String -> java.lang.String */ + /* should we cache the Class instead ? */ + private Map/*<String,String>*/ cache ; + + /** + * associated class loader + */ + public ClassLoader loader ; + + /** + * Constructor. Initializes the imported package vector and the cache + */ + public RJavaImport( ClassLoader loader ){ + this.loader = loader ; + importedPackages = new Vector/*<String>*/(); + cache = new HashMap/*<String,String>*/() ; + } + + /** + * Look for the class in the set of packages + * + * @param clazz the simple class name + * + * @return an instance of Class representing the actual class + */ + public Class lookup( String clazz){ + Class res = lookup_(clazz) ; + if( DEBUG ) System.out.println( " [J] lookup( '" + clazz + "' ) = " + (res == null ? " " : ("'" + res.getName() + "'" ) ) ) ; + return res ; + } + + private Class lookup_( String clazz ){ + Class res = null ; + + if( cache.containsKey( clazz ) ){ + try{ + String fullname = (String)cache.get( clazz ) ; + Class cl = Class.forName( fullname ) ; + return cl ; + } catch( Exception e ){ + /* does not happen */ + } + } + + /* first try to see if the class does not exist verbatim */ + try{ + res = Class.forName( clazz ) ; + } catch( Exception e){} + if( res != null ) { + cache.put( clazz, clazz ) ; + return res; + } + + int npacks = importedPackages.size() ; + if( DEBUG ) System.out.println( " [J] " + npacks + " packages" ) ; + if( npacks > 0 ){ + for( int i=0; i<npacks; i++){ + try{ + String p = (String)importedPackages.get(i); + String candidate = p + "." + clazz ; + if( DEBUG ) System.out.println( " [J] trying class : " + candidate ) ; + res = Class.forName( candidate ) ; + } catch( Exception e){ + if( DEBUG ) System.out.println( " [JE] " + e.getMessage() ); + } + if( res != null ){ + cache.put( clazz, res.getName() ) ; + return res ; + } + } + } + return null ; + } + + /** + * @return true if the class is known + */ + public boolean exists( String clazz){ + boolean res = exists_(clazz) ; + if( DEBUG ) System.out.println( " [J] exists( '" + clazz + "' ) = " + res ) ; + return res ; + } + + public boolean exists_( String clazz ){ + if( cache.containsKey( clazz ) ) return true ; + + return ( lookup_( clazz ) != null ); + } + + /** + * Adds a package to the list of "imported" packages + * + * @param packageName package path name + */ + public void importPackage( String packageName ){ + importedPackages.add( packageName ) ; + } + + /** + * Adds a set of packages + * + * @param packages package path names + */ + public void importPackage( String[] packages ){ + for( int i=0; i<packages.length; i++){ + importPackage( packages[i] ) ; + } + } + + /** + * @return the simple names of the classes currently known + * by this importer + */ + public String[] getKnownClasses(){ + Set/*<String>*/ set = cache.keySet() ; + int size = set.size() ; + String[] res = new String[size]; + set.toArray( res ); + if( DEBUG ) System.out.println( " [J] getKnownClasses().length = " + res.length ) ; + return res ; + } + + public static Class lookup( String clazz , Set importers ){ + Class res ; + Iterator iterator = importers.iterator() ; + while( iterator.hasNext()){ + RJavaImport importer = (RJavaImport)iterator.next() ; + res = importer.lookup( clazz ) ; + if( res != null ) return res ; + } + return null ; + } + +} + diff --git a/com.oracle.truffle.r.pkgs/rJava/src/java/RJavaTools.java b/com.oracle.truffle.r.pkgs/rJava/src/java/RJavaTools.java new file mode 100644 index 0000000000000000000000000000000000000000..f7906f94bf24832931efa3a81e6adcc5dbf14be3 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/src/java/RJavaTools.java @@ -0,0 +1,748 @@ +/* + * This material is distributed under the GNU General Public License + * Version 2. You may review the terms of this license at + * http://www.gnu.org/licenses/gpl-2.0.html + * + * Copyright (c) 2009-2010, Simon Urbanek and Romain Francois + * Copyright (c) 2018, Oracle and/or its affiliates + * + * All rights reserved. + */ + +// RJavaTools.java: rJava - low level R to java interface +// +// Copyright (C) 2009 - 2010 Simon Urbanek and Romain Francois +// +// This file is part of rJava. +// +// rJava is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// rJava 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 for more details. +// +// You should have received a copy of the GNU General Public License +// along with rJava. If not, see <http://www.gnu.org/licenses/>. + +import java.lang.reflect.Method ; +import java.lang.reflect.Field ; +import java.lang.reflect.Constructor ; +import java.lang.reflect.InvocationTargetException ; +import java.lang.reflect.Modifier ; +import java.lang.reflect.Member ; + +import java.util.Vector ; + + +/** + * Tools used internally by rJava. + * + * The method lookup code is heavily based on ReflectionTools + * by Romain Francois <francoisromain@free.fr> licensed under GPL v2 or higher. + */ +public class RJavaTools { + + /** + * Returns an inner class of the class with the given simple name + * + * @param cl class + * @param name simple name of the inner class + * @param staticRequired boolean, if <code>true</code> the inner class is required to be static + */ + public static Class getClass(Class cl, String name, boolean staticRequired){ + Class[] clazzes = cl.getClasses(); + for( int i=0; i<clazzes.length; i++){ + if( getSimpleName( clazzes[i].getName() ).equals( name ) && ( !staticRequired || isStatic(clazzes[i]) ) ){ + return clazzes[i] ; + } + } + return null; + } + + + /** + * Returns the static inner classes of the class + * + * @param cl class + * @return an array of classes or null if cl does not have static inner classes + */ + public static Class[] getStaticClasses(Class cl){ + Class[] clazzes = cl.getClasses(); + if( clazzes == null ) return null; + + Vector vec = new Vector() ; + int n = clazzes.length; + for( int i=0; i<n; i++){ + Class clazz = clazzes[i] ; + if( isStatic( clazz ) ){ + vec.add( clazz ) ; + } + } + if( vec.size() == 0 ){ + return null ; + } + Class[] out = new Class[ vec.size() ] ; + vec.toArray( out ) ; + return out ; + } + + /** + * Indicates if a class is static + * + * @param clazz class + * @return true if the class is static + */ + public static boolean isStatic( Class clazz ){ + return (clazz.getModifiers() & Modifier.STATIC) != 0 ; + } + + /** + * Returns the static fields of the class + * + * @param cl class + * @return an array of static fields + */ + public static Field[] getStaticFields( Class cl ){ + Field[] members = cl.getFields() ; + if( members.length == 0 ){ + return null; + } + Vector vec = new Vector() ; + int n = members.length ; + for( int i=0; i<n; i++){ + Field memb = members[i] ; + if( isStatic( memb ) ){ + vec.add( memb ) ; + } + } + if( vec.size() == 0 ){ + return null ; + } + Field[] out = new Field[ vec.size() ] ; + vec.toArray( out ) ; + return out ; + } + + /** + * Returns the static methods of the class + * + * @param cl class + * @return an array of static fields + */ + public static Method[] getStaticMethods( Class cl ){ + Method[] members = cl.getMethods() ; + Vector vec = new Vector() ; + if( members.length == 0 ){ + return null; + } + int n = members.length ; + for( int i=0; i<n; i++){ + Method memb = members[i] ; + if( isStatic( memb ) ){ + vec.add( memb ) ; + } + } + if( vec.size() == 0 ){ + return null ; + } + Method[] out = new Method[ vec.size() ] ; + vec.toArray( out ) ; + return out ; + } + + + /** + * Returns the names of the fields of a given class + * + * @param cl class + * @param staticRequired if true only static fields are returned + * @return the public (and maybe only static) names of the fields. + */ + public static String[] getFieldNames( Class cl, boolean staticRequired ){ + return getMemberNames( cl.getFields(), staticRequired ) ; + } + + /** + * Returns the completion names of the methods of a given class. + * See the getMethodCompletionName method below + * + * @param cl class + * @param staticRequired if true only static methods are returned + * @return the public (and maybe only static) names of the methods. + */ + public static String[] getMethodNames( Class cl, boolean staticRequired ){ + return getMemberNames( cl.getMethods(), staticRequired ) ; + } + + private static String[] getMemberNames(Member[] members, boolean staticRequired){ + String[] result = null ; + int nm = members.length ; + if( nm == 0 ){ + return new String[0] ; + } + if( staticRequired ){ + Vector names = new Vector(); + for( int i=0; i<nm; i++ ){ + Member member = members[i] ; + if( isStatic( member ) ){ + names.add( getCompletionName(member) ) ; + } + } + if( names.size() == 0 ){ + return new String[0] ; + } + result = new String[ names.size() ] ; + names.toArray( result ) ; + } else{ + /* don't need the vector */ + result = new String[nm] ; + for( int i=0; i<nm; i++ ){ + result[i] = getCompletionName( members[i] ) ; + } + } + return result; + } + + /** + * Completion name of a member. + * + * <p>For fields, it just returns the name of the fields + * + * <p>For methods, this returns the name of the method + * plus a suffix that depends on the number of arguments of the method. + * + * <p>The string "()" is added + * if the method has no arguments, and the string "(" is added + * if the method has one or more arguments. + */ + public static String getCompletionName(Member m){ + if( m instanceof Field ) return m.getName(); + if( m instanceof Method ){ + String suffix = ( ((Method)m).getParameterTypes().length == 0 ) ? ")" : "" ; + return m.getName() + "(" + suffix ; + } + return "" ; + } + + /** + * Indicates if a member of a Class (field, method ) is static + * + * @param member class member + * @return true if the member is static + */ + public static boolean isStatic( Member member ){ + return (member.getModifiers() & Modifier.STATIC) != 0 ; + } + + /** + * Checks if the class of the object has the given field. The + * getFields method of Class is used so only public fields are + * checked + * + * @param o object + * @param name name of the field + * + * @return <code>true</code> if the class of <code>o</code> has the field <code>name</code> + */ + public static boolean hasField(Object o, String name) { + return classHasField(o.getClass(), name, false); + } + + /** + * Checks if the class of the object has the given inner class. The + * getClasses method of Class is used so only public classes are + * checked + * + * @param o object + * @param name (simple) name of the inner class + * + * @return <code>true</code> if the class of <code>o</code> has the class <code>name</code> + */ + public static boolean hasClass(Object o, String name) { + return classHasClass(o.getClass(), name, false); + } + + + /** + * Checks if the specified class has the given field. The + * getFields method of Class is used so only public fields are + * checked + * + * @param cl class object + * @param name name of the field + * @param staticRequired if <code>true</code> then the field is required to be static + * + * @return <code>true</code> if the class <code>cl</code> has the field <code>name</code> + */ + public static boolean classHasField(Class cl, String name, boolean staticRequired) { + Field[] fields = cl.getFields(); + for (int i = 0; i < fields.length; i++) + if(name.equals(fields[i].getName()) && (!staticRequired || ((fields[i].getModifiers() & Modifier.STATIC) != 0))) + return true; + return false; + } + + + /** + * Checks if the specified class has the given method. The + * getMethods method of Class is used so only public methods are + * checked + * + * @param cl class + * @param name name of the method + * @param staticRequired if <code>true</code> then the method is required to be static + * + * @return <code>true</code> if the class <code>cl</code> has the method <code>name</code> + */ + public static boolean classHasMethod(Class cl, String name, boolean staticRequired) { + Method[] methodz = cl.getMethods(); + for (int i = 0; i < methodz.length; i++) + if (name.equals(methodz[i].getName()) && (!staticRequired || ((methodz[i].getModifiers() & Modifier.STATIC) != 0))) + return true; + return false; + } + + /** + * Checks if the specified class has the given inner class. The + * getClasses method of Class is used so only public classes are + * checked + * + * @param cl class + * @param name name of the inner class + * @param staticRequired if <code>true</code> then the method is required to be static + * + * @return <code>true</code> if the class <code>cl</code> has the field <code>name</code> + */ + public static boolean classHasClass(Class cl, String name, boolean staticRequired) { + Class[] clazzes = cl.getClasses(); + for (int i = 0; i < clazzes.length; i++) + if (name.equals( getSimpleName(clazzes[i].getName()) ) && (!staticRequired || isStatic( clazzes[i] ) ) ) + return true; + return false; + } + + /** + * Checks if the class of the object has the given method. The + * getMethods method of Class is used so only public methods are + * checked + * + * @param o object + * @param name name of the method + * + * @return <code>true</code> if the class of <code>o</code> has the field <code>name</code> + */ + public static boolean hasMethod(Object o, String name) { + return classHasMethod(o.getClass(), name, false); + } + + + + /** + * Object creator. Find the best constructor based on the parameter classes + * and invoke newInstance on the resolved constructor + */ + public static Object newInstance( Class o_clazz, Object[] args, Class[] clazzes ) throws Throwable { + + boolean[] is_null = new boolean[args.length]; + for( int i=0; i<args.length; i++) { + is_null[i] = ( args[i] == null ) ; + } + + Constructor cons = getConstructor( o_clazz, clazzes, is_null ); + + /* enforcing accessibility (workaround for bug 128) */ + boolean access = cons.isAccessible(); + cons.setAccessible( true ); + + Object o; + try{ + o = cons.newInstance( args ) ; + } catch( InvocationTargetException e){ + /* the target exception is much more useful than the reflection wrapper */ + throw e.getTargetException() ; + } finally{ + cons.setAccessible( access ); + } + return o; + } + + static boolean[] arg_is_null(Object[] args){ + if( args == null ) return null ; + boolean[] is_null = new boolean[args.length]; + for( int i=0; i<args.length; i++) { + is_null[i] = ( args[i] == null ) ; + } + return is_null ; + } + + /** + * Invoke a method of a given class + * <p>First the appropriate method is resolved by getMethod and + * then invokes the method + */ + public static Object invokeMethod( Class o_clazz, Object o, String name, Object[] args, Class[] clazzes) throws Throwable { + + Method m = getMethod( o_clazz, name, clazzes, arg_is_null(args) ); + + /* enforcing accessibility (workaround for bug 128) */ + boolean access = m.isAccessible(); + m.setAccessible( true ); + + Object out; + try{ + out = m.invoke( o, args ) ; + } catch( InvocationTargetException e){ + /* the target exception is much more useful than the reflection wrapper */ + throw e.getTargetException() ; + } finally{ + m.setAccessible( access ); + } + return out ; + } + + /** + * Attempts to find the best-matching constructor of the class + * o_clazz with the parameter types arg_clazz + * + * @param o_clazz Class to look for a constructor + * @param arg_clazz parameter types + * @param arg_is_null indicates if each argument is null + * + * @return <code>null</code> if no constructor is found, or the constructor + * + */ + public static Constructor getConstructor( Class o_clazz, Class[] arg_clazz, boolean[] arg_is_null) + throws SecurityException, NoSuchMethodException { + + if (o_clazz == null) + return null; + + Constructor cons = null ; + + /* if there is no argument, try to find a direct match */ + if (arg_clazz == null || arg_clazz.length == 0) { + cons = o_clazz.getConstructor( (Class[] )null ); + return cons ; + } + + /* try to find an exact match */ + try { + cons = o_clazz.getConstructor(arg_clazz); + if (cons != null) + return cons ; + } catch (NoSuchMethodException e) { + /* we catch this one because we want to further search */ + } + + /* ok, no exact match was found - we have to walk through all methods */ + cons = null; + Constructor[] candidates = o_clazz.getConstructors(); + for (int k = 0; k < candidates.length; k++) { + Constructor c = candidates[k]; + Class[] param_clazz = c.getParameterTypes(); + if (arg_clazz.length != param_clazz.length) // number of parameters must match + continue; + int n = arg_clazz.length; + boolean ok = true; + for (int i = 0; i < n; i++) { + if( arg_is_null[i] ){ + /* then the class must not be a primitive type */ + if( isPrimitive(arg_clazz[i]) ){ + ok = false ; + break ; + } + } else{ + if (arg_clazz[i] != null && !param_clazz[i].isAssignableFrom(arg_clazz[i])) { + ok = false; + break; + } + } + } + // it must be the only match so far or more specific than the current match + if (ok && (cons == null || isMoreSpecific(c, cons))) + cons = c; + } + + if( cons == null ){ + throw new NoSuchMethodException( "No constructor matching the given parameters" ) ; + } + + return cons; + + } + + + static boolean isPrimitive(Class cl){ + return cl.equals(Boolean.TYPE) || cl.equals(Integer.TYPE) || + cl.equals(Double.TYPE) || cl.equals(Float.TYPE) || + cl.equals(Long.TYPE) || cl.equals(Short.TYPE) || + cl.equals(Character.TYPE) ; + } + + /** + * Attempts to find the best-matching method of the class <code>o_clazz</code> with the method name <code>name</code> and arguments types defined by <code>arg_clazz</code>. + * The lookup is performed by finding the most specific methods that matches the supplied arguments (see also {@link #isMoreSpecific}). + * + * @param o_clazz class in which to look for the method + * @param name method name + * @param arg_clazz an array of classes defining the types of arguments + * @param arg_is_null indicates if each argument is null + * + * @return <code>null</code> if no matching method could be found or the best matching method. + */ + public static Method getMethod(Class o_clazz, String name, Class[] arg_clazz, boolean[] arg_is_null) + throws SecurityException, NoSuchMethodException { + + if (o_clazz == null) + return null; + + /* if there is no argument, try to find a direct match */ + if (arg_clazz == null || arg_clazz.length == 0) { + return o_clazz.getMethod(name, (Class[])null); + } + + /* try to find an exact match */ + Method met; + try { + met = o_clazz.getMethod(name, arg_clazz); + if (met != null) + return met; + } catch (NoSuchMethodException e) { + /* we want to search further */ + } + + /* ok, no exact match was found - we have to walk through all methods */ + met = null; + Method[] ml = o_clazz.getMethods(); + for (int k = 0; k < ml.length; k++) { + Method m = ml[k]; + if (!m.getName().equals(name)) // the name must match + continue; + Class[] param_clazz = m.getParameterTypes(); + if (arg_clazz.length != param_clazz.length) // number of parameters must match + continue; + int n = arg_clazz.length; + boolean ok = true; + for (int i = 0; i < n; i++) { + if( arg_is_null[i] ){ + /* then the class must not be a primitive type */ + if( isPrimitive(arg_clazz[i]) ){ + ok = false ; + break ; + } + } else{ + if (arg_clazz[i] != null && !param_clazz[i].isAssignableFrom(arg_clazz[i])) { + ok = false; + break; + } + } + } + if (ok && (met == null || isMoreSpecific(m, met))) // it must be the only match so far or more specific than the current match + met = m; + } + + if( met == null ){ + throw new NoSuchMethodException( "No suitable method for the given parameters" ) ; + } + return met; + } + + /** + * Returns <code>true</code> if <code>m1</code> is more specific than <code>m2</code>. + * The measure used is described in the isMoreSpecific( Class[], Class[] ) method + * + * @param m1 method to compare + * @param m2 method to compare + * + * @return <code>true</code> if <code>m1</code> is more specific (in arguments) than <code>m2</code>. + */ + private static boolean isMoreSpecific(Method m1, Method m2) { + Class[] m1_param_clazz = m1.getParameterTypes(); + Class[] m2_param_clazz = m2.getParameterTypes(); + return isMoreSpecific( m1_param_clazz, m2_param_clazz ); + } + + /** + * Returns <code>true</code> if <code>cons1</code> is more specific than <code>cons2</code>. + * The measure used is described in the isMoreSpecific( Class[], Class[] ) method + * + * @param cons1 constructor to compare + * @param cons2 constructor to compare + * + * @return <code>true</code> if <code>cons1</code> is more specific (in arguments) than <code>cons2</code>. + */ + private static boolean isMoreSpecific(Constructor cons1, Constructor cons2) { + Class[] cons1_param_clazz = cons1.getParameterTypes(); + Class[] cons2_param_clazz = cons2.getParameterTypes(); + return isMoreSpecific( cons1_param_clazz, cons2_param_clazz ); + } + + /** + * Returns <code>true</code> if <code>c1</code> is more specific than <code>c2</code>. + * + * The measure used is the sum of more specific arguments minus the sum of less specific arguments + * which in total must be positive to qualify as more specific. + * (More specific means the argument is a subclass of the other argument). + * + * Both set of classes must have signatures fully compatible in the arguments + * (more precisely incompatible arguments are ignored in the comparison). + * + * @param c1 set of classes to compare + * @param c2 set of classes to compare + */ + private static boolean isMoreSpecific( Class[] c1, Class[] c2){ + int n = c1.length ; + int res = 0; + for (int i = 0; i < n; i++) + if( c1[i] != c2[i]) { + if( c1[i].isAssignableFrom(c2[i])) + res--; + else if( c2[i].isAssignableFrom(c2[i]) ) + res++; + } + return res > 0; + } + + /** + * Returns the list of classes of the object + * + * @param o an Object + */ + public static Class[] getClasses(Object o){ + Vector/*<Class<?>>*/ vec = new Vector(); + Class cl = o.getClass(); + while( cl != null ){ + vec.add( cl ) ; + cl = cl.getSuperclass() ; + } + Class[] res = new Class[ vec.size() ] ; + vec.toArray( res) ; + return res ; + } + + /** + * Returns the list of class names of the object + * + * @param o an Object + */ + public static String[] getClassNames(Object o){ + Vector/*<String>*/ vec = new Vector(); + Class cl = o.getClass(); + while( cl != null ){ + vec.add( cl.getName() ) ; + cl = cl.getSuperclass() ; + } + String[] res = new String[ vec.size() ] ; + vec.toArray( res) ; + return res ; + } + + /** + * Returns the list of simple class names of the object + * + * @param o an Object + */ + public static String[] getSimpleClassNames(Object o, boolean addConditionClasses){ + boolean hasException = false ; + Vector/*<String>*/ vec = new Vector(); + Class cl = o.getClass(); + String name ; + while( cl != null ){ + name = getSimpleName( cl.getName() ) ; + if( "Exception".equals( name) ){ + hasException = true ; + } + vec.add( name ) ; + cl = cl.getSuperclass() ; + } + if( addConditionClasses ){ + if( !hasException ){ + vec.add( "Exception" ) ; + } + vec.add( "error" ) ; + vec.add( "condition" ) ; + } + + String[] res = new String[ vec.size() ] ; + vec.toArray( res) ; + return res ; + } + + /* because Class.getSimpleName is java 5 API */ + private static String getSimpleClassName( Object o ){ + return getSimpleName( o.getClass().getName() ) ; + } + + private static String getSimpleName( String s ){ + int lastsquare = s.lastIndexOf( '[' ) ; + if( lastsquare >= 0 ){ + if( s.charAt( s.lastIndexOf( '[' ) + 1 ) == 'L' ){ + s = s.substring( s.lastIndexOf( '[' ) + 2, s.lastIndexOf( ';' ) ) ; + } else { + char first = s.charAt( 0 ); + if( first == 'I' ) { + s = "int" ; + } else if( first == 'D' ){ + s = "double" ; + } else if( first == 'Z' ){ + s = "boolean" ; + } else if( first == 'B' ){ + s = "byte" ; + } else if( first == 'J' ){ + s = "long" ; + } else if( first == 'F' ){ + s = "float" ; + } else if( first == 'S' ){ + s = "short" ; + } else if( first == 'C' ){ + s = "char" ; + } + } + } + + int lastdollar = s.lastIndexOf( '$' ) ; + if( lastdollar >= 0 ){ + s = s.substring( lastdollar + 1); + } + + int lastdot = s.lastIndexOf( '.' ) ; + if( lastdot >= 0 ){ + s = s.substring( lastdot + 1); + } + + if( lastsquare >= 0 ){ + StringBuffer buf = new StringBuffer( s ); + int i ; + for( i=0; i<=lastsquare; i++){ + buf.append( "[]" ); + } + return buf.toString(); + } else { + return s ; + } + + } + + /** + * @param cl class + * @param field name of the field + * + * @return the class name of the field of the class (or null) + * if the class does not have the given field) + */ + public static String getFieldTypeName( Class cl, String field){ + String res = null ; + try{ + res = cl.getField( field ).getType().getName() ; + } catch( NoSuchFieldException e){ + /* just return null */ + res = null ; + } + return res ; + } + +} diff --git a/com.oracle.truffle.r.pkgs/rJava/src/java/RectangularArrayBuilder.java b/com.oracle.truffle.r.pkgs/rJava/src/java/RectangularArrayBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..0c4b174fc12bcdc36c6ce9e63eee9410e13917b3 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/src/java/RectangularArrayBuilder.java @@ -0,0 +1,199 @@ +/* + * This material is distributed under the GNU General Public License + * Version 2. You may review the terms of this license at + * http://www.gnu.org/licenses/gpl-2.0.html + * + * Copyright (c) 2006, Simon Urbanek + * Copyright (c) 2018, Oracle and/or its affiliates + * + * All rights reserved. + */ + +// :tabSize=2:indentSize=2:noTabs=false:folding=explicit:collapseFolds=1: + +import java.lang.reflect.Array ; + +/** + * Builds rectangular java arrays + */ +public class RectangularArrayBuilder extends RJavaArrayIterator { + + // {{{ constructors + /** + * constructor + * + * @param payload one dimensional array + * @param dimensions target dimensions + * @throws NotAnArrayException if payload is not an array + */ + public RectangularArrayBuilder( Object payload, int[] dimensions) throws NotAnArrayException, ArrayDimensionException { + + super( dimensions ) ; + if( !RJavaArrayTools.isArray(payload) ){ + throw new NotAnArrayException( payload.getClass() ) ; + } + if( !RJavaArrayTools.isSingleDimensionArray(payload)){ + throw new ArrayDimensionException( "not a single dimension array : " + payload.getClass() ) ; + } + + if( dimensions.length == 1 ){ + array = payload ; + } else{ + + String typeName = RJavaArrayTools.getObjectTypeName( payload ); + Class clazz = null ; + try{ + clazz = RJavaArrayTools.getClassForSignature( typeName, payload.getClass().getClassLoader() ); + } catch( ClassNotFoundException e){/* should not happen */} + + array = Array.newInstance( clazz , dimensions ) ; + if( typeName.equals( "I" ) ){ + fill_int( (int[])payload ) ; + } else if( typeName.equals( "Z" ) ){ + fill_boolean( (boolean[])payload ) ; + } else if( typeName.equals( "B" ) ){ + fill_byte( (byte[])payload ) ; + } else if( typeName.equals( "J" ) ){ + fill_long( (long[])payload ) ; + } else if( typeName.equals( "S" ) ){ + fill_short( (short[])payload ) ; + } else if( typeName.equals( "D" ) ){ + fill_double( (double[])payload ) ; + } else if( typeName.equals( "C" ) ){ + fill_char( (char[])payload ) ; + } else if( typeName.equals( "F" ) ){ + fill_float( (float[])payload ) ; + } else{ + fill_Object( (Object[])payload ) ; + } + + } + } + public RectangularArrayBuilder( Object payload, int length ) throws NotAnArrayException, ArrayDimensionException{ + this( payload, new int[]{ length } ) ; + } + + // java < 1.5 kept happy + public RectangularArrayBuilder(int x , int[] dim ) throws NotAnArrayException { throw new NotAnArrayException("primitive type : int ") ; } + public RectangularArrayBuilder(boolean x, int[] dim ) throws NotAnArrayException { throw new NotAnArrayException("primitive type : boolean ") ; } + public RectangularArrayBuilder(byte x , int[] dim ) throws NotAnArrayException { throw new NotAnArrayException("primitive type : byte ") ; } + public RectangularArrayBuilder(long x , int[] dim ) throws NotAnArrayException { throw new NotAnArrayException("primitive type : long ") ; } + public RectangularArrayBuilder(short x , int[] dim ) throws NotAnArrayException { throw new NotAnArrayException("primitive type : short ") ; } + public RectangularArrayBuilder(double x , int[] dim ) throws NotAnArrayException { throw new NotAnArrayException("primitive type : double ") ; } + public RectangularArrayBuilder(char x , int[] dim ) throws NotAnArrayException { throw new NotAnArrayException("primitive type : char ") ; } + public RectangularArrayBuilder(float x , int[] dim ) throws NotAnArrayException { throw new NotAnArrayException("primitive type : float ") ; } + + public RectangularArrayBuilder(int x , int length ) throws NotAnArrayException { throw new NotAnArrayException("primitive type : int ") ; } + public RectangularArrayBuilder(boolean x, int length ) throws NotAnArrayException { throw new NotAnArrayException("primitive type : boolean ") ; } + public RectangularArrayBuilder(byte x , int length ) throws NotAnArrayException { throw new NotAnArrayException("primitive type : byte ") ; } + public RectangularArrayBuilder(long x , int length ) throws NotAnArrayException { throw new NotAnArrayException("primitive type : long ") ; } + public RectangularArrayBuilder(short x , int length ) throws NotAnArrayException { throw new NotAnArrayException("primitive type : short ") ; } + public RectangularArrayBuilder(double x , int length ) throws NotAnArrayException { throw new NotAnArrayException("primitive type : double ") ; } + public RectangularArrayBuilder(char x , int length ) throws NotAnArrayException { throw new NotAnArrayException("primitive type : char ") ; } + public RectangularArrayBuilder(float x , int length ) throws NotAnArrayException { throw new NotAnArrayException("primitive type : float ") ; } + // }}} + + // {{{ fill_** + private void fill_int( int[] payload ){ + int k; + while( hasNext() ){ + int[] current = (int[])next() ; + k = start ; + for( int j=0; j<current.length; j++, k+=increment){ + current[j] = payload[k]; + } + } + } + + private void fill_boolean( boolean[] payload ){ + int k; + while( hasNext() ){ + boolean[] current = (boolean[])next() ; + k = start ; + for( int j=0; j<current.length; j++, k+=increment){ + current[j] = payload[k]; + } + } + } + + private void fill_byte( byte[] payload ){ + int k; + while( hasNext() ){ + byte[] current = (byte[])next() ; + k = start ; + for( int j=0; j<current.length; j++, k+=increment){ + current[j] = payload[k]; + } + } + } + + private void fill_long( long[] payload ){ + int k; + while( hasNext() ){ + long[] current = (long[])next() ; + k = start ; + for( int j=0; j<current.length; j++, k+=increment){ + current[j] = payload[k]; + } + } + } + + private void fill_short( short[] payload ){ + int k; + while( hasNext() ){ + short[] current = (short[])next() ; + k = start ; + for( int j=0; j<current.length; j++, k+=increment){ + current[j] = payload[k]; + } + } + } + + private void fill_double( double[] payload ){ + int k; + while( hasNext() ){ + double[] current = (double[])next() ; + k = start ; + for( int j=0; j<current.length; j++, k+=increment){ + current[j] = payload[k]; + } + } + } + + private void fill_char( char[] payload ){ + int k; + while( hasNext() ){ + char[] current = (char[])next() ; + k = start ; + for( int j=0; j<current.length; j++, k+=increment){ + current[j] = payload[k]; + } + } + } + + private void fill_float( float[] payload ){ + int k; + while( hasNext() ){ + float[] current = (float[])next() ; + k = start ; + for( int j=0; j<current.length; j++, k+=increment){ + current[j] = payload[k]; + } + } + } + + private void fill_Object( Object[] payload ){ + int k; + while( hasNext() ){ + Object[] current = (Object[])next() ; + k = start ; + for( int j=0; j<current.length; j++, k+=increment){ + current[j] = payload[k]; + } + } + } + + // }}} + +} + diff --git a/com.oracle.truffle.r.pkgs/rJava/src/java/RectangularArrayExamples.java b/com.oracle.truffle.r.pkgs/rJava/src/java/RectangularArrayExamples.java new file mode 100644 index 0000000000000000000000000000000000000000..37221045298aacf25d39d9c663ad2fc8661b5ffb --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/src/java/RectangularArrayExamples.java @@ -0,0 +1,265 @@ +/* + * This material is distributed under the GNU General Public License + * Version 2. You may review the terms of this license at + * http://www.gnu.org/licenses/gpl-2.0.html + * + * Copyright (c) 2006, Simon Urbanek + * Copyright (c) 2018, Oracle and/or its affiliates + * + * All rights reserved. + */ + +// :tabSize=2:indentSize=2:noTabs=false:folding=explicit:collapseFolds=1: + +/** + * Utility class that makes example rectangular java arrays of 2 and 3 dimensions + * for all primitive types, String and Point (as an example of array of non primitive object) + */ +public class RectangularArrayExamples { + + // {{{ Example 2d rect arrays + public static int[][] getIntDoubleRectangularArrayExample(){ + int[][] x = new int[5][2]; + int k= 0; + for( int j=0; j<2; j++){ + for( int i=0; i<5; i++, k++){ + x[i][j] = k ; + } + } + return x; + } + + public static boolean[][] getBooleanDoubleRectangularArrayExample(){ + boolean[][] x = new boolean[5][2]; + boolean current = false; + for( int j=0; j<2; j++){ + for( int i=0; i<5; i++, current = !current){ + x[i][j] = current ; + } + } + return x ; + } + + public static byte[][] getByteDoubleRectangularArrayExample(){ + byte[][] x = new byte[5][2]; + int k= 0; + for( int j=0; j<2; j++){ + for( int i=0; i<5; i++, k++){ + x[i][j] = (byte)k ; + } + } + return x; + } + + public static long[][] getLongDoubleRectangularArrayExample(){ + long[][] x = new long[5][2]; + int k= 0; + for( int j=0; j<2; j++){ + for( int i=0; i<5; i++, k++){ + x[i][j] = (long)k ; + } + } + return x; + } + + public static short[][] getShortDoubleRectangularArrayExample(){ + short[][] x = new short[5][2]; + int k= 0; + for( int j=0; j<2; j++){ + for( int i=0; i<5; i++, k++){ + x[i][j] = (short)k ; + } + } + return x; + } + + public static double[][] getDoubleDoubleRectangularArrayExample(){ + double[][] x = new double[5][2]; + int k= 0; + for( int j=0; j<2; j++){ + for( int i=0; i<5; i++, k++){ + x[i][j] = k + 0.0 ; + } + } + return x; + } + + public static char[][] getCharDoubleRectangularArrayExample(){ + char[][] x = new char[5][2]; + int k= 0; + for( int j=0; j<2; j++){ + for( int i=0; i<5; i++, k++){ + x[i][j] = (char)k ; + } + } + return x; + } + + public static float[][] getFloatDoubleRectangularArrayExample(){ + float[][] x = new float[5][2]; + int k= 0; + for( int j=0; j<2; j++){ + for( int i=0; i<5; i++, k++){ + x[i][j] = k + 0.0f ; + } + } + return x; + } + + public static String[][] getStringDoubleRectangularArrayExample(){ + String[][] x = new String[5][2]; + int k= 0; + for( int j=0; j<2; j++){ + for( int i=0; i<5; i++, k++){ + x[i][j] = "" + k ; + } + } + return x; + } + + public static DummyPoint[][] getDummyPointDoubleRectangularArrayExample(){ + DummyPoint[][] x = new DummyPoint[5][2]; + int k= 0; + for( int j=0; j<2; j++){ + for( int i=0; i<5; i++, k++){ + x[i][j] = new DummyPoint(k,k) ; + } + } + return x; + } + // }}} + + // {{{ Example 3d rect arrays + public static int[][][] getIntTripleRectangularArrayExample(){ + int[][][] x = new int[5][3][2]; + int current = 0 ; + for( int k=0; k<2; k++){ + for( int j=0; j<3; j++){ + for( int i=0; i<5; i++, current++){ + x[i][j][k] = current ; + } + } + } + return x; + } + + public static boolean[][][] getBooleanTripleRectangularArrayExample(){ + boolean[][][] x = new boolean[5][3][2]; + boolean current = false ; + for( int k=0; k<2; k++){ + for( int j=0; j<3; j++){ + for( int i=0; i<5; i++, current= !current){ + x[i][j][k] = current ; + } + } + } + return x; + } + + public static byte[][][] getByteTripleRectangularArrayExample(){ + byte[][][] x = new byte[5][3][2]; + int current = 0 ; + for( int k=0; k<2; k++){ + for( int j=0; j<3; j++){ + for( int i=0; i<5; i++, current++){ + x[i][j][k] = (byte)current ; + } + } + } + return x; + } + + public static long[][][] getLongTripleRectangularArrayExample(){ + long[][][] x = new long[5][3][2]; + int current = 0 ; + for( int k=0; k<2; k++){ + for( int j=0; j<3; j++){ + for( int i=0; i<5; i++, current++){ + x[i][j][k] = (long)current ; + } + } + } + return x; + } + + public static short[][][] getShortTripleRectangularArrayExample(){ + short[][][] x = new short[5][3][2]; + int current = 0 ; + for( int k=0; k<2; k++){ + for( int j=0; j<3; j++){ + for( int i=0; i<5; i++, current++){ + x[i][j][k] = (short)current ; + } + } + } + return x; + } + + public static double[][][] getDoubleTripleRectangularArrayExample(){ + double[][][] x = new double[5][3][2]; + int current = 0 ; + for( int k=0; k<2; k++){ + for( int j=0; j<3; j++){ + for( int i=0; i<5; i++, current++){ + x[i][j][k] = 0.0 + current ; + } + } + } + return x; + } + + public static char[][][] getCharTripleRectangularArrayExample(){ + char[][][] x = new char[5][3][2]; + int current = 0 ; + for( int k=0; k<2; k++){ + for( int j=0; j<3; j++){ + for( int i=0; i<5; i++, current++){ + x[i][j][k] = (char)current ; + } + } + } + return x; + } + + public static float[][][] getFloatTripleRectangularArrayExample(){ + float[][][] x = new float[5][3][2]; + int current = 0 ; + for( int k=0; k<2; k++){ + for( int j=0; j<3; j++){ + for( int i=0; i<5; i++, current++){ + x[i][j][k] = 0.0f + current ; + } + } + } + return x; + } + + public static String[][][] getStringTripleRectangularArrayExample(){ + String[][][] x = new String[5][3][2]; + int current = 0 ; + for( int k=0; k<2; k++){ + for( int j=0; j<3; j++){ + for( int i=0; i<5; i++, current++){ + x[i][j][k] = ""+current ; + } + } + } + return x; + } + + public static DummyPoint[][][] getDummyPointTripleRectangularArrayExample(){ + DummyPoint[][][] x = new DummyPoint[5][3][2]; + int current = 0 ; + for( int k=0; k<2; k++){ + for( int j=0; j<3; j++){ + for( int i=0; i<5; i++, current++){ + x[i][j][k] = new DummyPoint( current, current ) ; + } + } + } + return x; + } + + // }}} + +} diff --git a/com.oracle.truffle.r.pkgs/rJava/src/java/RectangularArraySummary.java b/com.oracle.truffle.r.pkgs/rJava/src/java/RectangularArraySummary.java new file mode 100644 index 0000000000000000000000000000000000000000..686a6c259f0475b1f591caf0b91638b5d1675fb2 --- /dev/null +++ b/com.oracle.truffle.r.pkgs/rJava/src/java/RectangularArraySummary.java @@ -0,0 +1,280 @@ +/* + * This material is distributed under the GNU General Public License + * Version 2. You may review the terms of this license at + * http://www.gnu.org/licenses/gpl-2.0.html + * + * Copyright (c) 2006, Simon Urbanek + * Copyright (c) 2018, Oracle and/or its affiliates + * + * All rights reserved. + */ + +// :tabSize=2:indentSize=2:noTabs=false:folding=explicit:collapseFolds=1: + +import java.lang.reflect.Array ; + +/** + * Utility class to extract something from a rectangular array + */ +public class RectangularArraySummary extends RJavaArrayIterator { + + private int length ; + + private String typeName ; + + private boolean isprimitive ; + + private Class componentType ; + + /** + * Constructor + * + * @param array the array to check + * @throws NotAnArrayException if array is not an array + */ + public RectangularArraySummary(Object array, int[] dimensions) throws NotAnArrayException, NotComparableException { + super( dimensions ); + this.array = array ; + typeName = RJavaArrayTools.getObjectTypeName(array ); + isprimitive = RJavaArrayTools.isPrimitiveTypeName( typeName ) ; + try{ + componentType = RJavaArrayTools.getClassForSignature( typeName , array.getClass().getClassLoader() ) ; + } catch( ClassNotFoundException e){} + checkComparableObjects() ; + } + + public RectangularArraySummary(Object array, int length ) throws NotAnArrayException, NotComparableException{ + this( array, new int[]{ length } ); + } + + /** + * Iterates over the array to find the minimum value + * (in the sense of the Comparable interface) + */ + public Object min( boolean narm ) { + if( isprimitive ){ + return null ; // TODO :implement + } + Object smallest = null ; + Object current ; + boolean found = false ; + + if( dimensions.length == 1 ){ + return( min( (Object[])array, narm ) ) ; + } else{ + + /* need to iterate */ + while( hasNext() ){ + current = min( (Object[])next(), narm ) ; + if( current == null ){ + if( !narm ) return null ; + } else{ + if( !found ){ + smallest = current ; + found = true ; + } else if( smaller( current, smallest ) ) { + smallest = current ; + } + } + } + return smallest ; + } + + } + + /** + * Iterates over the array to find the maximum value + * (in the sense of the Comparable interface) + */ + public Object max( boolean narm ) { + if( isprimitive ){ + return null ; // TODO :implement + } + + Object biggest = null ; + Object current ; + boolean found = false ; + + if( dimensions.length == 1 ){ + return( max( (Object[])array, narm ) ) ; + } else{ + + /* need to iterate */ + while( hasNext() ){ + current = max( (Object[])next(), narm ) ; + if( current == null ){ + if( !narm ) return null ; + } else{ + if( !found ){ + biggest = current ; + found = true ; + } else if( bigger( current, biggest) ){ + biggest = current ; + } + } + } + return biggest ; + } + + } + + /** + * Iterates over the array to find the range of the java array + * (in the sense of the Comparable interface) + */ + public Object[] range( boolean narm ) { + if( isprimitive ){ + return null ; // TODO :implement + } + + if( dimensions.length == 1 ){ + return( range( (Object[])array, narm ) ) ; + } else{ + + Object[] range = null ; + Object[] current ; + boolean found = false ; + + /* need to iterate */ + while( hasNext() ){ + current = range( (Object[])next(), narm ) ; + if( current == null ){ + if( !narm ) return null ; + } else{ + if( !found ){ + range = current ; + found = true ; + } else { + if( bigger( current[1], range[1] ) ){ + range[1] = current[1] ; + } + if( smaller( current[0], range[0] ) ){ + range[0] = current[0] ; + } + + } + } + } + return range ; + } + + } + + + + + /** + * returns the minimum (in the sense of Comparable) of the + * objects in the one dimensioned array + */ + private static Object min( Object[] x, boolean narm ){ + + int n = x.length ; + Object smallest = null ; + Object current ; + boolean found_min = false; + + // find somewhere to start from () + for( int i=0; i<n; i++){ + current = x[i] ; + if( current == null ){ + if( !narm ) return null ; + } else{ + if( !found_min ){ + smallest = current ; + found_min = true ; + } else if( ((Comparable)smallest).compareTo(current) > 0 ) { + smallest = current ; + } + } + } + return smallest ; + + } + + /** + * returns the minimum (in the sense of Comparable) of the + * objects in the one dimensioned array + */ + private static Object max( Object[] x, boolean narm ){ + + int n = x.length ; + Object biggest = null ; + Object current ; + boolean found_min = false; + + // find somewhere to start from () + for( int i=0; i<n; i++){ + current = x[i] ; + if( current == null ){ + if( !narm ) return null ; + } else{ + if( !found_min ){ + biggest = current ; + found_min = true ; + } else if( ((Comparable)biggest).compareTo(current) < 0 ) { + biggest = current ; + } + } + } + return biggest ; + + } + + /** + * returns the range (in the sense of Comparable) of the + * objects in the one dimensioned array + */ + private static Object[] range( Object[] x, boolean narm ){ + + int n = x.length ; + Object[] range = (Object[])Array.newInstance( x.getClass().getComponentType(), 2) ; + Object current ; + boolean found = false; + + // find somewhere to start from () + for( int i=0; i<n; i++){ + current = x[i] ; + if( current == null ){ + if( !narm ) return null ; + } else{ + if( !found ){ + range[0] = current ; + range[1] = current ; + found = true ; + } else { + // max + if( ((Comparable)range[1]).compareTo(current) < 0 ) { + range[1] = current ; + } + // min + if( ((Comparable)range[0]).compareTo(current) > 0 ) { + range[0] = current ; + } + + } + } + } + return range ; + + } + + public void checkComparableObjects() throws NotComparableException { + if( ! containsComparableObjects() ) throw new NotComparableException( typeName ) ; + } + + public boolean containsComparableObjects(){ + return Comparable.class.isAssignableFrom( componentType ) ; + } + + // TODO : use these + private static boolean smaller( Object x, Object y){ + return ( (Comparable)x ).compareTo(y) < 0 ; + } + private static boolean bigger( Object x, Object y){ + return ( (Comparable)x ).compareTo(y) > 0 ; + } + + +} + diff --git a/com.oracle.truffle.r.pkgs/rJava/tests/testthat.R b/com.oracle.truffle.r.pkgs/rJava/tests/testthat.R deleted file mode 100644 index 46b38886ec61b333475eda982a77f68ac0bdd59a..0000000000000000000000000000000000000000 --- a/com.oracle.truffle.r.pkgs/rJava/tests/testthat.R +++ /dev/null @@ -1,5 +0,0 @@ -library(testthat) -library(rjava) - -test_check("rjava") -typeName \ No newline at end of file diff --git a/com.oracle.truffle.r.pkgs/rJava/tests/testthat/testArrays.R b/com.oracle.truffle.r.pkgs/rJava/tests/testthat/testArrays.R deleted file mode 100644 index 1fb95666c0d118d0b40b88a009f4a5f3f1051f61..0000000000000000000000000000000000000000 --- a/com.oracle.truffle.r.pkgs/rJava/tests/testthat/testArrays.R +++ /dev/null @@ -1,104 +0,0 @@ -# prerequisites: -# - 'testthat' package has to be installed: install.packages("testthat") -# - FastR`s rJava package has to be installed: bin/r CMD INSTALL com.oracle.truffle.r.pkgs/rjava - -library(testthat) -library(rJava) - -.jaddClassPath(paste0(Sys.getenv("R_HOME"), "/mxbuild/dists/fastr-unit-tests.jar")) - -testName <- "test .jarray" -test_that(testName, { - cat(paste0(testName, "\n")) - - a <- .jarray(c(1.1, 2.1, 3.1)) - expect_true(is.external.array(a)) - expect_equal(length(a), 3) - expect_equal(a[1], c(1.1)) - expect_equal(a[2], c(2.1)) - expect_equal(a[3], c(3.1)) - - a <- .jarray(c(1L, 2L, 3L)) - expect_true(is.external.array(a)) - expect_equal(length(a), 3) - expect_equal(a[1], c(1)) - expect_equal(a[2], c(2)) - expect_equal(a[3], c(3)) - - a <- .jarray(c(TRUE, FALSE)) - expect_true(is.external.array(a)) - expect_equal(length(a), 2) - expect_equal(a[1], TRUE) - expect_equal(a[2], FALSE) - - a <- .jarray(c(.jbyte(1), .jchar("a"), .jfloat(1.1), .jlong(2), .jshort(123))) - expect_true(is.external.array(a)) - expect_equal(length(a), 5) - expect_equal(a[1], 1) - expect_equal(a[2], "a") - expect_true((a[3] - 1.1)^2 < 1e-8) - expect_equal(a[4], 2) - expect_equal(a[5], 123) - - to <- .jnew('java.util.ArrayList') - a <- .jarray(to) - expect_true(is.external.array(a)) - expect_equal(length(a), 1) - # fails at the moment - # testthat passes 'to' into .Call("find_label_", quote(to), environment()) - # expect_equal(a[1], to) - - to <- .jnew('java.util.ArrayList') - a <- .jarray(c(to, to)) - expect_true(is.external.array(a)) - # fails at the moment - # expect_equal(length(a), 2) - # expect_equal(a[1], to) - # expect_equal(a[2], to) - - a <- .jarray(list(1, 2, 3)) - expect_true(is.external.array(a)) - expect_equal(length(a), 3) - expect_equal(a[1], 1) - expect_equal(a[2], 2) - expect_equal(a[3], 3) -}) - -testName <- "test .jevalArray" -test_that(testName, { - cat(paste0(testName, "\n")) - - expectedValues <- list( - Boolean=list("logical", TRUE, FALSE, TRUE), - Byte=list("integer", 1,2,3), - Char=list("character", "a", "b", "c"), - Double=list("double",1.1, 2.1, 3.1), - Float=list("double", 1.1, 2.1, 3.1), - Integer=list("integer",1,2,3), - Long=list("double", 1,2,3), - Short=list("integer",1,2,3), - String=list("character", "a", "b", "c")) - testClassName <- "com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass" - t<-.jnew(class=testClassName) - - for(expectedName in names(expectedValues)) { - fieldName <- paste0("fieldStatic", expectedName, "Array") - ev<-expectedValues[expectedName][[1]] - arrayType <- ev[[1]] - arrayLength <- length(ev) - 1 - a<-t[fieldName] - expect_true(is.external.array(a), info=paste0("the array was returned for ", fieldName), label="is.external.array") - ae<-.jevalArray(a) - expect_true(is.vector(ae), info=paste0("the array was returned for ", fieldName), label="is.vector") - expect_equal(typeof(ae), arrayType, info=paste0("the array was returned for ", fieldName), label="typeof") - expect_equal(length(ae), arrayLength, info=paste0("the array was returned for ", fieldName), label="array length") - for(i in 1:arrayLength) { - if(expectedName != "Float") { - expect_equal(a[i], ev[[i+1]], info=paste0("the array was returned for ", fieldName), label=paste0("value at", i)) - } else { - expect_true((ev[[i+1]] - a[i])^2 < 1e-8, info=paste0("the array was returned for ", fieldName), label=paste0("value at", i)) - } - } - } - -}) diff --git a/com.oracle.truffle.r.pkgs/rJava/tests/testthat/testBasic.R b/com.oracle.truffle.r.pkgs/rJava/tests/testthat/testBasic.R deleted file mode 100644 index 1062c93039bbe8c78bbe2bb8bb816a81bf4bce15..0000000000000000000000000000000000000000 --- a/com.oracle.truffle.r.pkgs/rJava/tests/testthat/testBasic.R +++ /dev/null @@ -1,135 +0,0 @@ -# prerequisites: -# - 'testthat' package has to be installed: install.packages("testthat") -# - FastR`s rJava package has to be installed: bin/r CMD INSTALL com.oracle.truffle.r.pkgs/rjava - -library(testthat) -library(rJava) -.jaddClassPath(paste0(Sys.getenv("R_HOME"), "/mxbuild/dists/fastr-unit-tests.jar")) - -bo <- TRUE -bt <- 123 -ch <- 'a' -d <- 1.123456 -f <- 1.123 -i <- 12345L -l <- 123456 -sh <- 1234 -st <- "a test string" - -jbt <- .jbyte(bt) -jch <- .jchar(ch) -jf <- .jfloat(f) -jl <- .jlong(l) -jsh <- .jshort(sh) - -primitiveTypeNames <- c("Boolean", "Byte", "Char", "Double", "Float", "Integer", "Long", "Short") -expectedValues <- list(bo, bt, ch, d, f, i, l, sh, st) - -testClassName <- "com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass" -t<-.jnew(class=testClassName, bo, jbt, jch, d, jf, i, jl, jsh, st) - -testForMember <- function(valueProvider, memberNameProvider, typeNames) { - for(idx in 1:length(typeNames)) { - typeName <- typeNames[idx] - member <- memberNameProvider(typeName) - expectedValue <- expectedValues[[idx]] - value <- valueProvider(t, member) - testValue(member, typeName, expectedValue, value) - } -} - -testValue <- function(memberName, typeName, expectedValue, value) { - cat(paste0(" ", memberName, " returned value [", value, "] and is expected to be [", expectedValue, "]"), "\n") - if(typeName != "Float") { - expect_that(expectedValue, equals(value), info=memberName, label=memberName) - } else { - expect_true((expectedValue - value)^2 < 1e-8, info=memberName, label=memberName) - } -} - -testName <- "test if primitive field access works" -test_that(testName, { - cat(paste0(testName, "\n")) - testForMember( valueProvider = function(t, member) { .jfield(t, , member) }, - memberNameProvider = function(typeName) { paste0("field", typeName) }, - primitiveTypeNames ) -}) - -testName <- "test if primitive static field access works" -test_that(testName, { - cat(paste0(testName, "\n")) - testForMember( valueProvider = function(t, member) { .jfield(t, , member) }, - memberNameProvider = function(typeName) { paste0("fieldStatic", typeName) }, - primitiveTypeNames ) -}) - -testName <- "test if static field access works" -test_that("test if static field access works", { - testForMember( valueProvider = function(t, member) { .jfield(t, , member) }, - memberNameProvider = function(typeName) { paste0("fieldStatic", typeName, "Object") }, - c(primitiveTypeNames, "String") ) -}) - -testName <- "test if field access works" -test_that(testName, { - cat(paste0(testName, "\n")) - testForMember( valueProvider = function(t, member) { .jfield(t, , member) }, - memberNameProvider = function(typeName) { paste0("field", typeName, "Object")}, - c(primitiveTypeNames, "String") ) -}) - -testName <- "test if calling static method returning primitive values works" -test_that(testName, { - cat(paste0(testName, "\n")) - testForMember( valueProvider = function(t, member) { .jcall(t, , member) }, - memberNameProvider = function(typeName) { paste0("methodStatic", typeName) }, - primitiveTypeNames ) -}) - -testName <- "test if calling method returning primitive values works" -test_that(testName, { - cat(paste0(testName, "\n")) - testForMember( valueProvider = function(t, member) { .jcall(t, , member) }, - memberNameProvider = function(typeName) { paste0("method", typeName) }, - primitiveTypeNames ) -}) - -testName <- "test if calling static method returning object values works" -test_that(testName, { - cat(paste0(testName, "\n")) - testForMember( valueProvider = function(t, member) { .jcall(t, , member) }, - memberNameProvider = function(typeName) { paste0("methodStatic", typeName, "Object") }, - c(primitiveTypeNames, "String") ) -}) - -testName <- "test if calling method returning object values works" -test_that(testName, { - cat(paste0(testName, "\n")) - testForMember( valueProvider = function(t, member) { .jcall(t, , member) }, - memberNameProvider = function(typeName) { paste0("method", typeName, "Object") }, - c(primitiveTypeNames, "String") ) -}) - -testName <- "test if static access via class name works" -test_that(testName, { - cat(paste0(testName, "\n")) - value <- .jfield(testClassName, , "fieldStaticInteger") - testValue("fieldStaticInteger", "Integer", i, value) - - value <- .jcall(testClassName, , "methodStaticInteger") - testValue("methodStaticInteger", "Integer", i, value) -}) - -testName <- "test if calling method with all primitive type parameters + string works" -test_that(testName, { - cat(paste0(testName, "\n")) - value <- .jcall(t, , "allTypesMethod", bo, jbt, jch, d, jf, i, jl, jsh, st) - expect_false(is.null(value)) -}) - -testName <- "test if calling method with all primitive type parameters + string works" -test_that(testName, { - cat(paste0(testName, "\n")) - value <- .jcall(t, , "allTypesStaticMethod", bo, jbt, jch, d, jf, i, jl, jsh, st) - expect_false(is.null(value)) -}) \ No newline at end of file diff --git a/com.oracle.truffle.r.pkgs/rJava/tests/testthat/testJ.R b/com.oracle.truffle.r.pkgs/rJava/tests/testthat/testJ.R deleted file mode 100644 index a406765bfd11c9486d53507445846d5dfbfda577..0000000000000000000000000000000000000000 --- a/com.oracle.truffle.r.pkgs/rJava/tests/testthat/testJ.R +++ /dev/null @@ -1,21 +0,0 @@ -# prerequisites: -# - 'testthat' package has to be installed: install.packages("testthat") -# - FastR`s rJava package has to be installed: bin/r CMD INSTALL com.oracle.truffle.r.pkgs/rjava - -library(testthat) -library(rJava) - -.jaddClassPath(paste0(Sys.getenv("R_HOME"), "/mxbuild/dists/fastr-unit-tests.jar")) - -testName <- "test J function" -test_that(testName, { - cat(paste0(testName, "\n")) - tc <- J("com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass") - expect_equal(2147483647L, tc$fieldStaticInteger) - - tc <- J("com/oracle/truffle/r/test/library/fastr/TestJavaInterop$TestClass") - expect_equal(2147483647L, tc$fieldStaticInteger) - - expect_equal(2147483647L, J("com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass", "methodStaticInteger")) - expect_equal(2147483647L, J("com/oracle/truffle/r/test/library/fastr/TestJavaInterop$TestClass", "methodStaticInteger")) -}) 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 531d285ffd8af586e24062ab1bda8a3a70e48fd0..376ba865e2ab3799c5ab434ed3821fb517e7fbe1 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.tck/src/com/oracle/truffle/r/test/tck/RTCKLanguageProvider.java b/com.oracle.truffle.r.test.tck/src/com/oracle/truffle/r/test/tck/RTCKLanguageProvider.java index 90561ebb5c8c32bf8e7f87c4beebbab9ee45736c..9e79652192acdda8ac5de6311e4e067a54fffc45 100644 --- a/com.oracle.truffle.r.test.tck/src/com/oracle/truffle/r/test/tck/RTCKLanguageProvider.java +++ b/com.oracle.truffle.r.test.tck/src/com/oracle/truffle/r/test/tck/RTCKLanguageProvider.java @@ -143,6 +143,7 @@ public final class RTCKLanguageProvider implements LanguageProvider { ops.add(createBinaryOperator(context, "/", numOrBoolOrArrNumBool, numOrBoolOrNullOrArrNumBool, numOrBoolOrNullOrArrNumBool, RResultVerifier.newBuilder(numOrBoolOrNullOrArrNumBool, numOrBoolOrNullOrArrNumBool).emptyArrayCheck().build())); + // TODO cause occasional and more then accasional fails on gate builds // < // ops.add(createBinaryOperator(context, "<", boolOrArrBool, strOrNumOrBoolOrArrStrNumBool, // strOrNumOrBoolOrArrStrNumBool, 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 38955f0d5d51209d884f310d4dc15a09ec383c31..28e719055c235dd5c8ca7d1063e7c0edaeee97f6 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 diff --git a/mx.fastr/copyrights/overrides b/mx.fastr/copyrights/overrides index 100cb2cf768764ba0bb0c3975809c57fd4cc3398..7b0d3d3e6faed594cd935beb1abb230ed702db62 100644 --- a/mx.fastr/copyrights/overrides +++ b/mx.fastr/copyrights/overrides @@ -953,3 +953,21 @@ com.oracle.truffle.r.native/gnur/patch/src/nmath/nmath.h,no.copyright com.oracle.truffle.r.native/fficall/src/common/rffi_upcalls.h,no.copyright com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/REnvironmentMRTest.java,no.copyright com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/VectorMRTest.java,no.copyright +com.oracle.truffle.r.pkgs/rJava/src/java/ArrayDimensionException.java,rjava.copyright +com.oracle.truffle.r.pkgs/rJava/src/java/ArrayWrapper.java,rjava.copyright +com.oracle.truffle.r.pkgs/rJava/src/java/DummyPoint.java,rjava.copyright +com.oracle.truffle.r.pkgs/rJava/src/java/FlatException.java,rjava.copyright +com.oracle.truffle.r.pkgs/rJava/src/java/Makefile,rjava.copyright +com.oracle.truffle.r.pkgs/rJava/src/java/NotAnArrayException.java,rjava.copyright +com.oracle.truffle.r.pkgs/rJava/src/java/NotComparableException.java,rjava.copyright +com.oracle.truffle.r.pkgs/rJava/src/java/ObjectArrayException.java,rjava.copyright +com.oracle.truffle.r.pkgs/rJava/src/java/PrimitiveArrayException.java,rjava.copyright +com.oracle.truffle.r.pkgs/rJava/src/java/RJavaArrayIterator.java,rjava.copyright +com.oracle.truffle.r.pkgs/rJava/src/java/RJavaArrayTools.java,rjava_romain.copyright +com.oracle.truffle.r.pkgs/rJava/src/java/RJavaClassLoader.java,rjava.copyright +com.oracle.truffle.r.pkgs/rJava/src/java/RJavaComparator.java,rjava.copyright +com.oracle.truffle.r.pkgs/rJava/src/java/RJavaImport.java,rjava.copyright +com.oracle.truffle.r.pkgs/rJava/src/java/RJavaTools.java,rjava_romain.copyright +com.oracle.truffle.r.pkgs/rJava/src/java/RectangularArrayBuilder.java,rjava.copyright +com.oracle.truffle.r.pkgs/rJava/src/java/RectangularArrayExamples.java,rjava.copyright +com.oracle.truffle.r.pkgs/rJava/src/java/RectangularArraySummary.java,rjava.copyright \ No newline at end of file diff --git a/mx.fastr/copyrights/rjava.copyright.hash b/mx.fastr/copyrights/rjava.copyright.hash new file mode 100644 index 0000000000000000000000000000000000000000..9e3b79043d75b8c32b937a85630cf9b0ceccd738 --- /dev/null +++ b/mx.fastr/copyrights/rjava.copyright.hash @@ -0,0 +1,10 @@ +## + # This material is distributed under the GNU General Public License + # Version 2. You may review the terms of this license at + # http://www.gnu.org/licenses/gpl-2.0.html + # + # Copyright (c) 2006, Simon Urbanek + # Copyright (c) 2018, Oracle and/or its affiliates + # + # All rights reserved. +## diff --git a/mx.fastr/copyrights/rjava.copyright.hash.regex b/mx.fastr/copyrights/rjava.copyright.hash.regex new file mode 100644 index 0000000000000000000000000000000000000000..c20f92dbc17b5a9ff1aa828011be693afb3843de --- /dev/null +++ b/mx.fastr/copyrights/rjava.copyright.hash.regex @@ -0,0 +1 @@ +##\n # This material is distributed under the GNU General Public License\n # Version 2. You may review the terms of this license at\n # http://www.gnu.org/licenses/gpl-2.0.html\n #\n # Copyright \(c\) 2006, Simon Urbanek\n # Copyright \(c\) (?:(20[0-9][0-9]), )?(20[0-9][0-9]), Oracle and/or its affiliates\n #\n # All rights reserved.\n##\n.* \ No newline at end of file diff --git a/mx.fastr/copyrights/rjava.copyright.star b/mx.fastr/copyrights/rjava.copyright.star new file mode 100644 index 0000000000000000000000000000000000000000..21d71837814993fe3c6f5ad0425fee8c58c1fde8 --- /dev/null +++ b/mx.fastr/copyrights/rjava.copyright.star @@ -0,0 +1,10 @@ +/* + * This material is distributed under the GNU General Public License + * Version 2. You may review the terms of this license at + * http://www.gnu.org/licenses/gpl-2.0.html + * + * Copyright (c) 2006, Simon Urbanek + * Copyright (c) 2018, Oracle and/or its affiliates + * + * All rights reserved. + */ diff --git a/mx.fastr/copyrights/rjava.copyright.star.regex b/mx.fastr/copyrights/rjava.copyright.star.regex new file mode 100644 index 0000000000000000000000000000000000000000..40ec1b6bfc0a660f1d4355e816cb333cb06f392a --- /dev/null +++ b/mx.fastr/copyrights/rjava.copyright.star.regex @@ -0,0 +1 @@ +/\*\n \* This material is distributed under the GNU General Public License\n \* Version 2. You may review the terms of this license at\n \* http://www.gnu.org/licenses/gpl-2.0.html\n \*\n \* Copyright \(c\) 2006, Simon Urbanek\n \* Copyright \(c\) (?:(20[0-9][0-9]), )?(20[0-9][0-9]), Oracle and/or its affiliates\n \*\n \* All rights reserved.\n \*/\n.* \ No newline at end of file diff --git a/mx.fastr/copyrights/rjava_romain.copyright.star b/mx.fastr/copyrights/rjava_romain.copyright.star new file mode 100644 index 0000000000000000000000000000000000000000..e9d81b3a1c797033882ffbcc62568b9e837ba71e --- /dev/null +++ b/mx.fastr/copyrights/rjava_romain.copyright.star @@ -0,0 +1,10 @@ +/* + * This material is distributed under the GNU General Public License + * Version 2. You may review the terms of this license at + * http://www.gnu.org/licenses/gpl-2.0.html + * + * Copyright (c) 2009-2010, Simon Urbanek and Romain Francois + * Copyright (c) 2018, Oracle and/or its affiliates + * + * All rights reserved. + */ \ No newline at end of file diff --git a/mx.fastr/copyrights/rjava_romain.copyright.star.regex b/mx.fastr/copyrights/rjava_romain.copyright.star.regex new file mode 100644 index 0000000000000000000000000000000000000000..98390228623fb0ab663e87077fa7207d78ae522a --- /dev/null +++ b/mx.fastr/copyrights/rjava_romain.copyright.star.regex @@ -0,0 +1 @@ +/\*\n \* This material is distributed under the GNU General Public License\n \* Version 2. You may review the terms of this license at\n \* http://www.gnu.org/licenses/gpl-2.0.html\n \*\n \* Copyright \(c\) (?:(20[0-9][0-9])-)?(20[0-9][0-9]), Simon Urbanek and Romain Francois\n \* Copyright \(c\) (?:(20[0-9][0-9]), )?(20[0-9][0-9]), Oracle and/or its affiliates\n \*\n \* All rights reserved.\n \*/\n.* \ No newline at end of file