From c5349ea8ee1858cf58d1d70af88406f7448be95e Mon Sep 17 00:00:00 2001 From: Lukas Stadler <lukas.stadler@oracle.com> Date: Thu, 12 Oct 2017 18:20:37 +0200 Subject: [PATCH] handle collation and timezones based on env variables --- .../builtin/base/DatePOSIXFunctions.java | 8 +- .../truffle/r/nodes/builtin/base/IConv.java | 12 +- .../r/nodes/builtin/base/LocaleFunctions.java | 96 +++------- .../truffle/r/nodes/builtin/base/Order.java | 179 +++++++++--------- .../truffle/r/nodes/builtin/base/Rank.java | 6 +- .../oracle/truffle/r/runtime/REnvVars.java | 35 ++-- .../com/oracle/truffle/r/runtime/RError.java | 1 + .../com/oracle/truffle/r/runtime/RLocale.java | 179 ++++++++++++++++++ .../r/runtime/context/ChildContextInfo.java | 30 +-- .../truffle/r/runtime/context/RContext.java | 33 ++-- .../r/test/builtins/TestBuiltin_order.java | 2 + .../truffle/r/test/generate/FastRSession.java | 7 +- .../r/test/generate/GnuROneShotRSession.java | 2 + 13 files changed, 358 insertions(+), 232 deletions(-) create mode 100644 com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RLocale.java diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DatePOSIXFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DatePOSIXFunctions.java index 56bb231456..769b288779 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DatePOSIXFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DatePOSIXFunctions.java @@ -189,7 +189,7 @@ public class DatePOSIXFunctions { protected RList asPOSIXlt(RAbstractDoubleVector x, String tz) { TimeZone zone; if (tz.isEmpty()) { - zone = RContext.getInstance().getSystemTimeZone(); + zone = RContext.getInstance().stateREnvVars.getSystemTimeZone(); } else { zone = TimeZone.getTimeZone(tz); } @@ -235,7 +235,7 @@ public class DatePOSIXFunctions { RAbstractVector yearVector = (RAbstractVector) RRuntime.convertScalarVectors(x.getDataAt(5)); TimeZone zone; if (tz.isEmpty()) { - zone = RContext.getInstance().getSystemTimeZone(); + zone = RContext.getInstance().stateREnvVars.getSystemTimeZone(); } else { zone = TimeZone.getTimeZone(tz); } @@ -360,7 +360,7 @@ public class DatePOSIXFunctions { builder.appendLiteral(' ').appendZoneText(TextStyle.SHORT); } } else { - zone = RContext.getInstance().getSystemTimeZone().toZoneId(); + zone = RContext.getInstance().stateREnvVars.getSystemTimeZone().toZoneId(); } DateTimeFormatter[] formatters = new DateTimeFormatter[builders.length]; @@ -406,7 +406,7 @@ public class DatePOSIXFunctions { TimeZone zone; String zoneString = RRuntime.asString(tz); if (zoneString.isEmpty()) { - zone = RContext.getInstance().getSystemTimeZone(); + zone = RContext.getInstance().stateREnvVars.getSystemTimeZone(); } else { zone = TimeZone.getTimeZone(zoneString); } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IConv.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IConv.java index 4c2723335e..7a80372f4e 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IConv.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IConv.java @@ -46,8 +46,10 @@ import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RError.Message; import com.oracle.truffle.r.runtime.RInternalError; +import com.oracle.truffle.r.runtime.RLocale; 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.RStringVector; import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector; @@ -79,7 +81,7 @@ public abstract class IConv extends RBuiltinNode.Arg6 { Charset fromCharset = getCharset(from, from, to); Charset toCharset = getCharset(to, from, to); boolean complete = x.isComplete(); - if (fromCharset == StandardCharsets.UTF_8 && toCharset == StandardCharsets.UTF_8) { + if ((fromCharset == StandardCharsets.UTF_8 || fromCharset == StandardCharsets.UTF_16) && (toCharset == StandardCharsets.UTF_8 || toCharset == StandardCharsets.UTF_16)) { // this conversion cannot change anything return x; } else { @@ -117,13 +119,15 @@ public abstract class IConv extends RBuiltinNode.Arg6 { } private Charset getCharset(String name, String from, String to) { - String toCharsetName = "".equals(name) ? LocaleFunctions.LC.CTYPE.getValue() : name; + if (name.isEmpty()) { + return RContext.getInstance().stateRLocale.getCharset(RLocale.CTYPE); + } Charset toCharset; - if ("C".equals(toCharsetName)) { + if ("C".equals(name)) { toCharset = StandardCharsets.US_ASCII; } else { try { - toCharset = Charset.forName(toCharsetName); + toCharset = Charset.forName(name); } catch (IllegalCharsetNameException | UnsupportedCharsetException e) { throw error(Message.UNSUPPORTED_ENCODING_CONVERSION, from, to); } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LocaleFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LocaleFunctions.java index 49df208d7e..bdc7684361 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LocaleFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LocaleFunctions.java @@ -33,7 +33,6 @@ import static com.oracle.truffle.r.runtime.builtins.RBehavior.READS_STATE; import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL; import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE; -import java.nio.charset.Charset; import java.util.Locale; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; @@ -41,6 +40,7 @@ import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.r.nodes.builtin.NodeWithArgumentCasts.Casts; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; import com.oracle.truffle.r.runtime.RError; +import com.oracle.truffle.r.runtime.RLocale; import com.oracle.truffle.r.runtime.RRuntime; import com.oracle.truffle.r.runtime.builtins.RBuiltin; import com.oracle.truffle.r.runtime.context.RContext; @@ -52,49 +52,9 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector; public class LocaleFunctions { - enum LC { - COLLATE(stdDefault()), - CTYPE(stdDefault()), - MONETARY(stdDefault()), - NUMERIC("C"), - TIME(stdDefault()), - MESSAGES(stdDefault()), - PAPER(""), - MEASUREMENT(""); - - private String value; - private String defaultValue; - - LC(String defaultValue) { - this.defaultValue = defaultValue; - } - - static String stdDefault() { - String defLocale = Locale.getDefault().toString() + "_"; - String defCharSet = Charset.defaultCharset().name(); - return defLocale + defCharSet; - } - - private String getLCEnvVar() { - if (value != null) { - return value; - } - String val = RContext.getInstance().stateREnvVars.get("LC_" + name()); - if (val == null) { - return defaultValue; - } else { - return val; - } - } - - private static boolean isAll(int category) { - return category == 1; - } - - private static LC getLCCategory(int category) { - return LC.values()[category - 2]; - } - } + private static final int LC_ALL = 1; + private static final int MAPPING_START = 2; + private static final RLocale[] MAPPING = new RLocale[]{RLocale.COLLATE, RLocale.CTYPE, RLocale.MONETARY, RLocale.NUMERIC, RLocale.TIME, RLocale.MESSAGES, RLocale.PAPER, RLocale.MEASUREMENT}; @RBuiltin(name = "Sys.getlocale", kind = INTERNAL, parameterNames = {"category"}, behavior = READS_STATE) public abstract static class GetLocale extends RBuiltinNode.Arg1 { @@ -104,30 +64,33 @@ public class LocaleFunctions { CastsHelper.category(casts); } - private static final int[] ALL_CATEGORIES = new int[]{3, 2, 4, 5, 6, 7, 8, 9}; - @Specialization @TruffleBoundary - protected Object getLocale(int category) { - return RDataFactory.createStringVector(getLocaleData(category)); - } - - protected String getLocaleData(int category) { - if (LC.isAll(category)) { - // "LC_ALL" + protected static Object getLocale(int category) { + RContext context = RContext.getInstance(); + if (category == LC_ALL) { + String singleRep = RLocale.COLLATE.getRepresentation(context); + for (RLocale locale : RLocale.values()) { + if (locale.isListed() && !locale.getRepresentation(context).equals(singleRep)) { + singleRep = null; + break; + } + } + if (singleRep != null) { + return singleRep; + } StringBuilder sb = new StringBuilder(); - for (int i = 0; i < ALL_CATEGORIES.length; i++) { - String d = getLocaleData(ALL_CATEGORIES[i]); - if (d.length() > 0) { - sb.append(d); - if (i != ALL_CATEGORIES.length - 1) { + for (RLocale locale : RLocale.values()) { + if (locale.isListed()) { + if (sb.length() > 0) { sb.append('/'); } + sb.append(locale.getRepresentation(context)); } } return sb.toString(); } else { - return LC.getLCCategory(category).getLCEnvVar(); + return MAPPING[category - MAPPING_START].getRepresentation(context); } } } @@ -141,17 +104,20 @@ public class LocaleFunctions { casts.arg("locale").mustBe(stringValue()).asStringVector().mustBe(singleElement()).findFirst(); } + private static final RLocale[] SET_ALL = new RLocale[]{RLocale.COLLATE, RLocale.CTYPE, RLocale.MONETARY, RLocale.TIME}; + @Specialization @TruffleBoundary - protected Object setLocale(int category, String locale) { - if (LC.isAll(category)) { - for (LC lc : LC.values()) { - lc.value = locale; + protected Object setLocale(int category, String value) { + RContext context = RContext.getInstance(); + if (category == LC_ALL) { + for (RLocale locale : SET_ALL) { + context.stateRLocale.setLocale(locale, value); } } else { - LC.getLCCategory(category).value = locale; + context.stateRLocale.setLocale(MAPPING[category - MAPPING_START], value); } - return locale; + return GetLocale.getLocale(category); } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Order.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Order.java index a82f8b5c89..ac41e8e535 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Order.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Order.java @@ -18,9 +18,11 @@ import static com.oracle.truffle.r.runtime.RError.Message.INVALID_LOGICAL; import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE; import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL; +import java.text.CollationKey; import java.text.Collator; import java.text.ParseException; import java.text.RuleBasedCollator; +import java.util.Locale; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; @@ -39,9 +41,11 @@ import com.oracle.truffle.r.nodes.unary.CastToVectorNode; import com.oracle.truffle.r.nodes.unary.CastToVectorNodeGen; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RInternalError; +import com.oracle.truffle.r.runtime.RLocale; 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.context.RContext; import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames; import com.oracle.truffle.r.runtime.data.RComplex; import com.oracle.truffle.r.runtime.data.RDataFactory; @@ -76,17 +80,13 @@ public abstract class Order extends RPrecedenceBuiltinNode { private static final int[] SINCS = {1073790977, 268460033, 67121153, 16783361, 4197377, 1050113, 262913, 65921, 16577, 4193, 1073, 281, 77, 23, 8, 1, 0}; - private RIntVector executeOrderVector1(RAbstractVector v, byte naLast, boolean dec) { - return executeOrderVector1(v, naLast, dec, false); - } - - private RIntVector executeOrderVector1(RAbstractVector vIn, byte naLast, boolean dec, boolean needsStringCollation) { + private RIntVector executeOrderVector1(RAbstractVector vIn, byte naLast, boolean dec) { RAbstractVector v = vectorProfile.profile(vIn); int n = v.getLength(); reportWork(n); int[] indx = createIndexes(v, n, naLast); - initOrderVector1(needsStringCollation).execute(indx, v, naLast, dec, null); + initOrderVector1().execute(indx, v, naLast, dec, true); for (int i = 0; i < indx.length; i++) { indx[i] = indx[i] + 1; } @@ -134,10 +134,10 @@ public abstract class Order extends RPrecedenceBuiltinNode { return result; } - private OrderVector1Node initOrderVector1(boolean needsStringCollation) { - if (orderVector1Node == null || needsStringCollation && !orderVector1Node.needsStringCollation) { + private OrderVector1Node initOrderVector1() { + if (orderVector1Node == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); - orderVector1Node = insert(OrderVector1NodeGen.create(needsStringCollation)); + orderVector1Node = insert(OrderVector1NodeGen.create()); } return orderVector1Node; } @@ -198,35 +198,19 @@ public abstract class Order extends RPrecedenceBuiltinNode { } @Specialization(guards = {"oneVec(args)", "isFirstStringPrecedence(args)"}) - Object orderString(byte naLast, boolean decreasing, RArgsValuesAndNames args, - @Cached("create()") BranchProfile collationProfile) { + Object orderString(byte naLast, boolean decreasing, RArgsValuesAndNames args) { RAbstractStringVector v = (RAbstractStringVector) castVector(args.getArgument(0)); - int n = v.getLength(); - boolean needsCollation = false; - outer: for (int i = 0; i < n; i++) { - String str = v.getDataAt(i); - for (int i2 = 0; i2 < str.length(); i2++) { - char c = str.charAt(i2); - if (c > 127) { - collationProfile.enter(); - needsCollation = true; - break outer; - } - } - } - - return executeOrderVector1(v, naLast, decreasing, needsCollation); + return executeOrderVector1(v, naLast, decreasing); } - @Specialization(guards = {"oneVec(args)", "isFirstComplexPrecedence( args)"}) + @Specialization(guards = {"oneVec(args)", "isFirstComplexPrecedence(args)"}) Object orderComplex(byte naLast, boolean decreasing, RArgsValuesAndNames args) { RAbstractComplexVector v = (RAbstractComplexVector) castVector(args.getArgument(0)); return executeOrderVector1(v, naLast, decreasing); } - @SuppressWarnings("unused") - @Specialization(guards = {"oneVec(args)", "isFirstListPrecedence( args)"}) - Object orderList(byte naLast, boolean decreasing, RArgsValuesAndNames args) { + @Specialization(guards = {"oneVec(args)", "isFirstListPrecedence(args)"}) + Object orderList(@SuppressWarnings("unused") byte naLast, @SuppressWarnings("unused") boolean decreasing, RArgsValuesAndNames args) { /* * Lists are not actually supported by GnuR but there is a corner case of a length < 2 list * that produces a result in GnuR and there is a unit test for that (when called via @@ -261,6 +245,9 @@ public abstract class Order extends RPrecedenceBuiltinNode { return n; } + /* + * TODO: multi-element order does not honor string collation. + */ @Specialization(guards = {"!oneVec(args)", "!noVec(args)"}) Object orderMulti(byte naLast, boolean decreasing, RArgsValuesAndNames args, @Cached("createEqualityProfile()") ValueProfile lengthProfile) { @@ -320,25 +307,20 @@ public abstract class Order extends RPrecedenceBuiltinNode { * Also used by {@link Rank}, where the "rho" parameter is not null. TODO handle S4 objects * (which involves rho) */ + abstract static class OrderVector1Node extends RBaseNode { private final ConditionProfile decProfile = ConditionProfile.createBinaryProfile(); - private final boolean needsStringCollation; - - protected OrderVector1Node(boolean needsStringCollation) { - this.needsStringCollation = needsStringCollation; - } - - public abstract Object execute(int[] v, Object dv, byte naLast, boolean dec, Object rho); + public abstract Object execute(int[] v, Object dv, byte naLast, boolean dec, boolean sortNA); @Specialization - protected Object orderVector1(int[] indx, RAbstractIntVector dv, byte naLast, boolean decreasing, Object rho) { + protected Object orderVector1(int[] indx, RAbstractIntVector dv, byte naLast, boolean decreasing, boolean sortNA) { if (indx.length < 2) { return indx; } int lo = 0; int hi = indx.length - 1; - if (rho == null) { + if (sortNA) { int numNa = 0; if (!dv.isComplete() && !RRuntime.isNA(naLast)) { boolean[] isNa = new boolean[indx.length]; @@ -370,13 +352,13 @@ public abstract class Order extends RPrecedenceBuiltinNode { } @Specialization - protected Object orderVector1(int[] indx, RAbstractDoubleVector dv, byte naLast, boolean decreasing, Object rho) { + protected Object orderVector1(int[] indx, RAbstractDoubleVector dv, byte naLast, boolean decreasing, boolean sortNA) { if (indx.length < 2) { return indx; } int lo = 0; int hi = indx.length - 1; - if (rho == null && !RRuntime.isNA(naLast)) { + if (sortNA && !RRuntime.isNA(naLast)) { int numNa = 0; boolean[] isNa = new boolean[indx.length]; for (int i = 0; i < isNa.length; i++) { @@ -406,13 +388,13 @@ public abstract class Order extends RPrecedenceBuiltinNode { } @Specialization - protected Object orderVector1(int[] indx, RAbstractStringVector dv, byte naLast, boolean decreasing, Object rho) { + protected Object orderVector1(int[] indx, RAbstractStringVector dv, byte naLast, boolean decreasing, boolean sortNA) { if (indx.length < 2) { return indx; } int lo = 0; int hi = indx.length - 1; - if (rho == null) { + if (sortNA) { int numNa = 0; if (!dv.isComplete() && !RRuntime.isNA(naLast)) { boolean[] isNa = new boolean[indx.length]; @@ -444,13 +426,13 @@ public abstract class Order extends RPrecedenceBuiltinNode { } @Specialization - protected Object orderVector1(int[] indx, RAbstractComplexVector dv, byte naLast, boolean decreasing, Object rho) { + protected Object orderVector1(int[] indx, RAbstractComplexVector dv, byte naLast, boolean decreasing, boolean sortNA) { if (indx.length < 2) { return indx; } int lo = 0; int hi = indx.length - 1; - if (rho == null) { + if (sortNA) { int numNa = 0; if (!dv.isComplete() && !RRuntime.isNA(naLast)) { boolean[] isNa = new boolean[indx.length]; @@ -483,7 +465,7 @@ public abstract class Order extends RPrecedenceBuiltinNode { @SuppressWarnings("unused") @Specialization - protected Object orderVector1(int[] indx, RList dv, byte naLast, boolean decreasing, Object rho) { + protected Object orderVector1(int[] indx, RList dv, byte naLast, boolean decreasing, boolean sortNA) { /* Only needed to satisfy .Internal(rank) in unit test */ return indx; } @@ -544,61 +526,76 @@ public abstract class Order extends RPrecedenceBuiltinNode { } } + @TruffleBoundary private void sort(int[] indx, RAbstractStringVector dv, int lo, int hi, boolean dec) { - Collator collator = createCollator(); int t = 0; for (; SINCS[t] > hi - lo + 1; t++) { } - for (int h = SINCS[t]; t < 16; h = SINCS[++t]) { - for (int i = lo + h; i <= hi; i++) { - int itmp = indx[i]; - int j = i; - while (j >= lo + h) { - int a = indx[j - h]; - int b = itmp; - int c = compareString(collator, dv.getDataAt(a), dv.getDataAt(b)); - if (decProfile.profile(dec)) { - if (!(c < 0 || (c == 0 && a > b))) { - break; - } - } else { - if (!(c > 0 || (c == 0 && a > b))) { - break; + + Locale locale = RContext.getInstance().stateRLocale.getLocale(RLocale.COLLATE); + if (locale == Locale.ROOT) { + // simple comparison based on numeric value of characters + for (int h = SINCS[t]; t < 16; h = SINCS[++t]) { + for (int i = lo + h; i <= hi; i++) { + int itmp = indx[i]; + int j = i; + while (j >= lo + h) { + int a = indx[j - h]; + int b = itmp; + int c = dv.getDataAt(a).compareTo(dv.getDataAt(b)); + if (decProfile.profile(dec)) { + if (!(c < 0 || (c == 0 && a > b))) { + break; + } + } else { + if (!(c > 0 || (c == 0 && a > b))) { + break; + } } + indx[j] = indx[j - h]; + j -= h; } - indx[j] = indx[j - h]; - j -= h; + indx[j] = itmp; } - indx[j] = itmp; } - } - } - - private int compareString(Collator collator, String dataAt, String dataAt2) { - if (needsStringCollation) { - return compare(collator, dataAt, dataAt2); } else { - return dataAt.compareToIgnoreCase(dataAt2); - } - } - - @TruffleBoundary - private static int compare(Collator collator, String dataAt, String dataAt2) { - return collator.compare(dataAt, dataAt2); - } + int length = dv.getLength(); + Collator baseCollator = Collator.getInstance(locale); + String rules = ((RuleBasedCollator) baseCollator).getRules(); + Collator collator; + try { + collator = new RuleBasedCollator(rules.replaceAll("<'\u005f'", "<' '<'\u005f'")); + } catch (ParseException e) { + throw RInternalError.shouldNotReachHere(e); + } + CollationKey[] entries = new CollationKey[length]; + for (int i = 0; i < length; i++) { + entries[i] = collator.getCollationKey(dv.getDataAt(i)); + } - @TruffleBoundary - private Collator createCollator() { - if (!needsStringCollation) { - return null; - } - // add rule for space before '_' - Collator collator = Collator.getInstance(); - String rules = ((RuleBasedCollator) collator).getRules(); - try { - return new RuleBasedCollator(rules.replaceAll("<'\u005f'", "<' '<'\u005f'")); - } catch (ParseException e) { - throw RInternalError.shouldNotReachHere(e); + for (int h = SINCS[t]; t < 16; h = SINCS[++t]) { + for (int i = lo + h; i <= hi; i++) { + int itmp = indx[i]; + int j = i; + while (j >= lo + h) { + int a = indx[j - h]; + int b = itmp; + int c = entries[a].compareTo(entries[b]); + if (decProfile.profile(dec)) { + if (!(c < 0 || (c == 0 && a > b))) { + break; + } + } else { + if (!(c > 0 || (c == 0 && a > b))) { + break; + } + } + indx[j] = indx[j - h]; + j -= h; + } + indx[j] = itmp; + } + } } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Rank.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Rank.java index d9c48f7c88..15e8e71aa7 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Rank.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Rank.java @@ -52,8 +52,6 @@ public abstract class Rank extends RBuiltinNode.Arg3 { @Child private Order.CmpNode orderCmpNode; private final BranchProfile errorProfile = BranchProfile.create(); - private static final Object rho = new Object(); - private enum TiesKind { AVERAGE, MAX, @@ -76,7 +74,7 @@ public abstract class Rank extends RBuiltinNode.Arg3 { private Order.OrderVector1Node initOrderVector1() { if (orderVector1Node == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); - orderVector1Node = insert(OrderVector1NodeGen.create(false)); + orderVector1Node = insert(OrderVector1NodeGen.create()); } return orderVector1Node; } @@ -112,7 +110,7 @@ public abstract class Rank extends RBuiltinNode.Arg3 { indx[i] = i; } RAbstractVector x = xa instanceof RAbstractLogicalVector ? xa.castSafe(RType.Integer, isNAProfile) : xa; - initOrderVector1().execute(indx, x, RRuntime.LOGICAL_TRUE, false, rho); + initOrderVector1().execute(indx, x, RRuntime.LOGICAL_TRUE, false, false); initOrderCmp(); int j; for (int i = 0; i < n; i = j + 1) { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvVars.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvVars.java index c5c06f0b1b..1300b87938 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvVars.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvVars.java @@ -35,6 +35,7 @@ import java.security.CodeSource; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.TimeZone; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.vm.PolyglotEngine; @@ -53,15 +54,14 @@ import com.oracle.truffle.r.runtime.ffi.BaseRFFI; */ public final class REnvVars implements RContext.ContextState { - private final Map<String, String> envVars = new HashMap<>(System.getenv()); + private final HashMap<String, String> envVars; - private REnvVars() { + private REnvVars(Map<String, String> initialEnvVars) { + envVars = new HashMap<>(initialEnvVars); } @Override public RContext.ContextState initialize(RContext context) { - // explicit environment settings in nested contexts must be installed first - checkExplicitEnvSettings(context); RCmdOptions cmdOptions = context.getCmdOptions(); // If running Rscript, R_DEFAULT_PACKAGES may need to be set String defaultPackages = cmdOptions.getString(RCmdOption.DEFAULT_PACKAGES); @@ -125,23 +125,8 @@ public final class REnvVars implements RContext.ContextState { return this; } - public static REnvVars newContextState() { - return new REnvVars(); - } - - private void checkExplicitEnvSettings(RContext context) { - String[] envs = context.getEnvSettings(); - if (envs == null || envs.length == 0) { - return; - } - for (String envdef : envs) { - String[] parts = envdef.split("="); - if (parts.length == 2) { - envVars.put(parts[0], parts[1]); - } else { - // for now just ignore - } - } + public static REnvVars newContextState(Map<String, String> initialEnvVars) { + return new REnvVars(initialEnvVars); } private String getEitherCase(String var) { @@ -290,6 +275,14 @@ public final class REnvVars implements RContext.ContextState { } } + public TimeZone getSystemTimeZone() { + String tzName = envVars.get("TZ"); + if (tzName != null) { + return TimeZone.getTimeZone(tzName); + } + return TimeZone.getDefault(); + } + private String expandParameters(String value) { StringBuilder result = new StringBuilder(); int x = 0; 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 4180cd6a52..e8d02437ed 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 @@ -903,6 +903,7 @@ public final class RError extends RuntimeException implements TruffleException { FILE_NOT_FOUND_IN_ZIP("requested file not found in the zip file"), LIST_NO_VALID_NAMES("list argument has no valid names"), VALUES_MUST_BE_LENGTH("values must be length %s,\n but FUN(X[[%d]]) result is length %s"), + OS_REQUEST_LOCALE("OS reports request to set locale to \"%s\" cannot be honored"), INVALID_TYPE("invalid type (%s) for '%s' (must be a %s)"); public final String message; diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RLocale.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RLocale.java new file mode 100644 index 0000000000..ca3c2f2891 --- /dev/null +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RLocale.java @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.runtime; + +import java.nio.charset.Charset; +import java.nio.charset.IllegalCharsetNameException; +import java.nio.charset.StandardCharsets; +import java.nio.charset.UnsupportedCharsetException; +import java.util.EnumMap; +import java.util.Locale; + +import com.oracle.truffle.r.runtime.RError.Message; +import com.oracle.truffle.r.runtime.context.RContext; +import com.oracle.truffle.r.runtime.context.RContext.ContextState; + +public enum RLocale { + COLLATE(true, true, true), + CTYPE(false, true, true), + MONETARY(true, true, true), + NUMERIC(true, false, true), + TIME(true, true, true), + MESSAGES(true, true, true), + PAPER(false, false, false), + MEASUREMENT(false, false, false); + + private final String name; + private final boolean needsLocale; + private final boolean initializedAtStartup; + private final boolean listed; + + RLocale(boolean needsLocale, boolean initializedAtStartup, boolean listed) { + this.needsLocale = needsLocale; + this.initializedAtStartup = initializedAtStartup; + this.listed = listed; + this.name = "LC_" + name(); + } + + public static final class ContextStateImpl implements RContext.ContextState { + private final EnumMap<RLocale, Locale> locales = new EnumMap<>(RLocale.class); + private final EnumMap<RLocale, Charset> charsets = new EnumMap<>(RLocale.class); + + private ContextStateImpl() { + // private constructor + } + + private static String getDefinition(String name, REnvVars envVars) { + // lookup order: LC_ALL, LC_<name>, LANG + for (String identifier : new String[]{"LC_ALL", name, "LANG"}) { + String value = envVars.get(identifier); + if (value != null && !value.isEmpty()) { + return value; + } + } + return null; + } + + @Override + public ContextState initialize(RContext context) { + REnvVars envVars = context.stateREnvVars; + for (RLocale locale : RLocale.values()) { + if (locale.initializedAtStartup) { + setLocale(locale, getDefinition(locale.name, envVars), true); + } else { + setLocale(locale, "C", true); + } + } + return this; + } + + private static Charset getCharset(String value) { + try { + int dot = value.indexOf('.'); + return dot == -1 ? Charset.forName(value) : Charset.forName(value.substring(dot + 1)); + } catch (UnsupportedCharsetException | IllegalCharsetNameException e) { + return null; + } + } + + private static Locale getLocale(String value) { + int dot = value.indexOf('.'); + String code = dot == -1 ? value : value.substring(0, dot); + Locale.Builder builder = new Locale.Builder(); + switch (code.length()) { + case 2: + return builder.setLanguage(code).build(); + case 5: + if (code.charAt(2) == '_') { + return builder.setLanguage(code.substring(0, 2)).setRegion(code.substring(3)).build(); + } + break; + default: + if (code.length() >= 7 && code.charAt(2) == '_' && code.charAt(5) == '_') { + return builder.setLanguage(code.substring(0, 2)).setRegion(code.substring(3)).setVariant(code.substring(6)).build(); + } + break; + } + return null; + } + + public void setLocale(RLocale locale, String value) { + setLocale(locale, value, false); + } + + public void setLocale(RLocale locale, String value, boolean startup) { + Charset c = null; + Locale l = null; + if ("C".equals(value) || "POSIX".equals(value)) { + c = StandardCharsets.US_ASCII; + } else if (value != null) { + c = getCharset(value); + l = getLocale(value); + if ((c == null && l == null) || (l == null && locale.needsLocale)) { + if (startup) { + RContext.getInstance().getConsole().printErrorln("Setting " + locale.name + " failed, using default"); + } else { + RError.warning(RError.SHOW_CALLER, Message.OS_REQUEST_LOCALE, value); + } + } + } + charsets.put(locale, c == null ? StandardCharsets.UTF_8 : c); + locales.put(locale, l == null ? Locale.ROOT : l); + } + + public Charset getCharset(RLocale locale) { + return charsets.get(locale); + } + + public Locale getLocale(RLocale locale) { + return locales.get(locale); + } + + public static ContextStateImpl newContextState() { + return new ContextStateImpl(); + } + } + + public String getRepresentation(RContext context) { + ContextStateImpl state = context.stateRLocale; + Locale l = state.locales.get(this); + Charset c = state.charsets.get(this); + if (c == StandardCharsets.US_ASCII) { + if (l == Locale.ROOT) { + return "C"; + } else { + return l.toString(); + } + } else { + if (l == Locale.ROOT) { + return c.name(); + } else { + return l.toString() + "." + c.name(); + } + } + } + + public boolean isListed() { + return listed; + } +} diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/ChildContextInfo.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/ChildContextInfo.java index bdcd6c61d3..a40ce96e9b 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/ChildContextInfo.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/ChildContextInfo.java @@ -25,7 +25,7 @@ package com.oracle.truffle.r.runtime.context; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.TimeZone; +import java.util.Map; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; @@ -34,8 +34,8 @@ import com.oracle.truffle.api.TruffleContext; import com.oracle.truffle.api.vm.PolyglotEngine; import com.oracle.truffle.api.vm.PolyglotEngine.Builder; import com.oracle.truffle.r.launcher.RCmdOptions; -import com.oracle.truffle.r.launcher.RStartParams; import com.oracle.truffle.r.launcher.RCmdOptions.Client; +import com.oracle.truffle.r.launcher.RStartParams; import com.oracle.truffle.r.runtime.RInternalError; import com.oracle.truffle.r.runtime.context.RContext.ContextKind; @@ -71,9 +71,8 @@ public final class ChildContextInfo { static final AtomicInteger multiSlotInds = new AtomicInteger(-1); private final RStartParams startParams; - private final String[] env; + private final Map<String, String> env; private final RContext.ContextKind kind; - private final TimeZone systemTimeZone; /** * Any context created by another has a parent. When such a context is destroyed we must reset @@ -89,7 +88,8 @@ public final class ChildContextInfo { private PolyglotEngine vm; public Executor executor; - private ChildContextInfo(RStartParams startParams, String[] env, ContextKind kind, RContext parent, InputStream stdin, OutputStream stdout, OutputStream stderr, TimeZone systemTimeZone, int id, + private ChildContextInfo(RStartParams startParams, Map<String, String> env, ContextKind kind, RContext parent, InputStream stdin, OutputStream stdout, OutputStream stderr, + int id, int multiSlotInd) { this.startParams = startParams; this.env = env; @@ -98,7 +98,6 @@ public final class ChildContextInfo { this.stdin = stdin; this.stdout = stdout; this.stderr = stderr; - this.systemTimeZone = systemTimeZone; this.multiSlotInd = multiSlotInd; this.id = id; } @@ -149,10 +148,8 @@ public final class ChildContextInfo { * @param kind defines the degree to which this context shares base and package environments * with its parent * @param parent if non-null {@code null}, the parent creating the context - * @param systemTimeZone the system's time zone */ - public static ChildContextInfo create(RStartParams startParams, String[] env, ContextKind kind, RContext parent, InputStream stdin, OutputStream stdout, OutputStream stderr, - TimeZone systemTimeZone) { + public static ChildContextInfo create(RStartParams startParams, Map<String, String> env, ContextKind kind, RContext parent, InputStream stdin, OutputStream stdout, OutputStream stderr) { int id = contextInfoIds.incrementAndGet(); int multiSlotInd = multiSlotInds.get(); if (kind == ContextKind.SHARE_ALL || kind == ContextKind.SHARE_NOTHING) { @@ -164,23 +161,18 @@ public final class ChildContextInfo { throw RInternalError.shouldNotReachHere(); } assert kind != ContextKind.SHARE_PARENT_RW || (kind == ContextKind.SHARE_PARENT_RW && parent.getKind() == ContextKind.SHARE_NOTHING && parent.getMultiSlotInd() == 0); - return new ChildContextInfo(startParams, env, kind, parent, stdin, stdout, stderr, systemTimeZone, id, kind == ContextKind.SHARE_PARENT_RW ? 0 : multiSlotInd); - } - - public static ChildContextInfo create(RStartParams startParams, String[] env, ContextKind kind, RContext parent, InputStream stdin, OutputStream stdout, OutputStream stderr) { - return create(startParams, env, kind, parent, stdin, stdout, stderr, TimeZone.getDefault()); + return new ChildContextInfo(startParams, env, kind, parent, stdin, stdout, stderr, id, kind == ContextKind.SHARE_PARENT_RW ? 0 : multiSlotInd); } /** * Create a context configuration object such that FastR does not restore previously stored * sessions on startup. * - * @param env TODO * @param kind defines the degree to which this context shares base and package environments * with its parent * @param parent if non-null {@code null}, the parent creating the context */ - public static ChildContextInfo createNoRestore(Client client, String[] env, ContextKind kind, RContext parent, InputStream stdin, OutputStream stdout, OutputStream stderr) { + public static ChildContextInfo createNoRestore(Client client, Map<String, String> env, ContextKind kind, RContext parent, InputStream stdin, OutputStream stdout, OutputStream stderr) { RStartParams params = new RStartParams(RCmdOptions.parseArguments(client, new String[]{"R", "--vanilla", "--slave", "--silent", "--no-restore"}, false), false); return create(params, env, kind, parent, stdin, stdout, stderr); } @@ -189,7 +181,7 @@ public final class ChildContextInfo { return startParams; } - public String[] getEnv() { + public Map<String, String> getEnv() { return env; } @@ -201,10 +193,6 @@ public final class ChildContextInfo { return parent; } - public TimeZone getSystemTimeZone() { - return systemTimeZone; - } - public int getId() { return id; } 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 41383f5865..c20435b25a 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 @@ -40,11 +40,11 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.TimeZone; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; @@ -80,6 +80,7 @@ import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RErrorHandling; import com.oracle.truffle.r.runtime.RInternalCode.ContextStateImpl; import com.oracle.truffle.r.runtime.RInternalError; +import com.oracle.truffle.r.runtime.RLocale; import com.oracle.truffle.r.runtime.ROptions; import com.oracle.truffle.r.runtime.RProfile; import com.oracle.truffle.r.runtime.RRuntime; @@ -226,9 +227,7 @@ public final class RContext implements RTruffleObject { private final RStartParams startParameters; private final RCmdOptions cmdOptions; - private final String[] environment; private final RContext.ContextKind contextKind; - private final TimeZone systemTimeZone; public final Map<Class<?>, RootCallTarget> nativeCallTargets = new HashMap<>(); public RootCallTarget getOrCreateNativeCallTarget(Class<?> clazz, Supplier<RootCallTarget> creatFunction) { @@ -332,6 +331,7 @@ public final class RContext implements RTruffleObject { * processor, but the set is relatively small, so we just enumerate them here. */ public final REnvVars stateREnvVars; + public final RLocale.ContextStateImpl stateRLocale; public final TempPathName stateTempPath; public final RProfile stateRProfile; public final StdConnections.ContextStateImpl stateStdConnections; @@ -357,7 +357,8 @@ public final class RContext implements RTruffleObject { private final AllocationReporter allocationReporter; private ContextState[] contextStates() { - return new ContextState[]{stateREnvVars, stateRProfile, stateTempPath, stateROptions, stateREnvironment, stateRErrorHandling, stateRConnection, stateStdConnections, stateRNG, stateRFFI, + return new ContextState[]{stateREnvVars, stateRLocale, stateRProfile, stateTempPath, stateROptions, stateREnvironment, stateRErrorHandling, stateRConnection, stateStdConnections, stateRNG, + stateRFFI, stateRSerialize, stateLazyDBCache, stateInstrumentation, stateDLL}; } @@ -386,35 +387,33 @@ public final class RContext implements RTruffleObject { } Object initialInfo = env.getConfig().get(ChildContextInfo.CONFIG_KEY); + Map<String, String> initialEnvVars; if (initialInfo == null) { /* * This implies that FastR is being invoked initially from another Truffle language or - * via RCommand/RscriptCommand. TODO How to deciden if session state is to be restored - * stored session should be restored. + * via RCommand/RscriptCommand. TODO How to decide if session state is to be restored */ this.cmdOptions = RCmdOptions.parseArguments(Client.R, args, true); this.startParameters = new RStartParams(cmdOptions, false); - this.environment = null; this.contextKind = ContextKind.SHARE_NOTHING; - this.systemTimeZone = TimeZone.getDefault(); this.parentContext = null; this.id = ChildContextInfo.contextInfoIds.incrementAndGet(); this.multiSlotIndex = 0; - this.truffleContext = null; // TODO + this.truffleContext = null; this.executor = null; + initialEnvVars = System.getenv(); } else { // child spawned explicitly by R ChildContextInfo info = (ChildContextInfo) initialInfo; this.cmdOptions = RCmdOptions.parseArguments(Client.R, args, true); this.startParameters = info.getStartParams(); - this.environment = info.getEnv(); this.contextKind = info.getKind(); - this.systemTimeZone = info.getSystemTimeZone(); this.parentContext = info.getParent(); this.id = info.getId(); this.multiSlotIndex = info.getMultiSlotInd(); this.truffleContext = info.getTruffleContext(); this.executor = info.executor; + initialEnvVars = info.getEnv() == null ? Collections.emptyMap() : info.getEnv(); } outputWelcomeMessage(startParameters); @@ -425,7 +424,8 @@ public final class RContext implements RTruffleObject { this.initial = isInitial; this.env = env; - this.stateREnvVars = REnvVars.newContextState(); + this.stateREnvVars = REnvVars.newContextState(initialEnvVars); + this.stateRLocale = RLocale.ContextStateImpl.newContextState(); this.stateTempPath = TempPathName.newContextState(); this.stateROptions = ROptions.ContextStateImpl.newContextState(stateREnvVars); this.stateRProfile = RProfile.newContextState(stateREnvVars); @@ -502,6 +502,7 @@ public final class RContext implements RTruffleObject { doEnvOptionsProfileInitialization(); } + stateRLocale.initialize(this); stateREnvironment.initialize(this); stateRErrorHandling.initialize(this); stateRConnection.initialize(this); @@ -730,10 +731,6 @@ public final class RContext implements RTruffleObject { return startParameters; } - public String[] getEnvSettings() { - return environment; - } - public boolean hasExecutor() { return executor != null; } @@ -790,10 +787,6 @@ public final class RContext implements RTruffleObject { return loadingBase; } - public TimeZone getSystemTimeZone() { - return systemTimeZone; - } - public String getNamespaceName() { return nameSpaceName; } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_order.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_order.java index c9fd1e66c5..5763cc90d5 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_order.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_order.java @@ -164,5 +164,7 @@ public class TestBuiltin_order extends TestBase { assertEval("order(c('40 50', '405', '40 51', '4028', '40 20', '40 30', '404'))"); assertEval("order(c(1,2,0), decreasing=NA)"); + + assertEval("invisible(Sys.setlocale('LC_COLLATE', 'EN_us')); str(as.data.frame(list(a=c('A wo','Far ','abc ')))); invisible(Sys.setlocale('LC_COLLATE', 'C')); str(as.data.frame(list(a=c('A wo','Far ','abc '))));"); } } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/generate/FastRSession.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/generate/FastRSession.java index e3311fb648..b03e4a7ac0 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/generate/FastRSession.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/generate/FastRSession.java @@ -30,7 +30,8 @@ import java.nio.CharBuffer; import java.nio.charset.CharsetDecoder; import java.nio.charset.CodingErrorAction; import java.nio.charset.StandardCharsets; -import java.util.TimeZone; +import java.util.HashMap; +import java.util.Map; import java.util.Timer; import java.util.TimerTask; @@ -113,7 +114,9 @@ public final class FastRSession implements RSession { public ChildContextInfo createContextInfo(ContextKind contextKind) { RStartParams params = new RStartParams(RCmdOptions.parseArguments(Client.R, new String[]{"R", "--vanilla", "--slave", "--silent", "--no-restore"}, false), false); - return ChildContextInfo.create(params, null, contextKind, mainContext, input, output, output, TimeZone.getTimeZone("GMT")); + Map<String, String> env = new HashMap<>(); + env.put("TZ", "GMT"); + return ChildContextInfo.create(params, env, contextKind, mainContext, input, output, output); } private FastRSession() { diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/generate/GnuROneShotRSession.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/generate/GnuROneShotRSession.java index 8c6ba569e2..bc65c7c631 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/generate/GnuROneShotRSession.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/generate/GnuROneShotRSession.java @@ -86,6 +86,8 @@ public class GnuROneShotRSession implements RSession { // fix time zone to "GMT" (to create consistent expected output) pb.environment().put("TZ", "GMT"); pb.environment().remove("R_HOME"); // don't confuse GnuR with FastR! + pb.environment().remove("LC_ALL"); + pb.environment().remove("LC_COLLATE"); pb.redirectErrorStream(true); Process p = pb.start(); p.getOutputStream().write(GNUR_OPTIONS.getBytes()); -- GitLab