From 2eff92eda15264945c7af90290c366161ccc940f Mon Sep 17 00:00:00 2001 From: Mick Jordan <mick.jordan@oracle.com> Date: Fri, 5 Aug 2016 17:33:49 -0700 Subject: [PATCH] cast pipelines for conditions,connections,file,serialize,sys,etc., functions --- .../builtin/base/ConditionFunctions.java | 83 ++++-- .../r/nodes/builtin/base/DelayedAssign.java | 13 +- .../nodes/builtin/base/DynLoadFunctions.java | 66 ++-- .../r/nodes/builtin/base/FileFunctions.java | 281 ++++++++++-------- .../nodes/builtin/base/LoadSaveFunctions.java | 39 ++- .../builtin/base/NamespaceFunctions.java | 51 +++- .../r/nodes/builtin/base/NormalizePath.java | 5 - .../truffle/r/nodes/builtin/base/Quit.java | 21 +- .../r/nodes/builtin/base/RegFinalizer.java | 28 +- .../builtin/base/SerializeFunctions.java | 51 +++- .../r/nodes/builtin/base/SetTimeLimit.java | 11 +- .../truffle/r/nodes/builtin/base/Stop.java | 22 +- .../r/nodes/builtin/base/SysFunctions.java | 105 +++---- .../truffle/r/nodes/builtin/base/Warning.java | 36 +-- .../r/nodes/builtin/base/WithVisible.java | 29 +- .../com/oracle/truffle/r/runtime/RError.java | 14 +- 16 files changed, 493 insertions(+), 362 deletions(-) diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConditionFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConditionFunctions.java index b4b560584b..cd3e5e552c 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConditionFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConditionFunctions.java @@ -11,6 +11,7 @@ */ package com.oracle.truffle.r.nodes.builtin.base; +import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*; import static com.oracle.truffle.r.runtime.RErrorHandling.getHandlerStack; import static com.oracle.truffle.r.runtime.RVisibility.OFF; import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX; @@ -18,7 +19,6 @@ import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.r.nodes.builtin.CastBuilder; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; @@ -52,27 +52,29 @@ public class ConditionFunctions { @RBuiltin(name = ".addCondHands", visibility = OFF, kind = INTERNAL, parameterNames = {"classes", "handlers", "parentenv", "target", "calling"}, behavior = COMPLEX) public abstract static class AddCondHands extends RBuiltinNode { + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("classes").mustBe(nullValue().or(stringValue())).asStringVector(); + casts.arg("handlers").mustBe(nullValue().or(instanceOf(RList.class))); + casts.arg("calling").asLogicalVector().findFirst(); + } @SuppressWarnings("unused") @Specialization(guards = "isRNull(classes) || isRNull(handlers)") @TruffleBoundary protected Object addCondHands(Object classes, Object handlers, Object parentEnv, Object target, byte calling) { - RContext.getInstance().setVisible(false); return getHandlerStack(); } - @Specialization(guards = "classes.getLength() == handlers.getLength()") + @Specialization @TruffleBoundary protected Object addCondHands(RAbstractStringVector classes, RList handlers, REnvironment parentEnv, Object target, byte calling) { - RContext.getInstance().setVisible(false); + if (classes.getLength() != handlers.getLength()) { + throw RError.error(this, RError.Message.BAD_HANDLER_DATA); + } return RErrorHandling.createHandlers(classes, handlers, parentEnv, target, calling); } - @SuppressWarnings("unused") - @Fallback - protected Object fallback(Object classesObj, Object handlersObj, Object parentEnv, Object target, byte calling) { - throw RError.error(this, RError.Message.BAD_HANDLER_DATA); - } } @RBuiltin(name = ".resetCondHands", visibility = OFF, kind = INTERNAL, parameterNames = {"stack"}, behavior = COMPLEX) @@ -87,34 +89,39 @@ public class ConditionFunctions { } public abstract static class RestartAdapter extends RBuiltinNode { - public static boolean lengthok(Object restart) { - return (restart instanceof RList) && ((RList) restart).getLength() >= 2; + protected void checkLength(RList restart) { + if (restart.getLength() < 2) { + throw RError.error(this, RError.Message.BAD_RESTART); + } } - protected RError badRestart() throws RError { - throw RError.error(this, RError.Message.BAD_RESTART); + protected void restart(CastBuilder casts) { + casts.arg("restart").mustBe(instanceOf(RList.class), RError.Message.BAD_RESTART); } + } @RBuiltin(name = ".addRestart", kind = INTERNAL, parameterNames = "restart", behavior = COMPLEX) public abstract static class AddRestart extends RestartAdapter { + @Override + public void createCasts(CastBuilder casts) { + restart(casts); + } + @Specialization protected Object addRestart(RList restart) { + checkLength(restart); RErrorHandling.addRestart(restart); return RNull.instance; } - @Specialization(guards = "lengthok(restart)") - protected Object addRestart(@SuppressWarnings("unused") Object restart) { - throw badRestart(); - } } @RBuiltin(name = ".getRestart", kind = INTERNAL, parameterNames = "restart", behavior = COMPLEX) public abstract static class GetRestart extends RBuiltinNode { @Override protected void createCasts(CastBuilder casts) { - casts.toInteger(0); + casts.arg("restart").asIntegerVector().findFirst(); } @Specialization @@ -126,8 +133,14 @@ public class ConditionFunctions { @RBuiltin(name = ".invokeRestart", kind = INTERNAL, parameterNames = {"restart", "args"}, behavior = COMPLEX) public abstract static class InvokeRestart extends RestartAdapter { - @Specialization(guards = "lengthok(restart)") + @Override + public void createCasts(CastBuilder casts) { + restart(casts); + } + + @Specialization protected RNull invokeRestart(RList restart, Object args) { + checkLength(restart); if (RErrorHandling.invokeRestart(restart, args) == null) { throw RError.error(this, RError.Message.RESTART_NOT_ON_STACK); } else { @@ -135,11 +148,6 @@ public class ConditionFunctions { } } - @SuppressWarnings("unused") - @Fallback - protected Object invokeRestart(Object restart, Object args) { - throw badRestart(); - } } @RBuiltin(name = ".signalCondition", kind = INTERNAL, parameterNames = {"condition", "msg", "call"}, behavior = COMPLEX) @@ -161,28 +169,43 @@ public class ConditionFunctions { @RBuiltin(name = "seterrmessage", visibility = OFF, kind = INTERNAL, parameterNames = "msg", behavior = COMPLEX) public abstract static class Seterrmessage extends RBuiltinNode { + @Override + public void createCasts(CastBuilder casts) { + casts.arg("msg").defaultError(RError.Message.ERR_MSG_MUST_BE_STRING).mustBe(stringValue()).asStringVector().mustBe(size(1)).findFirst(); + } + @Specialization - protected RNull seterrmessage(RAbstractStringVector msg) { + protected RNull seterrmessage(String msg) { RContext.getInstance().setVisible(false); - RErrorHandling.seterrmessage(msg.getDataAt(0)); + RErrorHandling.seterrmessage(msg); return RNull.instance; } } @RBuiltin(name = ".dfltWarn", kind = INTERNAL, parameterNames = {"message", "call"}, behavior = COMPLEX) public abstract static class DfltWarn extends RBuiltinNode { + @Override + public void createCasts(CastBuilder casts) { + casts.arg("message").defaultError(RError.Message.ERR_MSG_BAD).mustBe(stringValue()).asStringVector().mustBe(size(1)).findFirst(); + } + @Specialization - protected RNull dfltWarn(RAbstractStringVector msg, Object call) { - RErrorHandling.dfltWarn(msg.getDataAt(0), call); + protected RNull dfltWarn(String msg, Object call) { + RErrorHandling.dfltWarn(msg, call); return RNull.instance; } } @RBuiltin(name = ".dfltStop", kind = INTERNAL, parameterNames = {"message", "call"}, behavior = COMPLEX) public abstract static class DfltStop extends RBuiltinNode { + @Override + public void createCasts(CastBuilder casts) { + casts.arg("message").defaultError(RError.Message.ERR_MSG_BAD).mustBe(stringValue()).asStringVector().mustBe(size(1)).findFirst(); + } + @Specialization - protected Object dfltStop(RAbstractStringVector message, Object call) { - RErrorHandling.dfltStop(message.getDataAt(0), call); + protected Object dfltStop(String message, Object call) { + RErrorHandling.dfltStop(message, call); return RNull.instance; } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DelayedAssign.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DelayedAssign.java index f168a9091a..1c12e42ec8 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DelayedAssign.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DelayedAssign.java @@ -22,6 +22,7 @@ */ package com.oracle.truffle.r.nodes.builtin.base; +import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*; import static com.oracle.truffle.r.runtime.RVisibility.OFF; import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX; import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL; @@ -30,6 +31,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.profiles.BranchProfile; import com.oracle.truffle.r.nodes.RASTUtils; +import com.oracle.truffle.r.nodes.builtin.CastBuilder; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.builtins.RBuiltin; @@ -37,7 +39,6 @@ import com.oracle.truffle.r.runtime.data.RDataFactory; import com.oracle.truffle.r.runtime.data.RNull; import com.oracle.truffle.r.runtime.data.RPromise.Closure; import com.oracle.truffle.r.runtime.data.RPromise.PromiseState; -import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector; import com.oracle.truffle.r.runtime.env.REnvironment; import com.oracle.truffle.r.runtime.env.REnvironment.PutException; @@ -46,10 +47,16 @@ public abstract class DelayedAssign extends RBuiltinNode { private final BranchProfile errorProfile = BranchProfile.create(); + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("x").mustBe(stringValue()).asStringVector().mustBe(notEmpty(), RError.Message.INVALID_FIRST_ARGUMENT).findFirst(); + casts.arg("eval.env").mustBe(nullValue().not(), RError.Message.USE_NULL_ENV_DEFUNCT).mustBe(instanceOf(REnvironment.class)); + casts.arg("assign.env").mustBe(nullValue().not(), RError.Message.USE_NULL_ENV_DEFUNCT).mustBe(instanceOf(REnvironment.class)); + } + @Specialization @TruffleBoundary - protected Object doDelayedAssign(RAbstractStringVector nameVec, Object value, REnvironment evalEnv, REnvironment assignEnv) { - String name = nameVec.getDataAt(0); + protected Object doDelayedAssign(String name, Object value, REnvironment evalEnv, REnvironment assignEnv) { try { assignEnv.put(name, RDataFactory.createPromise(PromiseState.Explicit, Closure.create(RASTUtils.createNodeForValue(value)), evalEnv.getFrame())); return RNull.instance; diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DynLoadFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DynLoadFunctions.java index 06b412f7ff..09557dc390 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DynLoadFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DynLoadFunctions.java @@ -22,6 +22,7 @@ */ package com.oracle.truffle.r.nodes.builtin.base; +import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*; import static com.oracle.truffle.r.runtime.RVisibility.OFF; import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX; import static com.oracle.truffle.r.runtime.builtins.RBehavior.READS_STATE; @@ -30,20 +31,17 @@ import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL; import java.util.ArrayList; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.r.nodes.builtin.CastBuilder; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RInternalError; import com.oracle.truffle.r.runtime.RRuntime; -import com.oracle.truffle.r.runtime.RType; import com.oracle.truffle.r.runtime.builtins.RBuiltin; import com.oracle.truffle.r.runtime.data.RDataFactory; import com.oracle.truffle.r.runtime.data.RExternalPtr; import com.oracle.truffle.r.runtime.data.RList; import com.oracle.truffle.r.runtime.data.RNull; -import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector; import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector; import com.oracle.truffle.r.runtime.ffi.DLL; import com.oracle.truffle.r.runtime.ffi.DLL.DLLException; @@ -57,25 +55,19 @@ public class DynLoadFunctions { @RBuiltin(name = "dyn.load", visibility = OFF, kind = INTERNAL, parameterNames = {"lib", "local", "now", "unused"}, behavior = COMPLEX) public abstract static class DynLoad extends RBuiltinNode { - @Override protected void createCasts(CastBuilder casts) { - // TODO: not sure if the behavior is 100% compliant - casts.arg("now").asLogicalVector().findFirst(); + casts.arg("lib").mustBe(stringValue()).asStringVector().mustBe(size(1), RError.Message.CHAR_ARGUMENT).findFirst(); + casts.arg("local").asLogicalVector().findFirst().map(toBoolean()); + casts.arg("now").asLogicalVector().findFirst().map(toBoolean()); + casts.arg("unused").mustBe(stringValue()).asStringVector().findFirst(); } @Specialization @TruffleBoundary - protected RList doDynLoad(RAbstractStringVector libVec, RAbstractLogicalVector localVec, byte now, @SuppressWarnings("unused") String unused) { - // Length checked by GnuR - if (libVec.getLength() > 1) { - throw RError.error(this, RError.Message.TYPE_EXPECTED, RType.Character.getName()); - } - String lib = libVec.getDataAt(0); - // Length not checked by GnuR - byte local = localVec.getDataAt(0); + protected RList doDynLoad(String lib, boolean local, boolean now, @SuppressWarnings("unused") String unused) { try { - DLLInfo dllInfo = DLL.loadPackageDLL(lib, asBoolean(local), asBoolean(now)); + DLLInfo dllInfo = DLL.loadPackageDLL(lib, local, now); return dllInfo.toRList(); } catch (DLLException ex) { // This is not a recoverable error @@ -85,13 +77,15 @@ public class DynLoadFunctions { } } - private static boolean asBoolean(byte b) { - return b == RRuntime.LOGICAL_TRUE ? true : false; - } } @RBuiltin(name = "dyn.unload", visibility = OFF, kind = INTERNAL, parameterNames = {"lib"}, behavior = COMPLEX) public abstract static class DynUnload extends RBuiltinNode { + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("lib").mustBe(stringValue()).asStringVector().mustBe(size(1), RError.Message.CHAR_ARGUMENT).findFirst(); + } + @Specialization @TruffleBoundary protected RNull doDynunload(RAbstractStringVector lib) { @@ -124,12 +118,18 @@ public class DynLoadFunctions { } } - @RBuiltin(name = "is.loaded", kind = INTERNAL, parameterNames = {"symbol", "package", "type"}, behavior = READS_STATE) + @RBuiltin(name = "is.loaded", kind = INTERNAL, parameterNames = {"symbol", "PACKAGE", "type"}, behavior = READS_STATE) public abstract static class IsLoaded extends RBuiltinNode { + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("symbol").mustBe(stringValue()).asStringVector().mustBe(notEmpty()).findFirst(); + casts.arg("PACKAGE").mustBe(stringValue()).asStringVector().mustBe(notEmpty()).findFirst(); + casts.arg("type").mustBe(stringValue()).asStringVector().mustBe(notEmpty()).findFirst(); + } + @Specialization @TruffleBoundary - protected byte isLoaded(RAbstractStringVector symbol, RAbstractStringVector packageName, RAbstractStringVector typeVec) { - String type = typeVec.getDataAt(0); + protected byte isLoaded(String symbol, String packageName, String type) { NativeSymbolType nst = null; switch (type) { case "": @@ -147,24 +147,22 @@ public class DynLoadFunctions { // Not an error in GnuR } DLL.RegisteredNativeSymbol rns = new DLL.RegisteredNativeSymbol(nst, null, null); - boolean found = DLL.findSymbol(symbol.getDataAt(0), packageName.getDataAt(0), rns) != DLL.SYMBOL_NOT_FOUND; + boolean found = DLL.findSymbol(symbol, packageName, rns) != DLL.SYMBOL_NOT_FOUND; return RRuntime.asLogical(found); } } - @RBuiltin(name = "getSymbolInfo", kind = INTERNAL, parameterNames = {"symbol", "package", "withReg"}, behavior = READS_STATE) + @RBuiltin(name = "getSymbolInfo", kind = INTERNAL, parameterNames = {"symbol", "package", "withRegistrationInfo"}, behavior = READS_STATE) public abstract static class GetSymbolInfo extends RBuiltinNode { - @Override protected void createCasts(CastBuilder casts) { - // TODO: not sure if the behavior is 100% compliant - casts.arg("withReg").asLogicalVector().findFirst(); + casts.arg("symbol").mustBe(stringValue()).asStringVector().mustBe(notEmpty()).findFirst(); + casts.arg("withRegistrationInfo").mustBe(logicalValue()).asLogicalVector().findFirst().map(toBoolean()); } @Specialization @TruffleBoundary - protected Object getSymbolInfo(RAbstractStringVector symbolVec, String packageName, byte withReg) { - String symbol = symbolVec.getDataAt(0); + protected Object getSymbolInfo(String symbol, String packageName, boolean withReg) { DLL.RegisteredNativeSymbol rns = DLL.RegisteredNativeSymbol.any(); long f = DLL.findSymbol(RRuntime.asString(symbol), packageName, rns); SymbolInfo symbolInfo = null; @@ -176,7 +174,7 @@ public class DynLoadFunctions { @Specialization(guards = "isDLLInfo(externalPtr)") @TruffleBoundary - protected Object getSymbolInfo(RAbstractStringVector symbolVec, RExternalPtr externalPtr, byte withReg) { + protected Object getSymbolInfo(RAbstractStringVector symbolVec, RExternalPtr externalPtr, boolean withReg) { DLL.DLLInfo dllInfo = DLL.getDLLInfoForId((int) externalPtr.getAddr()); if (dllInfo == null) { throw RError.error(this, RError.Message.REQUIRES_NAME_DLLINFO); @@ -192,20 +190,14 @@ public class DynLoadFunctions { return getResult(symbolInfo, withReg); } - private static Object getResult(DLL.SymbolInfo symbolInfo, byte withReg) { + private static Object getResult(DLL.SymbolInfo symbolInfo, boolean withReg) { if (symbolInfo != null) { - return symbolInfo.createRSymbolObject(new DLL.RegisteredNativeSymbol(DLL.NativeSymbolType.Any, null, null), RRuntime.fromLogical(withReg)); + return symbolInfo.createRSymbolObject(new DLL.RegisteredNativeSymbol(DLL.NativeSymbolType.Any, null, null), withReg); } else { return RNull.instance; } } - @SuppressWarnings("unused") - @Fallback - protected Object getSymbolInfo(Object symbol, Object packageName, Object withReg) { - throw RError.error(this, RError.Message.REQUIRES_NAME_DLLINFO); - } - protected static boolean isDLLInfo(RExternalPtr externalPtr) { return DLL.isDLLInfo(externalPtr); } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FileFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FileFunctions.java index 33546b1db6..02dc87de5d 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FileFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FileFunctions.java @@ -11,6 +11,7 @@ */ package com.oracle.truffle.r.nodes.builtin.base; +import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*; import static com.oracle.truffle.r.runtime.RVisibility.OFF; import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO; import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL; @@ -45,7 +46,6 @@ import java.util.stream.Stream; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.r.nodes.builtin.CastBuilder; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; @@ -62,6 +62,8 @@ import com.oracle.truffle.r.runtime.data.RList; import com.oracle.truffle.r.runtime.data.RLogicalVector; import com.oracle.truffle.r.runtime.data.RNull; import com.oracle.truffle.r.runtime.data.RStringVector; +import com.oracle.truffle.r.runtime.data.RSymbol; +import com.oracle.truffle.r.runtime.data.RTypedValue; import com.oracle.truffle.r.runtime.data.model.RAbstractContainer; import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector; import com.oracle.truffle.r.runtime.ffi.RFFIFactory; @@ -78,15 +80,13 @@ public class FileFunctions { @Override protected void createCasts(CastBuilder casts) { - casts.toInteger(1); + casts.arg("names").mustBe(stringValue()).asStringVector(); + casts.arg("mode").asIntegerVector().findFirst().mustBe(gte(0).and(lte(7))); } @Specialization @TruffleBoundary protected Object fileAccess(RAbstractStringVector names, int mode) { - if (mode == RRuntime.INT_NA || mode < 0 || mode > 7) { - throw RError.error(this, RError.Message.INVALID_ARGUMENT, "mode"); - } int[] data = new int[names.getLength()]; for (int i = 0; i < data.length; i++) { File file = new File(Utils.tildeExpand(names.getDataAt(i))); @@ -110,6 +110,12 @@ public class FileFunctions { @RBuiltin(name = "file.append", kind = INTERNAL, parameterNames = {"file1", "file2"}, behavior = IO) public abstract static class FileAppend extends RBuiltinNode { + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("file1").mustBe(stringValue()).asStringVector(); + casts.arg("file1").mustBe(stringValue()).asStringVector(); + } + @Specialization @TruffleBoundary protected RLogicalVector doFileAppend(RAbstractStringVector file1Vec, RAbstractStringVector file2Vec) { @@ -200,6 +206,11 @@ public class FileFunctions { @RBuiltin(name = "file.create", kind = INTERNAL, parameterNames = {"vec", "showWarnings"}, behavior = IO) public abstract static class FileCreate extends RBuiltinNode { + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("vec").mustBe(stringValue()).asStringVector(); + casts.arg("showWarnings").asLogicalVector().findFirst().mapIf(logicalNA(), constant(RRuntime.LOGICAL_FALSE)); + } @Specialization @TruffleBoundary @@ -225,11 +236,6 @@ public class FileFunctions { return RDataFactory.createLogicalVector(status, RDataFactory.COMPLETE_VECTOR); } - @Fallback - @TruffleBoundary - protected Object doFileCreate(@SuppressWarnings("unused") Object x, @SuppressWarnings("unused") Object y) { - throw RError.error(this, RError.Message.INVALID_ARGUMENT, "file"); - } } @RBuiltin(name = "file.info", kind = INTERNAL, parameterNames = {"fn", "extra_cols"}, behavior = IO) @@ -394,7 +400,13 @@ public class FileFunctions { } } - abstract static class FileLinkAdaptor extends RBuiltinNode { + private abstract static class FileLinkAdaptor extends RBuiltinNode { + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("from").mustBe(stringValue(), RError.Message.INVALID_FIRST_FILENAME).asStringVector(); + casts.arg("to").mustBe(stringValue(), RError.Message.INVALID_SECOND_FILENAME).asStringVector(); + } + protected Object doFileLink(RAbstractStringVector vecFrom, RAbstractStringVector vecTo, boolean symbolic) { int lenFrom = vecFrom.getLength(); int lenTo = vecTo.getLength(); @@ -440,11 +452,6 @@ public class FileFunctions { return doFileLink(vecFrom, vecTo, false); } - @Fallback - @TruffleBoundary - protected Object doFileLink(@SuppressWarnings("unused") Object from, @SuppressWarnings("unused") Object to) { - throw RError.error(this, RError.Message.INVALID_ARGUMENT, "file"); - } } @RBuiltin(name = "file.symlink", kind = INTERNAL, parameterNames = {"from", "to"}, behavior = IO) @@ -455,16 +462,16 @@ public class FileFunctions { return doFileLink(vecFrom, vecTo, true); } - @Fallback - @TruffleBoundary - protected Object doFileSymLink(@SuppressWarnings("unused") Object from, @SuppressWarnings("unused") Object to) { - throw RError.error(this, RError.Message.INVALID_ARGUMENT, "file"); - } } - @RBuiltin(name = "file.remove", kind = INTERNAL, parameterNames = {"vec"}, behavior = IO) + @RBuiltin(name = "file.remove", kind = INTERNAL, parameterNames = {"file"}, behavior = IO) public abstract static class FileRemove extends RBuiltinNode { + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("file").mustBe(stringValue(), RError.Message.INVALID_FIRST_FILENAME).asStringVector(); + } + @Specialization @TruffleBoundary protected Object doFileRemove(RAbstractStringVector vec) { @@ -485,15 +492,16 @@ public class FileFunctions { return RDataFactory.createLogicalVector(status, RDataFactory.COMPLETE_VECTOR); } - @Fallback - @TruffleBoundary - protected Object doFileRemove(@SuppressWarnings("unused") Object x) { - throw RError.error(this, RError.Message.INVALID_ARGUMENT, "file"); - } } @RBuiltin(name = "file.rename", kind = INTERNAL, parameterNames = {"from", "to"}, behavior = IO) public abstract static class FileRename extends RBuiltinNode { + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("from").mustBe(stringValue()).asStringVector(); + casts.arg("to").mustBe(stringValue()).asStringVector(); + } + @Specialization @TruffleBoundary protected Object doFileRename(RAbstractStringVector vecFrom, RAbstractStringVector vecTo) { @@ -520,16 +528,16 @@ public class FileFunctions { return RDataFactory.createLogicalVector(status, RDataFactory.COMPLETE_VECTOR); } - @Fallback - @TruffleBoundary - protected Object doFileRename(@SuppressWarnings("unused") Object from, @SuppressWarnings("unused") Object to) { - throw RError.error(this, RError.Message.INVALID_ARGUMENT, "file"); - } } - @RBuiltin(name = "file.exists", kind = INTERNAL, parameterNames = {"vec"}, behavior = IO) + @RBuiltin(name = "file.exists", kind = INTERNAL, parameterNames = {"file"}, behavior = IO) public abstract static class FileExists extends RBuiltinNode { + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("file").mustBe(stringValue()).asStringVector(); + } + @Specialization @TruffleBoundary protected Object doFileExists(RAbstractStringVector vec) { @@ -547,10 +555,6 @@ public class FileFunctions { return RDataFactory.createLogicalVector(status, RDataFactory.COMPLETE_VECTOR); } - @Fallback - protected Object doFileExists(@SuppressWarnings("unused") Object vec) { - throw RError.error(this, RError.Message.INVALID_ARGUMENT, "file"); - } } // TODO Implement all the options @@ -559,17 +563,31 @@ public class FileFunctions { private static final String DOT = "."; private static final String DOTDOT = ".."; + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("path").mustBe(stringValue()).asStringVector(); + casts.arg("pattern").mustBe(stringValue().or(nullValue())); + casts.arg("all.files").asLogicalVector().findFirst().notNA().map(toBoolean()); + casts.arg("full.names").asLogicalVector().findFirst().notNA().map(toBoolean()); + casts.arg("recursive").asLogicalVector().findFirst().notNA().map(toBoolean()); + casts.arg("ignore.case").asLogicalVector().findFirst().notNA().map(toBoolean()); + casts.arg("include.dirs").asLogicalVector().findFirst().notNA().map(toBoolean()); + casts.arg("no..").asLogicalVector().findFirst().notNA().map(toBoolean()); + } + @SuppressWarnings("unused") @Specialization @TruffleBoundary - protected RStringVector doListFiles(RAbstractStringVector vec, RNull patternVec, byte allFiles, byte fullNames, byte recursive, byte ignoreCase, byte includeDirs, byte noDotDot) { + protected RStringVector doListFiles(RAbstractStringVector vec, RNull patternVec, boolean allFiles, boolean fullNames, boolean recursive, boolean ignoreCase, boolean includeDirs, + boolean noDotDot) { return doListFilesBody(vec, null, allFiles, fullNames, recursive, ignoreCase, includeDirs, noDotDot); } @Specialization @TruffleBoundary - protected RStringVector doListFiles(RAbstractStringVector vec, RAbstractStringVector patternVec, byte allFiles, byte fullNames, byte recursive, byte ignoreCase, byte includeDirs, - byte noDotDot) { + protected RStringVector doListFiles(RAbstractStringVector vec, RAbstractStringVector patternVec, boolean allFiles, boolean fullNames, boolean recursive, boolean ignoreCase, + boolean includeDirs, + boolean noDotDot) { /* * Pattern in first element of vector, remaining elements are ignored (as per GnuR). * N.B. The pattern matches file names not paths, which means we cannot just use the @@ -577,20 +595,22 @@ public class FileFunctions { */ String pattern = null; - if (!(patternVec.getLength() == 0 || patternVec.getDataAt(0).length() == 0)) { - pattern = patternVec.getDataAt(0); + if (patternVec.getLength() > 0) { + if (RRuntime.isNA(patternVec.getDataAt(0))) { + throw RError.error(this, RError.Message.INVALID_ARGUMENT, "pattern"); + } else { + pattern = patternVec.getDataAt(0); + } } + return doListFilesBody(vec, pattern, allFiles, fullNames, recursive, ignoreCase, includeDirs, noDotDot); } - private RStringVector doListFilesBody(RAbstractStringVector vec, String patternString, byte allFilesL, byte fullNamesL, byte recursiveL, byte ignoreCaseL, byte includeDirsL, byte noDotDotL) { - boolean allFiles = RRuntime.fromLogical(allFilesL); - boolean fullNames = RRuntime.fromLogical(fullNamesL); - boolean recursive = RRuntime.fromLogical(recursiveL); + private RStringVector doListFilesBody(RAbstractStringVector vec, String patternString, boolean allFiles, boolean fullNames, boolean recursive, + boolean ignoreCaseIn, boolean includeDirsIn, boolean noDotDot) { + boolean includeDirs = !recursive || includeDirsIn; @SuppressWarnings("unused") - boolean ignoreCase = check(ignoreCaseL, "ignoreCase"); - boolean includeDirs = !recursive || RRuntime.fromLogical(includeDirsL); - boolean noDotDot = RRuntime.fromLogical(noDotDotL); + boolean ignoreCase = check(ignoreCaseIn, "ignoreCase"); Pattern pattern = patternString == null ? null : Pattern.compile(patternString); // Curiously the result is not a vector of same length as the input, // as typical for R, but a single vector, which means duplicates may occur @@ -644,10 +664,10 @@ public class FileFunctions { Arrays.sort(data); return RDataFactory.createStringVector(data, RDataFactory.COMPLETE_VECTOR); } + } - private boolean check(byte valueLogical, String argName) { - boolean value = RRuntime.fromLogical(valueLogical); + private boolean check(boolean value, String argName) { if (value) { RError.warning(this, RError.Message.GENERIC, "'" + argName + "'" + " is not implemented"); } @@ -683,13 +703,18 @@ public class FileFunctions { } } - @RBuiltin(name = "list.dirs", kind = INTERNAL, parameterNames = {"path", "full.names", "recursive"}, behavior = IO) + @RBuiltin(name = "list.dirs", kind = INTERNAL, parameterNames = {"directory", "full.names", "recursive"}, behavior = IO) public abstract static class ListDirs extends RBuiltinNode { + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("directory").mustBe(stringValue()).asStringVector(); + casts.arg("full.names").asLogicalVector().findFirst().notNA().map(toBoolean()); + casts.arg("recursive").asLogicalVector().findFirst().notNA().map(toBoolean()); + } + @Specialization @TruffleBoundary - protected RStringVector listDirs(RAbstractStringVector paths, byte fullNamesL, byte recursiveL) { - boolean fullNames = RRuntime.fromLogical(fullNamesL); - boolean recursive = RRuntime.fromLogical(recursiveL); + protected RStringVector listDirs(RAbstractStringVector paths, boolean fullNames, boolean recursive) { ArrayList<String> dirList = new ArrayList<>(); for (int i = 0; i < paths.getLength(); i++) { String vecPathString = paths.getDataAt(i); @@ -736,6 +761,12 @@ public class FileFunctions { @RBuiltin(name = "file.path", kind = INTERNAL, parameterNames = {"paths", "fsep"}, behavior = IO) public abstract static class FilePath extends RBuiltinNode { + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("paths").mustBe(instanceOf(RList.class), RError.Message.INVALID_FIRST_ARGUMENT); + casts.arg("fsep").mustBe(stringValue()).asStringVector().findFirst().notNA(); + } + @Child private CastStringNode castStringNode; private CastStringNode initCastStringNode() { @@ -749,12 +780,12 @@ public class FileFunctions { @SuppressWarnings("unused") @Specialization(guards = "lengthZero(vec)") @TruffleBoundary - protected RStringVector doFilePathZero(RList vec, RAbstractStringVector fsep) { + protected RStringVector doFilePathZero(RList vec, String fsep) { return RDataFactory.createEmptyStringVector(); } @Specialization(guards = "!lengthZero(args)") - protected RStringVector doFilePath(RList args, RAbstractStringVector fsepVec) { + protected RStringVector doFilePath(RList args, String fsep) { Object[] argValues = args.getDataWithoutCopying(); int resultLength = 0; for (int i = 0; i < argValues.length; i++) { @@ -775,19 +806,24 @@ public class FileFunctions { String[] result = new String[resultLength]; String[][] inputs = new String[argValues.length][]; for (int i = 0; i < argValues.length; i++) { - Object elem = argValues[i]; - if (!(elem instanceof String || elem instanceof RStringVector)) { - elem = initCastStringNode().executeString(elem); + Object elem = args.getDataAt(i); + if (elem instanceof RTypedValue && ((RTypedValue) elem).isS4()) { + throw RError.nyi(this, "list files: S4 elem"); + } else if (elem instanceof RSymbol) { + inputs[i] = new String[]{((RSymbol) elem).getName()}; + } else { + if (!(elem instanceof String || elem instanceof RStringVector)) { + elem = initCastStringNode().executeString(elem); + } } if (elem instanceof String) { inputs[i] = new String[]{(String) elem}; } else if (elem instanceof RStringVector) { inputs[i] = ((RStringVector) elem).getDataWithoutCopying(); } else { - RInternalError.shouldNotReachHere(); + throw RError.error(this, RError.Message.NON_STRING_ARG_TO_INTERNAL_PASTE); } } - String fsep = fsepVec.getDataAt(0); for (int i = 0; i < resultLength; i++) { String path = ""; for (int j = 0; j < inputs.length; j++) { @@ -826,20 +862,18 @@ public class FileFunctions { @Override protected void createCasts(CastBuilder casts) { - casts.toLogical(2).toLogical(3).toLogical(4).toLogical(5); - } - - private boolean checkLogical(byte value, String name) throws RError { - if (RRuntime.isNA(value)) { - throw RError.error(this, RError.Message.INVALID_ARGUMENT, name); - } else { - return RRuntime.fromLogical(value); - } + casts.arg("from").mustBe(stringValue()).asStringVector(); + casts.arg("to").mustBe(stringValue()).asStringVector(); + casts.arg("overwrite").asLogicalVector().findFirst().notNA().map(toBoolean()); + casts.arg("recursive").asLogicalVector().findFirst().notNA().map(toBoolean()); + casts.arg("copy.mode").asLogicalVector().findFirst().notNA().map(toBoolean()); + casts.arg("copy.date").asLogicalVector().findFirst().notNA().map(toBoolean()); } @Specialization @TruffleBoundary - protected RLogicalVector fileCopy(RAbstractStringVector vecFrom, RAbstractStringVector vecTo, byte overwriteArg, byte recursiveArg, byte copyModeArg, byte copyDateArg) { + protected RLogicalVector fileCopy(RAbstractStringVector vecFrom, RAbstractStringVector vecTo, boolean overwrite, boolean recursiveA, boolean copyMode, boolean copyDate) { + boolean recursive = recursiveA; int lenFrom = vecFrom.getLength(); byte[] status = new byte[lenFrom]; if (lenFrom > 0) { @@ -847,22 +881,18 @@ public class FileFunctions { if (lenTo != 1) { throw RError.error(this, RError.Message.INVALID_ARGUMENT, "to"); } - boolean overWrite = checkLogical(overwriteArg, "overwrite"); - boolean recursive = checkLogical(recursiveArg, "recursive"); - boolean copyMode = checkLogical(copyModeArg, "copy.mode"); - boolean copyDate = checkLogical(copyDateArg, "copy.dates"); // Java cannot distinguish copy.mode and copy.dates CopyOption[] copyOptions; if (copyMode || copyDate) { - copyOptions = new CopyOption[overWrite ? 2 : 1]; - copyOptions[overWrite ? 1 : 0] = StandardCopyOption.COPY_ATTRIBUTES; - } else if (overWrite) { + copyOptions = new CopyOption[overwrite ? 2 : 1]; + copyOptions[overwrite ? 1 : 0] = StandardCopyOption.COPY_ATTRIBUTES; + } else if (overwrite) { copyOptions = new CopyOption[1]; } else { copyOptions = new CopyOption[0]; } - if (overWrite) { + if (overwrite) { copyOptions[0] = StandardCopyOption.REPLACE_EXISTING; } FileSystem fileSystem = FileSystems.getDefault(); @@ -899,10 +929,10 @@ public class FileFunctions { } } else { // copy to existing files is skipped unless overWrite - if (!Files.exists(toPath) || overWrite) { + if (!Files.exists(toPath) || overwrite) { /* - * Be careful if toPath is a directory, if empty Java will replace - * it with a plain file, otherwise the copy will fail + * toB Be careful if toPath is a directory, if empty Java will + * replace it with a plain file, otherwise the copy will fail */ if (Files.isDirectory(toPath)) { Path fromFileNamePath = fromPath.getFileName(); @@ -985,6 +1015,11 @@ public class FileFunctions { @RBuiltin(name = "dirname", kind = INTERNAL, parameterNames = {"path"}, behavior = IO) public abstract static class DirName extends XyzNameAdapter { + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("path").mustBe(stringValue(), RError.Message.CHAR_VEC_ARGUMENT); + } + @Specialization @TruffleBoundary protected RStringVector doDirName(RAbstractStringVector vec) { @@ -998,6 +1033,12 @@ public class FileFunctions { @RBuiltin(name = "basename", kind = INTERNAL, parameterNames = {"path"}, behavior = IO) public abstract static class BaseName extends XyzNameAdapter { + + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("path").mustBe(stringValue(), RError.Message.CHAR_VEC_ARGUMENT); + } + @Specialization @TruffleBoundary protected RStringVector doDirName(RAbstractStringVector vec) { @@ -1014,23 +1055,14 @@ public class FileFunctions { @Override protected void createCasts(CastBuilder casts) { - casts.toLogical(1).toLogical(2); - } - - private boolean checkLogical(byte value, String name) throws RError { - if (RRuntime.isNA(value)) { - throw RError.error(this, RError.Message.INVALID_ARGUMENT, name); - } else { - return RRuntime.fromLogical(value); - } + casts.arg("x").mustBe(stringValue(), RError.Message.CHAR_VEC_ARGUMENT); + casts.arg("recursive").asLogicalVector().findFirst().notNA().map(toBoolean()); + casts.arg("force").asLogicalVector().findFirst().notNA().map(toBoolean()); } @Specialization @TruffleBoundary - protected int doUnlink(RAbstractStringVector vec, byte recursiveArg, byte forceArg) { - @SuppressWarnings("unused") - boolean force = checkLogical(forceArg, "force"); - boolean recursive = checkLogical(recursiveArg, "recursive"); + protected int doUnlink(RAbstractStringVector vec, boolean recursive, @SuppressWarnings("unused") boolean force) { int result = 1; FileSystem fileSystem = FileSystems.getDefault(); for (int i = -0; i < vec.getLength(); i++) { @@ -1087,37 +1119,39 @@ public class FileFunctions { } - @SuppressWarnings("unused") - @Fallback - protected int doUnlink(Object vec, Object recursive, Object force) { - throw RError.nyi(this, "unlink"); - } - - public static boolean simpleArgs(@SuppressWarnings("unused") RAbstractStringVector vec, byte recursive, byte force) { - return recursive == RRuntime.LOGICAL_FALSE && force == RRuntime.LOGICAL_FALSE; - } } @RBuiltin(name = "dir.create", visibility = OFF, kind = INTERNAL, parameterNames = {"path", "showWarnings", "recursive", "mode"}, behavior = IO) public abstract static class DirCreate extends RBuiltinNode { + + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("path").mustBe(stringValue()).asStringVector().mustBe(size(1)).findFirst(); + casts.arg("showWarnings").asLogicalVector().findFirst().map(toBoolean()); + casts.arg("recursive").asLogicalVector().findFirst().map(toBoolean()); + casts.arg("mode").asIntegerVector().findFirst().mapIf(intNA(), constant(0777)); + } + @Specialization @TruffleBoundary - protected byte dirCreate(RAbstractStringVector pathVec, byte showWarnings, byte recursive, RIntVector octMode) { - boolean ok = true; - if (pathVec.getLength() != 1) { - throw RError.error(this, RError.Message.INVALID_ARGUMENT, "path"); - } - String path = Utils.tildeExpand(pathVec.getDataAt(0)); - if (RRuntime.fromLogical(recursive)) { - ok = mkparentdirs(new File(path).getAbsoluteFile().getParentFile(), showWarnings, octMode.getDataAt(0)); - } - if (ok) { - ok = mkdir(path, showWarnings, octMode.getDataAt(0)); + protected byte dirCreate(String pathIn, boolean showWarnings, boolean recursive, int octMode) { + boolean ok; + if (RRuntime.isNA(pathIn)) { + ok = false; + } else { + ok = true; + String path = Utils.tildeExpand(pathIn); + if (recursive) { + ok = mkparentdirs(new File(path).getAbsoluteFile().getParentFile(), showWarnings, octMode); + } + if (ok) { + ok = mkdir(path, showWarnings, octMode); + } } return RRuntime.asLogical(ok); } - private boolean mkparentdirs(File file, byte showWarnings, int mode) { + private boolean mkparentdirs(File file, boolean showWarnings, int mode) { if (file.isDirectory()) { return true; } @@ -1131,12 +1165,12 @@ public class FileFunctions { } } - private boolean mkdir(String path, byte showWarnings, int mode) { + private boolean mkdir(String path, boolean showWarnings, int mode) { try { RFFIFactory.getRFFI().getBaseRFFI().mkdir(path, mode); return true; } catch (IOException ex) { - if (RRuntime.fromLogical(showWarnings)) { + if (showWarnings) { RError.warning(this, RError.Message.DIR_CANNOT_CREATE, path); } return false; @@ -1146,6 +1180,11 @@ public class FileFunctions { @RBuiltin(name = "dir.exists", kind = INTERNAL, parameterNames = "paths", behavior = IO) public abstract static class DirExists extends RBuiltinNode { + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("paths").mustBe(stringValue()).asStringVector(); + } + @Specialization @TruffleBoundary protected RLogicalVector dirExists(RAbstractStringVector pathVec) { @@ -1158,9 +1197,5 @@ public class FileFunctions { return RDataFactory.createLogicalVector(data, RDataFactory.COMPLETE_VECTOR); } - @Fallback - protected RLogicalVector dirExists(@SuppressWarnings("unused") Object pathVec) { - throw RError.error(this, RError.Message.INVALID_ARGUMENT, "filename"); - } } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LoadSaveFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LoadSaveFunctions.java index e61ffdd09d..34351b6c07 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LoadSaveFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LoadSaveFunctions.java @@ -12,6 +12,7 @@ */ package com.oracle.truffle.r.nodes.builtin.base; +import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*; import static com.oracle.truffle.r.runtime.RVisibility.OFF; import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO; import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL; @@ -25,6 +26,7 @@ import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.r.nodes.builtin.CastBuilder; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; import com.oracle.truffle.r.nodes.builtin.base.SerializeFunctions.Adapter; import com.oracle.truffle.r.nodes.function.PromiseHelperNode.PromiseCheckHelperNode; @@ -38,7 +40,6 @@ import com.oracle.truffle.r.runtime.data.RDataFactory; import com.oracle.truffle.r.runtime.data.RNull; import com.oracle.truffle.r.runtime.data.RPairList; import com.oracle.truffle.r.runtime.data.RStringVector; -import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector; import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector; import com.oracle.truffle.r.runtime.env.REnvironment; import com.oracle.truffle.r.runtime.env.REnvironment.PutException; @@ -53,9 +54,16 @@ public class LoadSaveFunctions { private final NACheck naCheck = NACheck.create(); + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("con").mustBe(instanceOf(RConnection.class)); + casts.arg("envir").mustBe(nullValue().not(), RError.Message.USE_NULL_ENV_DEFUNCT).mustBe(instanceOf(REnvironment.class)); + casts.arg("verbose").asLogicalVector().findFirst().map(toBoolean()); + } + @Specialization @TruffleBoundary - protected RStringVector load(RConnection con, REnvironment envir, @SuppressWarnings("unused") RAbstractLogicalVector verbose) { + protected RStringVector load(RConnection con, REnvironment envir, @SuppressWarnings("unused") boolean verbose) { try (RConnection openConn = con.forceOpen("r")) { String s = openConn.readChar(5, true); if (s.equals("RDA2\n") || s.equals("RDB2\n") || s.equals("RDX2\n")) { @@ -98,10 +106,16 @@ public class LoadSaveFunctions { } } - @RBuiltin(name = "load", visibility = OFF, kind = INTERNAL, parameterNames = {"con", "envir"}, behavior = IO) + @RBuiltin(name = "load", visibility = OFF, kind = INTERNAL, parameterNames = {"file", "envir"}, behavior = IO) public abstract static class Load extends RBuiltinNode { // now deprecated but still used by some packages + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("file").mustBe(stringValue()).asStringVector().mustBe(notEmpty(), RError.Message.FIRST_ARGUMENT_NOT_FILENAME).findFirst(); + casts.arg("envir").mustBe(nullValue().not(), RError.Message.USE_NULL_ENV_DEFUNCT).mustBe(instanceOf(REnvironment.class)); + } + private static final int R_MAGIC_EMPTY = 999; private static final int R_MAGIC_CORRUPT = 998; private static final int R_MAGIC_TOONEW = 997; @@ -114,8 +128,8 @@ public class LoadSaveFunctions { @Specialization @TruffleBoundary - protected RStringVector load(RAbstractStringVector fileVec, @SuppressWarnings("unused") REnvironment envir) { - String path = Utils.tildeExpand(fileVec.getDataAt(0)); + protected RStringVector load(String pathIn, @SuppressWarnings("unused") REnvironment envir) { + String path = Utils.tildeExpand(pathIn); try (BufferedInputStream bs = new BufferedInputStream(new FileInputStream(path))) { int magic = readMagic(bs); switch (magic) { @@ -169,16 +183,25 @@ public class LoadSaveFunctions { } } - @RBuiltin(name = "saveToConn", visibility = OFF, kind = INTERNAL, parameterNames = {"list", "conn", "ascii", "version", "envir", "eval.promises"}, behavior = IO) + @RBuiltin(name = "saveToConn", visibility = OFF, kind = INTERNAL, parameterNames = {"list", "con", "ascii", "version", "environment", "eval.promises"}, behavior = IO) public abstract static class SaveToConn extends Adapter { private static final String ASCII_HEADER = "RDA2\n"; private static final String XDR_HEADER = "RDX2\n"; + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("list").mustBe(stringValue()).asStringVector().mustBe(notEmpty(), RError.Message.FIRST_ARGUMENT_NOT_CHARVEC).findFirst(); + casts.arg("con").mustBe(instanceOf(RConnection.class)); + casts.arg("ascii").mustBe(logicalValue(), RError.Message.ASCII_NOT_LOGICAL); + casts.arg("version").mustBe(nullValue().or(integerValue())); + casts.arg("environment").mustBe(nullValue().not(), RError.Message.USE_NULL_ENV_DEFUNCT).mustBe(instanceOf(REnvironment.class)); + casts.arg("eval.promises").asLogicalVector().findFirst().notNA().map(toBoolean()); + } + @Specialization protected Object saveToConn(VirtualFrame frame, RAbstractStringVector list, RConnection conn, byte asciiLogical, @SuppressWarnings("unused") RNull version, REnvironment envir, - byte evalPromisesLogical, // + boolean evalPromises, // @Cached("new()") PromiseCheckHelperNode promiseHelper) { - boolean evalPromises = RRuntime.fromLogical(evalPromisesLogical); RPairList prev = null; Object toSave = RNull.instance; for (int i = 0; i < list.getLength(); i++) { diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NamespaceFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NamespaceFunctions.java index 9c70a9bdcc..a0b4cfd5a4 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NamespaceFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NamespaceFunctions.java @@ -22,11 +22,13 @@ */ package com.oracle.truffle.r.nodes.builtin.base; +import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*; import static com.oracle.truffle.r.runtime.builtins.RBehavior.MODIFIES_STATE; import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE; import static com.oracle.truffle.r.runtime.builtins.RBehavior.READS_STATE; import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL; +import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.r.nodes.builtin.CastBuilder; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; @@ -40,8 +42,19 @@ import com.oracle.truffle.r.runtime.env.REnvironment; public class NamespaceFunctions { + private abstract static class CastHelper extends RBuiltinNode { + protected void name(CastBuilder casts) { + casts.arg("name").mustBe(stringValue().or(instanceOf(RSymbol.class))); + } + } + @RBuiltin(name = "getRegisteredNamespace", kind = INTERNAL, parameterNames = {"name"}, behavior = READS_STATE) - public abstract static class GetRegisteredNamespace extends RBuiltinNode { + public abstract static class GetRegisteredNamespace extends CastHelper { + @Override + protected void createCasts(CastBuilder casts) { + name(casts); + } + @Specialization protected Object doGetRegisteredNamespace(RAbstractStringVector name) { Object result = REnvironment.getRegisteredNamespace(name.getDataAt(0)); @@ -64,7 +77,12 @@ public class NamespaceFunctions { } @RBuiltin(name = "isRegisteredNamespace", kind = INTERNAL, parameterNames = {"name"}, behavior = READS_STATE) - public abstract static class IsRegisteredNamespace extends RBuiltinNode { + public abstract static class IsRegisteredNamespace extends CastHelper { + @Override + protected void createCasts(CastBuilder casts) { + name(casts); + } + @Specialization protected byte doIsRegisteredNamespace(RAbstractStringVector name) { Object result = REnvironment.getRegisteredNamespace(name.getDataAt(0)); @@ -93,8 +111,8 @@ public class NamespaceFunctions { return RRuntime.asLogical(env.isNamespaceEnv()); } - @Specialization - protected byte doIsNamespaceEnv(@SuppressWarnings("unused") RNull env) { + @Fallback + protected byte doIsNamespaceEnv(@SuppressWarnings("unused") Object env) { return RRuntime.LOGICAL_FALSE; } } @@ -108,23 +126,38 @@ public class NamespaceFunctions { } @RBuiltin(name = "registerNamespace", kind = INTERNAL, parameterNames = {"name", "env"}, behavior = MODIFIES_STATE) - public abstract static class RegisterNamespace extends RBuiltinNode { + public abstract static class RegisterNamespace extends CastHelper { @Override protected void createCasts(CastBuilder casts) { - casts.arg("name").asStringVector().findFirst(); + name(casts); + casts.arg("env").mustBe(instanceOf(REnvironment.class)); } @Specialization - protected RNull registerNamespace(String name, REnvironment env) { - if (REnvironment.registerNamespace(name, env) == null) { + protected RNull registerNamespace(RAbstractStringVector name, REnvironment env) { + if (REnvironment.registerNamespace(name.getDataAt(0), env) == null) { throw RError.error(this, RError.Message.NS_ALREADY_REG); } return RNull.instance; } + + @Specialization + protected RNull registerNamespace(RSymbol nameSym, REnvironment env) { + if (REnvironment.registerNamespace(nameSym.getName(), env) == null) { + throw RError.error(this, RError.Message.NS_ALREADY_REG); + } + return RNull.instance; + + } } @RBuiltin(name = "unregisterNamespace", kind = INTERNAL, parameterNames = {"name"}, behavior = MODIFIES_STATE) - public abstract static class UnregisterNamespace extends RBuiltinNode { + public abstract static class UnregisterNamespace extends CastHelper { + @Override + protected void createCasts(CastBuilder casts) { + name(casts); + } + @Specialization protected RNull unregisterNamespace(RAbstractStringVector name) { doUnregisterNamespace(name.getDataAt(0)); diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NormalizePath.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NormalizePath.java index 2d0e012711..9d46b2bb64 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NormalizePath.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NormalizePath.java @@ -85,9 +85,4 @@ public abstract class NormalizePath extends RBuiltinNode { return RDataFactory.createStringVector(results, RDataFactory.COMPLETE_VECTOR); } - @SuppressWarnings("unused") - @Specialization - protected Object doNormalizePath(Object path, Object winslash, Object mustWork) { - throw RError.error(this, RError.Message.WRONG_TYPE); - } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quit.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quit.java index 17aa680ce8..7ccf2fb189 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quit.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quit.java @@ -12,12 +12,12 @@ */ package com.oracle.truffle.r.nodes.builtin.base; +import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*; import static com.oracle.truffle.r.runtime.RVisibility.OFF; import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX; import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.r.nodes.builtin.CastBuilder; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; @@ -30,14 +30,15 @@ import com.oracle.truffle.r.runtime.RStartParams.SA_TYPE; import com.oracle.truffle.r.runtime.builtins.RBuiltin; import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.data.RNull; -import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector; @RBuiltin(name = "quit", visibility = OFF, kind = INTERNAL, parameterNames = {"save", "status", "runLast"}, behavior = COMPLEX) public abstract class Quit extends RBuiltinNode { @Override protected void createCasts(CastBuilder casts) { - casts.toInteger(1); + casts.arg("save").mustBe(stringValue(), RError.Message.QUIT_ASK).asStringVector().findFirst(); + casts.arg("status").asIntegerVector().findFirst(); + casts.arg("runLast").asLogicalVector().findFirst(); } private SA_TYPE checkSaveValue(String save) throws RError { @@ -51,20 +52,20 @@ public abstract class Quit extends RBuiltinNode { @Specialization @TruffleBoundary - protected Object doQuit(RAbstractStringVector saveArg, final int status, final byte runLastIn) { + protected Object doQuit(String save, final int status, final byte runLastIn) { + byte runLast = runLastIn; if (RContext.getInstance().stateInstrumentation.getBrowserState().inBrowser()) { RError.warning(this, RError.Message.BROWSER_QUIT); return RNull.instance; } - String save = saveArg.getDataAt(0); RStartParams.SA_TYPE ask = checkSaveValue(save); if (ask == SA_TYPE.SAVEASK && !RContext.getInstance().getConsoleHandler().isInteractive()) { RError.warning(this, RError.Message.QUIT_ASK_INTERACTIVE); } if (status == RRuntime.INT_NA) { RError.warning(this, RError.Message.QUIT_INVALID_STATUS); + runLast = RRuntime.LOGICAL_FALSE; } - byte runLast = runLastIn; if (runLast == RRuntime.LOGICAL_NA) { RError.warning(this, RError.Message.QUIT_INVALID_RUNLAST); runLast = RRuntime.LOGICAL_FALSE; @@ -73,12 +74,4 @@ public abstract class Quit extends RBuiltinNode { throw RInternalError.shouldNotReachHere("cleanup returned"); } - @SuppressWarnings("unused") - @Fallback - protected Object doQuit(Object saveArg, Object status, Object runLast) { - if (RRuntime.asString(saveArg) == null) { - throw RError.error(this, RError.Message.QUIT_ASK); - } - throw RError.error(this, RError.Message.INVALID_OR_UNIMPLEMENTED_ARGUMENTS); - } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RegFinalizer.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RegFinalizer.java index 03395b1cca..0eeca3ebde 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RegFinalizer.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RegFinalizer.java @@ -22,14 +22,14 @@ */ package com.oracle.truffle.r.nodes.builtin.base; +import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*; import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX; import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL; -import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.r.nodes.builtin.CastBuilder; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; import com.oracle.truffle.r.runtime.RError; -import com.oracle.truffle.r.runtime.RRuntime; import com.oracle.truffle.r.runtime.builtins.RBuiltin; import com.oracle.truffle.r.runtime.data.RExternalPtr; import com.oracle.truffle.r.runtime.data.RFunction; @@ -38,31 +38,27 @@ import com.oracle.truffle.r.runtime.env.REnvironment; @RBuiltin(name = "reg.finalizer", kind = INTERNAL, parameterNames = {"e", "f", "onexit"}, behavior = COMPLEX) public abstract class RegFinalizer extends RBuiltinNode { + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("e").mustBe(instanceOf(REnvironment.class).or(instanceOf(RExternalPtr.class)), RError.Message.REG_FINALIZER_FIRST); + casts.arg("f").mustBe(instanceOf(RFunction.class), RError.Message.REG_FINALIZER_SECOND); + casts.arg("onexit").asLogicalVector().findFirst().notNA(RError.Message.REG_FINALIZER_THIRD).map(toBoolean()); + } + @Specialization - protected RNull doRegFinalizer(RExternalPtr ext, RFunction fun, byte onexit) { + protected RNull doRegFinalizer(RExternalPtr ext, RFunction fun, boolean onexit) { return doRegFinalizerEither(ext, fun, onexit); } @Specialization - protected RNull doRegFinalizer(REnvironment env, RFunction fun, byte onexit) { + protected RNull doRegFinalizer(REnvironment env, RFunction fun, boolean onexit) { return doRegFinalizerEither(env, fun, onexit); } @SuppressWarnings("unused") - private RNull doRegFinalizerEither(Object env, RFunction fun, byte onexit) { - if (onexit == RRuntime.LOGICAL_NA) { - throw RError.error(this, RError.Message.REG_FINALIZER_THIRD); - } + private static RNull doRegFinalizerEither(Object env, RFunction fun, boolean onexit) { // TODO the actual work return RNull.instance; } - @SuppressWarnings("unused") - @Fallback - protected RNull doRegFinalizer(Object env, Object fun, byte onexit) { - if (fun instanceof RFunction) { - throw RError.error(this, RError.Message.REG_FINALIZER_FIRST); - } - throw RError.error(this, RError.Message.REG_FINALIZER_SECOND); - } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SerializeFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SerializeFunctions.java index a4b3465b43..438c5855bb 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SerializeFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SerializeFunctions.java @@ -22,6 +22,7 @@ */ package com.oracle.truffle.r.nodes.builtin.base; +import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*; import static com.oracle.truffle.r.runtime.RVisibility.OFF; import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO; import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL; @@ -29,8 +30,8 @@ import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL; import java.io.IOException; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.r.nodes.builtin.CastBuilder; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RRuntime; @@ -80,10 +81,19 @@ public class SerializeFunctions { throw RError.error(this, RError.Message.GENERIC, ex.getMessage()); } } + + protected void connection(CastBuilder casts) { + casts.arg("con").mustBe(instanceOf(RConnection.class)); + } } - @RBuiltin(name = "unserializeFromConn", kind = INTERNAL, parameterNames = {"conn", "refhook"}, behavior = IO) + @RBuiltin(name = "unserializeFromConn", kind = INTERNAL, parameterNames = {"con", "refhook"}, behavior = IO) public abstract static class UnserializeFromConn extends Adapter { + @Override + protected void createCasts(CastBuilder casts) { + connection(casts); + } + @Specialization protected Object doUnserializeFromConn(RConnection conn, @SuppressWarnings("unused") RNull refhook) { return doUnserializeFromConnBase(conn, null); @@ -96,8 +106,15 @@ public class SerializeFunctions { } } - @RBuiltin(name = "serializeToConn", visibility = OFF, kind = INTERNAL, parameterNames = {"object", "conn", "ascii", "version", "refhook"}, behavior = IO) + @RBuiltin(name = "serializeToConn", visibility = OFF, kind = INTERNAL, parameterNames = {"object", "con", "ascii", "version", "refhook"}, behavior = IO) public abstract static class SerializeToConn extends Adapter { + @Override + protected void createCasts(CastBuilder casts) { + connection(casts); + casts.arg("ascii").mustBe(logicalValue(), RError.Message.ASCII_NOT_LOGICAL); + casts.arg("version").mustBe(nullValue().or(integerValue())); + } + @Specialization protected Object doSerializeToConn(Object object, RConnection conn, byte asciiLogical, RNull version, RNull refhook) { int type; @@ -112,8 +129,13 @@ public class SerializeFunctions { } } - @RBuiltin(name = "unserialize", kind = INTERNAL, parameterNames = {"conn", "refhook"}, behavior = IO) + @RBuiltin(name = "unserialize", kind = INTERNAL, parameterNames = {"con", "refhook"}, behavior = IO) public abstract static class Unserialize extends Adapter { + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("con").mustBe(instanceOf(RConnection.class).or(instanceOf(RAbstractRawVector.class))); + } + @Specialization protected Object unSerialize(RConnection conn, @SuppressWarnings("unused") RNull refhook) { return doUnserializeFromConnBase(conn, null); @@ -125,8 +147,14 @@ public class SerializeFunctions { } } - @RBuiltin(name = "serialize", kind = INTERNAL, parameterNames = {"object", "conn", "type", "version", "refhook"}, behavior = IO) + @RBuiltin(name = "serialize", kind = INTERNAL, parameterNames = {"object", "con", "type", "version", "refhook"}, behavior = IO) public abstract static class Serialize extends Adapter { + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("con").mustBe(nullValue().or(instanceOf(RConnection.class))); + casts.arg("type").asIntegerVector().findFirst(); + } + @Specialization protected Object serialize(Object object, RConnection conn, int type, RNull version, RNull refhook) { return doSerializeToConnBase(object, conn, type, RRuntime.LOGICAL_NA, version, refhook); @@ -139,15 +167,16 @@ public class SerializeFunctions { return RDataFactory.createRawVector(data); } - @SuppressWarnings("unused") - @Fallback - protected Object serialize(Object object, Object conn, Object asciiLogical, Object version, Object refhook) { - throw RError.error(this, RError.Message.INVALID_OR_UNIMPLEMENTED_ARGUMENTS); - } } - @RBuiltin(name = "serializeb", kind = INTERNAL, parameterNames = {"object", "conn", "xdr", "version", "refhook"}, behavior = IO) + @RBuiltin(name = "serializeb", kind = INTERNAL, parameterNames = {"object", "con", "xdr", "version", "refhook"}, behavior = IO) public abstract static class SerializeB extends Adapter { + @Override + protected void createCasts(CastBuilder casts) { + connection(casts); + casts.arg("xdr").asLogicalVector().findFirst(); + } + @Specialization protected Object serializeB(Object object, RConnection conn, byte xdrLogical, RNull version, RNull refhook) { if (!RRuntime.fromLogical(xdrLogical)) { diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SetTimeLimit.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SetTimeLimit.java index 4eb04d2dfb..61e0a0edaf 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SetTimeLimit.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SetTimeLimit.java @@ -22,10 +22,12 @@ */ package com.oracle.truffle.r.nodes.builtin.base; +import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*; import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX; import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.r.nodes.builtin.CastBuilder; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; import com.oracle.truffle.r.runtime.builtins.RBuiltin; import com.oracle.truffle.r.runtime.data.RNull; @@ -33,9 +35,16 @@ import com.oracle.truffle.r.runtime.data.RNull; @RBuiltin(name = "setTimeLimit", kind = INTERNAL, parameterNames = {"cpu", "elapsed", "transient"}, behavior = COMPLEX) public abstract class SetTimeLimit extends RBuiltinNode { + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("cpu").asDoubleVector().findFirst(); + casts.arg("elapsed").asDoubleVector().findFirst(); + casts.arg("transient").asLogicalVector().findFirst().map(toBoolean()); + } + @SuppressWarnings("unused") @Specialization - protected RNull setTimeLimit(Object cpu, Object elapsed, Object trans) { + protected RNull setTimeLimit(double cpu, double elapsed, boolean trans) { // TODO: proper implementation sets timers checked during user interrupts that we currently // do not support return RNull.instance; diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Stop.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Stop.java index e8a0c744fb..2cdff7e1f4 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Stop.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Stop.java @@ -22,24 +22,34 @@ */ package com.oracle.truffle.r.nodes.builtin.base; +import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*; import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX; import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.r.nodes.builtin.CastBuilder; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; import com.oracle.truffle.r.runtime.RError; -import com.oracle.truffle.r.runtime.RRuntime; import com.oracle.truffle.r.runtime.builtins.RBuiltin; -import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector; +import com.oracle.truffle.r.runtime.data.RNull; -@RBuiltin(name = "stop", kind = INTERNAL, parameterNames = {"call.", "message"}, behavior = COMPLEX) +@RBuiltin(name = "stop", kind = INTERNAL, parameterNames = {"call", "message"}, behavior = COMPLEX) public abstract class Stop extends RBuiltinNode { + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("call").asLogicalVector().findFirst().map(toBoolean()); + casts.arg("message").mustBe(stringValue().or(nullValue())).asStringVector().mustBe(notEmpty(), RError.Message.INVALID_STRING_IN_STOP).findFirst(); + } + + @Specialization + protected Object stop(boolean call, @SuppressWarnings("unused") RNull msgVec) { + throw stop(call, ""); + } @Specialization - protected Object stop(byte call, RAbstractStringVector msgVec) { - assert msgVec.getLength() == 1; + protected RError stop(boolean call, String message) throws RError { CompilerDirectives.transferToInterpreter(); - throw RError.stop(RRuntime.fromLogical(call), RError.SHOW_CALLER2, RError.Message.GENERIC, msgVec.getDataAt(0)); + throw RError.stop(call, RError.SHOW_CALLER2, RError.Message.GENERIC, message); } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SysFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SysFunctions.java index c9ec3cf691..27809328cf 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SysFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SysFunctions.java @@ -22,6 +22,7 @@ */ package com.oracle.truffle.r.nodes.builtin.base; +import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*; import static com.oracle.truffle.r.runtime.RVisibility.CUSTOM; import static com.oracle.truffle.r.runtime.RVisibility.OFF; import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX; @@ -34,13 +35,13 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Map; -import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.frame.FrameInstance.FrameAccess; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.profiles.ConditionProfile; +import com.oracle.truffle.r.nodes.builtin.CastBuilder; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; import com.oracle.truffle.r.nodes.builtin.RBuiltinPackages; import com.oracle.truffle.r.runtime.RArguments; @@ -64,7 +65,6 @@ public class SysFunctions { @RBuiltin(name = "Sys.getpid", kind = INTERNAL, parameterNames = {}, behavior = READS_STATE) public abstract static class SysGetpid extends RBuiltinNode { - @Specialization @TruffleBoundary protected Object sysGetPid() { @@ -77,6 +77,12 @@ public class SysFunctions { public abstract static class SysGetenv extends RBuiltinNode { private final ConditionProfile zeroLengthProfile = ConditionProfile.createBinaryProfile(); + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("x").mustBe(stringValue(), RError.Message.ARGUMENT_WRONG_TYPE); + casts.arg("unset").mustBe(stringValue()).asStringVector().mustBe(size(1)); + } + @Specialization @TruffleBoundary protected Object sysGetEnv(RAbstractStringVector x, RAbstractStringVector unset) { @@ -110,11 +116,6 @@ public class SysFunctions { } } - @Specialization - protected Object sysGetEnvGeneric(@SuppressWarnings("unused") Object x, @SuppressWarnings("unused") Object unset) { - CompilerDirectives.transferToInterpreter(); - throw RError.error(this, RError.Message.WRONG_TYPE); - } } /** @@ -138,7 +139,6 @@ public class SysFunctions { // Now we can run the overrides RBuiltinPackages.loadDefaultPackageOverrides(RContext.getInstance().getNamespaceName()); } - System.console(); } } @@ -147,9 +147,17 @@ public class SysFunctions { @RBuiltin(name = "Sys.setenv", visibility = OFF, kind = INTERNAL, parameterNames = {"nm", "values"}, behavior = MODIFIES_STATE) public abstract static class SysSetEnv extends LoadNamespaceAdapter { + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("nm").mustBe(stringValue(), RError.Message.ARGUMENT_WRONG_TYPE); + casts.arg("values").mustBe(stringValue(), RError.Message.ARGUMENT_WRONG_TYPE); + } @Specialization protected RLogicalVector doSysSetEnv(VirtualFrame frame, RAbstractStringVector names, RAbstractStringVector values) { + if (names.getLength() != values.getLength()) { + throw RError.error(this, RError.Message.ARGUMENT_WRONG_LENGTH); + } checkNSLoad(frame, names, values, true); return doSysSetEnv(names, values); } @@ -168,6 +176,10 @@ public class SysFunctions { @RBuiltin(name = "Sys.unsetenv", visibility = OFF, kind = INTERNAL, parameterNames = {"x"}, behavior = READS_STATE) public abstract static class SysUnSetEnv extends LoadNamespaceAdapter { + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("x").mustBe(stringValue(), RError.Message.ARGUMENT_WRONG_TYPE); + } @Specialization protected RLogicalVector doSysUnSetEnv(VirtualFrame frame, RAbstractStringVector names) { @@ -189,6 +201,10 @@ public class SysFunctions { @RBuiltin(name = "Sys.sleep", visibility = OFF, kind = INTERNAL, parameterNames = {"time"}, behavior = COMPLEX) public abstract static class SysSleep extends RBuiltinNode { + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("time").asDoubleVector().findFirst().mustBe(gte(0.0).and(eq(Double.NaN).not())); + } @Specialization @TruffleBoundary @@ -197,44 +213,10 @@ public class SysFunctions { return RNull.instance; } - @Specialization - @TruffleBoundary - protected Object sysSleep(String secondsString) { - long millis = convertToMillis(checkValidString(secondsString)); - sleep(millis); - return RNull.instance; - } - - @Specialization(guards = "lengthOne(secondsVector)") - @TruffleBoundary - protected Object sysSleep(RStringVector secondsVector) { - long millis = convertToMillis(checkValidString(secondsVector.getDataAt(0))); - sleep(millis); - return RNull.instance; - } - - protected static boolean lengthOne(RStringVector vec) { - return vec.getLength() == 1; - } - - @Specialization - @TruffleBoundary - protected Object sysSleep(@SuppressWarnings("unused") Object arg) { - throw RError.error(this, RError.Message.INVALID_VALUE, "time"); - } - private static long convertToMillis(double d) { return (long) (d * 1000); } - private double checkValidString(String s) { - try { - return Double.parseDouble(s); - } catch (NumberFormatException ex) { - throw RError.error(this, RError.Message.INVALID_VALUE, "time"); - } - } - private static void sleep(long millis) { try { Thread.sleep(millis); @@ -249,16 +231,14 @@ public class SysFunctions { */ @RBuiltin(name = "Sys.readlink", kind = INTERNAL, parameterNames = {"paths"}, behavior = IO) public abstract static class SysReadlink extends RBuiltinNode { - - @Specialization - @TruffleBoundary - protected Object sysReadlink(String path) { - return RDataFactory.createStringVector(doSysReadLink(path)); + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("paths").mustBe(stringValue()); } @Specialization @TruffleBoundary - protected Object sysReadlink(RStringVector vector) { + protected Object sysReadlink(RAbstractStringVector vector) { String[] paths = new String[vector.getLength()]; boolean complete = RDataFactory.COMPLETE_VECTOR; for (int i = 0; i < paths.length; i++) { @@ -289,19 +269,20 @@ public class SysFunctions { return s; } - @Specialization - protected Object sysReadlinkGeneric(@SuppressWarnings("unused") Object path) { - CompilerDirectives.transferToInterpreter(); - throw RError.error(this, RError.Message.INVALID_ARGUMENT, "paths"); - } } - // TODO implement @RBuiltin(name = "Sys.chmod", visibility = OFF, kind = INTERNAL, parameterNames = {"paths", "octmode", "use_umask"}, behavior = IO) public abstract static class SysChmod extends RBuiltinNode { + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("paths").mustBe(stringValue()); + casts.arg("octmode").asIntegerVector().mustBe(notEmpty(), RError.Message.MODE_LENGTH_ONE); + casts.arg("use_umask").asLogicalVector().findFirst().notNA().map(toBoolean()); + } + @Specialization @TruffleBoundary - protected RLogicalVector sysChmod(RAbstractStringVector pathVec, RAbstractIntVector octmode, @SuppressWarnings("unused") byte useUmask) { + protected RLogicalVector sysChmod(RAbstractStringVector pathVec, RAbstractIntVector octmode, @SuppressWarnings("unused") boolean useUmask) { byte[] data = new byte[pathVec.getLength()]; for (int i = 0; i < data.length; i++) { String path = Utils.tildeExpand(pathVec.getDataAt(i)); @@ -318,10 +299,15 @@ public class SysFunctions { // TODO implement @RBuiltin(name = "Sys.umask", visibility = CUSTOM, kind = INTERNAL, parameterNames = {"octmode"}, behavior = COMPLEX) public abstract static class SysUmask extends RBuiltinNode { + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("octmode").asIntegerVector().findFirst(); + } + @SuppressWarnings("unused") @Specialization @TruffleBoundary - protected Object sysChmod(Object octmode) { + protected Object sysUmask(int octmode) { throw RError.nyi(this, "Sys.umask"); } } @@ -361,10 +347,15 @@ public class SysFunctions { @RBuiltin(name = "Sys.glob", kind = INTERNAL, parameterNames = {"paths", "dirmask"}, behavior = IO) public abstract static class SysGlob extends RBuiltinNode { + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("paths").mustBe(stringValue()).asStringVector(); + casts.arg("dirmask").asLogicalVector().findFirst().notNA().map(toBoolean()); + } @Specialization @TruffleBoundary - protected Object sysGlob(RAbstractStringVector pathVec, @SuppressWarnings("unused") byte dirMask) { + protected Object sysGlob(RAbstractStringVector pathVec, @SuppressWarnings("unused") boolean dirMask) { ArrayList<String> matches = new ArrayList<>(); // Sys.glob closure already called path.expand for (int i = 0; i < pathVec.getLength(); i++) { diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Warning.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Warning.java index 6ee52996d3..64b2b539db 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Warning.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Warning.java @@ -22,58 +22,38 @@ */ package com.oracle.truffle.r.nodes.builtin.base; +import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*; import static com.oracle.truffle.r.runtime.RVisibility.OFF; import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX; import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.r.nodes.builtin.CastBuilder; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; -import com.oracle.truffle.r.nodes.unary.CastStringNode; -import com.oracle.truffle.r.nodes.unary.CastStringNodeGen; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RErrorHandling; -import com.oracle.truffle.r.runtime.RRuntime; import com.oracle.truffle.r.runtime.builtins.RBuiltin; @RBuiltin(name = "warning", visibility = OFF, kind = INTERNAL, parameterNames = {"call", "immediate", "nobreaks", "message"}, behavior = COMPLEX) public abstract class Warning extends RBuiltinNode { - @Child private CastStringNode castString; - - private Object castString(Object operand) { - if (castString == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - castString = insert(CastStringNodeGen.create(false, false, false)); - } - return castString.execute(operand); - } - @Override protected void createCasts(CastBuilder casts) { - casts.toLogical(0); - casts.toLogical(1); - casts.toLogical(2); + casts.arg("call").asLogicalVector().findFirst().map(toBoolean()); + casts.arg("immediate").asLogicalVector().findFirst().map(toBoolean()); + casts.arg("nobreaks").asLogicalVector().findFirst().map(toBoolean()); + casts.arg("message").mustBe(stringValue()).asStringVector().mustBe(notEmpty(), RError.Message.INVALID_STRING_IN_WARNING).findFirst(); + } @Specialization @TruffleBoundary - protected String warning(byte callL, byte immediateL, byte noBreakWarningL, Object messageObj) { - String message = RRuntime.asString(castString(messageObj)); - boolean call = RRuntime.fromLogical(callL); - boolean immediate = RRuntime.fromLogical(immediateL); - boolean noBreakWarning = RRuntime.fromLogical(noBreakWarningL); + protected String warning(boolean call, boolean immediate, boolean noBreakWarning, String message) { + CompilerDirectives.transferToInterpreter(); RErrorHandling.warningcallInternal(call, message, immediate, noBreakWarning); return message; } - @SuppressWarnings("unused") - @Fallback - @TruffleBoundary - protected String warning(Object callL, Object immediateL, Object noBreakWarningL, Object message) { - throw RError.error(this, RError.Message.INVALID_OR_UNIMPLEMENTED_ARGUMENTS); - } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/WithVisible.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/WithVisible.java index abc807d011..ac1c170631 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/WithVisible.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/WithVisible.java @@ -22,40 +22,43 @@ */ package com.oracle.truffle.r.nodes.builtin.base; +import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*; import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX; import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.r.nodes.builtin.CastBuilder; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; +import com.oracle.truffle.r.runtime.FastROptions; import com.oracle.truffle.r.runtime.RError; -import com.oracle.truffle.r.runtime.RError.Message; import com.oracle.truffle.r.runtime.RRuntime; import com.oracle.truffle.r.runtime.builtins.RBuiltin; import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.data.RDataFactory; import com.oracle.truffle.r.runtime.data.RList; -import com.oracle.truffle.r.runtime.data.RMissing; import com.oracle.truffle.r.runtime.data.RStringVector; +// TODO The base package manual says this is a primitive but GNU R implements it as .Internal. +// That causes problems as the .Internal adds another layer of visibility setting that +// gets the wrong result. I believe that the only way to handle it as a .Internal would be to +// set noEvalArgs and evaluate the argument here and set the visibility explicitly. @RBuiltin(name = "withVisible", kind = PRIMITIVE, parameterNames = "x", behavior = COMPLEX) public abstract class WithVisible extends RBuiltinNode { private static final RStringVector LISTNAMES = RDataFactory.createStringVector(new String[]{"value", "visible"}, RDataFactory.COMPLETE_VECTOR); - @Specialization(guards = "!isRMissing(x)") + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("x").mustBe(missingValue().not(), RError.Message.ARGUMENT_MISSING, "x"); + } + + @Specialization protected RList withVisible(Object x) { - // (LS) temporarily disabled to enable parallel benchmarks - // if (FastROptions.IgnoreVisibility.getBooleanValue()) { - // RError.warning(this, RError.Message.GENERIC, "using withVisible with IgnoreVisibility"); - // } + if (FastROptions.IgnoreVisibility.getBooleanValue()) { + RError.warning(this, RError.Message.GENERIC, "using withVisible with IgnoreVisibility"); + } Object[] data = new Object[]{x, RRuntime.asLogical(RContext.getInstance().isVisible())}; - // Visibility is changed by the evaluation (else this code would not work), - // so we have to force it back on. return RDataFactory.createList(data, LISTNAMES); } - @Specialization - protected RList withVisible(@SuppressWarnings("unused") RMissing x) { - throw RError.error(this, Message.ARGUMENT_MISSING, "x"); - } } 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 108a51926b..3015d06283 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 @@ -262,7 +262,6 @@ public final class RError extends RuntimeException { MUST_BE_SQUARE_COMPATIBLE("'%s' (%d x %d) must be compatible with '%' (%d x %d)"), INVALID_TFB("invalid (to - from)/by in seq(.)"), WRONG_SIGN_IN_BY("wrong sign in 'by' argument"), - WRONG_TYPE("wrong type of argument"), BY_TOO_SMALL("'by' argument is much too small"), INCORRECT_SUBSCRIPTS("incorrect number of subscripts"), INCORRECT_SUBSCRIPTS_MATRIX("incorrect number of subscripts on matrix"), @@ -301,6 +300,7 @@ public final class RError extends RuntimeException { ARGUMENT_LENGTHS_DIFFER("argument lengths differ"), ZERO_LENGTH_PATTERN("zero-length pattern"), UNSUPPORTED_MODE("unsupported mode"), + MODE_LENGTH_ONE("'mode' must be of length at least one"), ALL_CONNECTIONS_IN_USE("all connections are in use"), CANNOT_READ_CONNECTION("cannot read from this connection"), CONNECTION_NOT_OPEN_READ("connection not open for reading"), @@ -326,6 +326,8 @@ public final class RError extends RuntimeException { NAMES_NONVECTOR("names() applied to a non-vector"), NAMES_LONGER("'names' attribute [%d] must be the same length as the vector [%d]"), ONLY_FIRST_VARIABLE_NAME("only the first element is used as variable name"), + INVALID_FIRST_FILENAME("invalid first filename"), + INVALID_SECOND_FILENAME("invalid second filename"), INVALID_FIRST_ARGUMENT("invalid first argument"), NO_ENCLOSING_ENVIRONMENT("no enclosing environment"), ASSIGN_EMPTY("cannot assign values in the empty environment"), @@ -337,6 +339,9 @@ public final class RError extends RuntimeException { COERCING_LHS_TO_LIST("Coercing LHS to a list"), ARGUMENT_NOT_LIST("argument not a list"), FIRST_ARGUMENT_NOT_NAMED_LIST("first argument must be a named list"), + FIRST_ARGUMENT_NOT_CHARVEC("first argument must be a character vector"), + FIRST_ARGUMENT_NOT_FILENAME("first argument must be a filename"), + ASCII_NOT_LOGICAL("'ascii' must be logical"), LIST_NAMES_SAME_LENGTH("names(x) must be a character vector of the same length as x"), DIMS_CONTAIN_NEGATIVE_VALUES("the dims contain negative values"), NEGATIVE_LENGTH_VECTORS_NOT_ALLOWED("negative length vectors are not allowed"), @@ -415,6 +420,8 @@ public final class RError extends RuntimeException { CANNOT_COERCE("cannot coerce type '%s' to vector of type '%s'"), ARGUMENT_ONLY_FIRST("argument '%s' has length > 1 and only the first element will be used"), ARGUMENT_ONLY_FIRST_1("only the first element of '%s' argument used"), + ARGUMENT_WRONG_LENGTH("wrong length for argument"), + ARGUMENT_WRONG_TYPE("wrong type for argument"), CANNOT_OPEN_FILE("cannot open file '%s': %s"), NOT_CONNECTION("'%s' is not a connection"), UNUSED_TEXTCONN("closing unused text connection %d (%s)"), @@ -692,6 +699,11 @@ public final class RError extends RuntimeException { SYSTEM_CHAR_ARG("non-empty character argument expected"), SYSTEM_INTERN_NOT_NA("'intern' must be logical and not NA"), NO_SUCH_FILE("cannot open file '%s': No such file or directory"), + NON_STRING_ARG_TO_INTERNAL_PASTE("non-string argument to Internal paste"), + INVALID_STRING_IN_STOP(" [invalid string in stop(.)]"), + INVALID_STRING_IN_WARNING(" [invalid string in warning(.)]"), + ERR_MSG_MUST_BE_STRING("error message must be a character string"), + ERR_MSG_BAD("bad error message"), CANNOT_BE_LENGTH("'%s' cannot be of length %d"); public final String message; -- GitLab