Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions rhino/src/main/java/org/mozilla/javascript/NativeNumber.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

package org.mozilla.javascript;

import java.text.NumberFormat;
import java.util.IllformedLocaleException;
import java.util.Locale;
import org.mozilla.javascript.dtoa.DecimalFormatter;

/**
Expand Down Expand Up @@ -98,8 +101,8 @@ static void init(Scriptable scope, boolean sealed) {
}

constructor.definePrototypeMethod(scope, "toString", 1, NativeNumber::js_toString);
// Alias toLocaleString to toString
constructor.definePrototypeMethod(scope, "toLocaleString", 0, NativeNumber::js_toString);
constructor.definePrototypeMethod(
scope, "toLocaleString", 0, NativeNumber::js_toLocaleString);
constructor.definePrototypeMethod(scope, "toSource", 0, NativeNumber::js_toSource);
constructor.definePrototypeMethod(scope, "valueOf", 0, NativeNumber::js_valueOf);
constructor.definePrototypeMethod(scope, "toFixed", 1, NativeNumber::js_toFixed);
Expand Down Expand Up @@ -225,6 +228,25 @@ private static Object js_toString(
return ScriptRuntime.numberToString(toSelf(thisObj).doubleValue, base);
}

private static Object js_toLocaleString(
Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
if (!cx.hasFeature(Context.FEATURE_INTL_402)) {
return js_toString(cx, scope, thisObj, ScriptRuntime.emptyArgs);
}

if (args.length != 0 && args[0] instanceof String) {
final String localeStr = (String) args[0];
try {
final Locale locale = new Locale.Builder().setLanguageTag(localeStr).build();
return NumberFormat.getInstance(locale).format(toSelf(thisObj).doubleValue);
} catch (final IllformedLocaleException e) {
throw ScriptRuntime.rangeError("Invalid language tag: " + localeStr);
}
}

return NumberFormat.getInstance(cx.getLocale()).format(toSelf(thisObj).doubleValue);
}

private static Object js_toSource(
Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
return "(new Number(" + ScriptRuntime.toString(toSelf(thisObj).doubleValue) + "))";
Expand Down
13 changes: 11 additions & 2 deletions rhino/src/main/java/org/mozilla/javascript/NativeString.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import java.text.Normalizer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.IllformedLocaleException;
import java.util.List;
import java.util.Locale;
import java.util.Map;
Expand Down Expand Up @@ -1115,7 +1116,11 @@ private static Object js_toLocaleLowerCase(
Locale locale = cx.getLocale();
if (args.length > 0 && cx.hasFeature(Context.FEATURE_INTL_402)) {
String lang = ScriptRuntime.toString(args[0]);
locale = new Locale(lang);
try {
locale = new Locale.Builder().setLanguageTag(lang).build();
} catch (final IllformedLocaleException e) {
// ignore and fall back to the context locale
}
}
return thisStr.toLowerCase(locale);
}
Expand All @@ -1128,7 +1133,11 @@ private static Object js_toLocaleUpperCase(
Locale locale = cx.getLocale();
if (args.length > 0 && cx.hasFeature(Context.FEATURE_INTL_402)) {
String lang = ScriptRuntime.toString(args[0]);
locale = new Locale(lang);
try {
locale = new Locale.Builder().setLanguageTag(lang).build();
} catch (final IllformedLocaleException e) {
// ignore and fall back to the context locale
}
}
return thisStr.toUpperCase(locale);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,16 @@ public class Test262SuiteTest {
static final Map<String, Script> HARNESS_SCRIPT_CACHE = new ConcurrentHashMap<>();
static final Map<Test262Case, TestResultTracker> RESULT_TRACKERS = new LinkedHashMap<>();

static ShellContextFactory CTX_FACTORY = new ShellContextFactory();
static ShellContextFactory CTX_FACTORY =
new ShellContextFactory() {
protected boolean hasFeature(Context cx, int featureIndex) {
if (Context.FEATURE_INTL_402 == featureIndex) {
return true;
}

return super.hasFeature(cx, featureIndex);
}
};

static final Set<String> UNSUPPORTED_FEATURES =
new HashSet<>(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.javascript.tests.es6;

import static org.junit.Assert.assertEquals;

import java.util.Locale;
import org.junit.Test;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.EcmaError;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.testutils.Utils;

public class NativeNumber2Test {

@Test
public void toLocaleString_no402() {
Utils.assertWithAllModes("1", "let n = 1; n.toLocaleString()");
Utils.assertWithAllModes("1", "(1.0).toLocaleString()");

Utils.assertWithAllModes("3.14", "let n = 3.14; n.toLocaleString()");
Utils.assertWithAllModes("-0.007", "let n = -0.007; n.toLocaleString()");

Utils.assertWithAllModes("10000", "1e4.toLocaleString()");
Utils.assertWithAllModes("1e-10", "let n = 1e-10; n.toLocaleString()");
Utils.assertWithAllModes("1e-10", "1e-10.toLocaleString()");

Utils.assertWithAllModes("0.01", "1e-2.toLocaleString()");

Utils.assertWithAllModes("NaN", "NaN.toLocaleString()");
Utils.assertWithAllModes("Infinity", "Infinity.toLocaleString()");
Utils.assertWithAllModes("-Infinity", "(-Infinity).toLocaleString()");
}

@Test
public void toLocaleString_no402IgnoreParams() {
Utils.assertWithAllModes("1.234", "let n = 1.234; n.toLocaleString('en-US')");
Utils.assertWithAllModes("1.234", "let n = 1.234; n.toLocaleString('de')");
Utils.assertWithAllModes("1.234", "let n = 1.234; n.toLocaleString('de-DE')");
}

@Test
public void toLocaleString() {
assertWithAllModes402("1", "let n = 1; n.toLocaleString()");
assertWithAllModes402("1", "let n = 1.0; n.toLocaleString()");

assertWithAllModes402("3.14", "let n = 3.14; n.toLocaleString()");
assertWithAllModes402("-0.007", "let n = -0.007; n.toLocaleString()");

assertWithAllModes402("10,000", "1e4.toLocaleString()");
assertWithAllModes402("0", "let n = 1e-10; n.toLocaleString()");
assertWithAllModes402("0", "1e-10.toLocaleString()");

assertWithAllModes402("0.01", "1e-2.toLocaleString()");

assertWithAllModes402("NaN", "NaN.toLocaleString()");
assertWithAllModes402("∞", "Infinity.toLocaleString()");
assertWithAllModes402("-∞", "(-Infinity).toLocaleString()");
}

@Test
public void toLocaleString_Invalid() {
Utils.assertException(
Utils.contextFactoryWithFeatures(Context.FEATURE_INTL_402),
Context.VERSION_ES6,
EcmaError.class,
"RangeError: Invalid language tag: 77",
"let n = 1; n.toLocaleString('77')");

assertWithAllModes402("1", "let n = 1; n.toLocaleString(77)");
}

@Test
public void toLocaleString_GermanContext() {
assertWithAllModes402("1", "let n = 1; n.toLocaleString()", Locale.GERMAN);
assertWithAllModes402("1", "let n = 1.0; n.toLocaleString()", Locale.GERMAN);

assertWithAllModes402("3,14", "let n = 3.14; n.toLocaleString()", Locale.GERMAN);
assertWithAllModes402("-0,007", "let n = -0.007; n.toLocaleString()", Locale.GERMAN);

assertWithAllModes402("10.000", "1e4.toLocaleString()", Locale.GERMAN);
assertWithAllModes402("0", "let n = 1e-10; n.toLocaleString()", Locale.GERMAN);
assertWithAllModes402("0", "1e-10.toLocaleString()", Locale.GERMAN);

assertWithAllModes402("0,01", "1e-2.toLocaleString()", Locale.GERMAN);

assertWithAllModes402("NaN", "NaN.toLocaleString()", Locale.GERMAN);
assertWithAllModes402("∞", "Infinity.toLocaleString()", Locale.GERMAN);
assertWithAllModes402("-∞", "(-Infinity).toLocaleString()", Locale.GERMAN);
}

@Test
public void toLocaleString_JapanContext() {
assertWithAllModes402("1", "let n = 1; n.toLocaleString()", Locale.JAPAN);
assertWithAllModes402("1", "let n = 1.0; n.toLocaleString()", Locale.JAPAN);

assertWithAllModes402("3.14", "let n = 3.14; n.toLocaleString()", Locale.JAPAN);
assertWithAllModes402("-0.007", "let n = -0.007; n.toLocaleString()", Locale.JAPAN);

assertWithAllModes402("10,000", "1e4.toLocaleString()", Locale.JAPAN);
assertWithAllModes402("0", "let n = 1e-10; n.toLocaleString()", Locale.JAPAN);
assertWithAllModes402("0", "1e-10.toLocaleString()", Locale.JAPAN);

assertWithAllModes402("0.01", "1e-2.toLocaleString()", Locale.JAPAN);

assertWithAllModes402("NaN", "NaN.toLocaleString()", Locale.JAPAN);
assertWithAllModes402("∞", "Infinity.toLocaleString()", Locale.JAPAN);
assertWithAllModes402("-∞", "(-Infinity).toLocaleString()", Locale.JAPAN);
}

@Test
public void toLocaleString_ArabicContext() {
assertWithAllModes402(
"\u0661", "let n = 1; n.toLocaleString('ar-SA')", Locale.forLanguageTag("ar-SA"));
assertWithAllModes402(
"\u0661", "let n = 1.0; n.toLocaleString('ar-SA')", Locale.forLanguageTag("ar-SA"));

assertWithAllModes402(
"\u0663\u066b\u0661\u0664",
"let n = 3.14; n.toLocaleString('ar-SA')",
Locale.forLanguageTag("ar-SA"));
assertWithAllModes402(
"\u061c\u002d\u0660\u066b\u0660\u0660\u0667",
"let n = -0.007; n.toLocaleString('ar-SA')",
Locale.forLanguageTag("ar-SA"));

assertWithAllModes402(
"\u0661\u0660\u066c\u0660\u0660\u0660",
"1e4.toLocaleString('ar-SA')",
Locale.forLanguageTag("ar-SA"));
assertWithAllModes402(
"\u0660",
"let n = 1e-10; n.toLocaleString('ar-SA')",
Locale.forLanguageTag("ar-SA"));
assertWithAllModes402(
"\u0660", "1e-10.toLocaleString('ar-SA')", Locale.forLanguageTag("ar-SA"));

assertWithAllModes402(
"\u0660\u066b\u0660\u0661",
"1e-2.toLocaleString('ar-SA')",
Locale.forLanguageTag("ar-SA"));

assertWithAllModes402(
"\u0644\u064a\u0633\u00a0\u0631\u0642\u0645",
"NaN.toLocaleString('ar-SA')",
Locale.forLanguageTag("ar-SA"));
assertWithAllModes402(
"\u221e", "Infinity.toLocaleString('ar-SA')", Locale.forLanguageTag("ar-SA"));
assertWithAllModes402(
"\u061c\u002d\u221e",
"(-Infinity).toLocaleString('ar-SA')",
Locale.forLanguageTag("ar-SA"));
}

@Test
public void toLocaleString_de() {
assertWithAllModes402("1", "let n = 1; n.toLocaleString('de')");
assertWithAllModes402("1", "let n = 1.0; n.toLocaleString('de')");

assertWithAllModes402("3,14", "let n = 3.14; n.toLocaleString('de')");
assertWithAllModes402("-0,007", "let n = -0.007; n.toLocaleString('de')");

assertWithAllModes402("10.000", "1e4.toLocaleString('de')");
assertWithAllModes402("0", "let n = 1e-10; n.toLocaleString('de')");
assertWithAllModes402("0", "1e-10.toLocaleString('de')");

assertWithAllModes402("0,01", "1e-2.toLocaleString('de')");

assertWithAllModes402("NaN", "NaN.toLocaleString('de')");
assertWithAllModes402("∞", "Infinity.toLocaleString('de')");
assertWithAllModes402("-∞", "(-Infinity).toLocaleString('de')");
}

@Test
public void toLocaleString_deDE() {
assertWithAllModes402("1", "let n = 1; n.toLocaleString('de-DE')");
assertWithAllModes402("1", "let n = 1.0; n.toLocaleString('de-DE')");

assertWithAllModes402("3,14", "let n = 3.14; n.toLocaleString('de-DE')");
assertWithAllModes402("-0,007", "let n = -0.007; n.toLocaleString('de-DE')");

assertWithAllModes402("10.000", "1e4.toLocaleString('de-DE')");
assertWithAllModes402("0", "let n = 1e-10; n.toLocaleString('de-DE')");
assertWithAllModes402("0", "1e-10.toLocaleString('de-DE')");

assertWithAllModes402("0,01", "1e-2.toLocaleString('de-DE')");

assertWithAllModes402("NaN", "NaN.toLocaleString('de-DE')");
assertWithAllModes402("∞", "Infinity.toLocaleString('de-DE')");
assertWithAllModes402("-∞", "(-Infinity).toLocaleString('de-DE')");
}

@Test
public void toLocaleString_Japan() {
assertWithAllModes402("1", "let n = 1; n.toLocaleString('japan')");
assertWithAllModes402("1", "let n = 1.0; n.toLocaleString('japan')");

assertWithAllModes402("3.14", "let n = 3.14; n.toLocaleString('japan')");
assertWithAllModes402("-0.007", "let n = -0.007; n.toLocaleString('japan')");

assertWithAllModes402("10,000", "1e4.toLocaleString('japan')");
assertWithAllModes402("0", "let n = 1e-10; n.toLocaleString('japan')");
assertWithAllModes402("0", "1e-10.toLocaleString('japan')");

assertWithAllModes402("0.01", "1e-2.toLocaleString('japan')");

assertWithAllModes402("NaN", "NaN.toLocaleString('japan')");
assertWithAllModes402("∞", "Infinity.toLocaleString('japan')");
assertWithAllModes402("-∞", "(-Infinity).toLocaleString('japan')");
}

@Test
public void toLocaleString_Arabic() {
assertWithAllModes402("\u0661", "let n = 1; n.toLocaleString('ar-SA')");
assertWithAllModes402("\u0661", "let n = 1.0; n.toLocaleString('ar-SA')");

assertWithAllModes402(
"\u0663\u066b\u0661\u0664", "let n = 3.14; n.toLocaleString('ar-SA')");
assertWithAllModes402(
"\u061c\u002d\u0660\u066b\u0660\u0660\u0667",
"let n = -0.007; n.toLocaleString('ar-SA')");

assertWithAllModes402(
"\u0661\u0660\u066c\u0660\u0660\u0660", "1e4.toLocaleString('ar-SA')");
assertWithAllModes402("\u0660", "let n = 1e-10; n.toLocaleString('ar-SA')");
assertWithAllModes402("\u0660", "1e-10.toLocaleString('ar-SA')");

assertWithAllModes402("\u0660\u066b\u0660\u0661", "1e-2.toLocaleString('ar-SA')");

assertWithAllModes402(
"\u0644\u064a\u0633\u00a0\u0631\u0642\u0645", "NaN.toLocaleString('ar-SA')");
assertWithAllModes402("\u221e", "Infinity.toLocaleString('ar-SA')");
assertWithAllModes402("\u061c\u002d\u221e", "(-Infinity).toLocaleString('ar-SA')");
}

private void assertWithAllModes402(final Object expected, final String script) {
Utils.runWithAllModes(
Utils.contextFactoryWithFeatures(Context.FEATURE_INTL_402),
cx -> {
final Scriptable scope = cx.initStandardObjects();

final Object res = cx.evaluateString(scope, script, "test.js", 0, null);
assertEquals(expected, res);
return null;
});
}

private void assertWithAllModes402(final Object expected, final String script, Locale locale) {
Utils.runWithAllModes(
Utils.contextFactoryWithFeatures(Context.FEATURE_INTL_402),
cx -> {
final Scriptable scope = cx.initStandardObjects();
cx.setLocale(locale);

final Object res = cx.evaluateString(scope, script, "test.js", 0, null);
assertEquals(expected, res);
return null;
});
}
}
Loading
Loading