mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-15 06:15:43 +00:00
Bug 769871 - Reimplement Number.toLocaleString per ECMA-402. r=jwalden
--HG-- extra : rebase_source : 5a1e5a57e89056d128956cd7d7f2c7ce6b3dec54
This commit is contained in:
parent
dfa2e312ca
commit
31ef79f418
@ -963,6 +963,7 @@ selfhosting_srcs := \
|
||||
$(srcdir)/builtin/Array.js \
|
||||
$(srcdir)/builtin/Intl.js \
|
||||
$(srcdir)/builtin/IntlData.js \
|
||||
$(srcdir)/builtin/Number.js \
|
||||
$(srcdir)/builtin/ParallelArray.js \
|
||||
$(srcdir)/builtin/String.js \
|
||||
$(NULL)
|
||||
|
@ -1101,14 +1101,11 @@ static JSFunctionSpec numberFormat_methods[] = {
|
||||
* NumberFormat constructor.
|
||||
* Spec: ECMAScript Internationalization API Specification, 11.1
|
||||
*/
|
||||
static JSBool
|
||||
NumberFormat(JSContext *cx, unsigned argc, Value *vp)
|
||||
static bool
|
||||
NumberFormat(JSContext *cx, CallArgs args, bool construct)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
RootedObject obj(cx);
|
||||
|
||||
bool construct = IsConstructing(args);
|
||||
if (!construct) {
|
||||
// 11.1.2.1 step 3
|
||||
JSObject *intl = cx->global()->getOrCreateIntlObject(cx);
|
||||
@ -1154,10 +1151,26 @@ NumberFormat(JSContext *cx, unsigned argc, Value *vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
NumberFormat(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return NumberFormat(cx, args, IsConstructing(args));
|
||||
}
|
||||
|
||||
JSBool
|
||||
js::intl_NumberFormat(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
JS_ASSERT(args.length() == 2);
|
||||
return NumberFormat(cx, args, true);
|
||||
}
|
||||
|
||||
static void
|
||||
numberFormat_finalize(FreeOp *fop, JSObject *obj)
|
||||
{
|
||||
UNumberFormat *nf = static_cast<UNumberFormat *>(obj->getReservedSlot(UNUMBER_FORMAT_SLOT).toPrivate());
|
||||
UNumberFormat *nf =
|
||||
static_cast<UNumberFormat*>(obj->getReservedSlot(UNUMBER_FORMAT_SLOT).toPrivate());
|
||||
if (nf)
|
||||
unum_close(nf);
|
||||
}
|
||||
|
@ -82,6 +82,16 @@ intl_CompareStrings(JSContext *cx, unsigned argc, Value *vp);
|
||||
|
||||
/******************** NumberFormat ********************/
|
||||
|
||||
/**
|
||||
* Returns a new instance of the standard built-in NumberFormat constructor.
|
||||
* Self-hosted code cannot cache this constructor (as it does for others in
|
||||
* Utilities.js) because it is initialized after self-hosted code is compiled.
|
||||
*
|
||||
* Usage: numberFormat = intl_NumberFormat(locales, options)
|
||||
*/
|
||||
extern JSBool
|
||||
intl_NumberFormat(JSContext *cx, unsigned argc, Value *vp);
|
||||
|
||||
/**
|
||||
* Returns an object indicating the supported locales for number formatting
|
||||
* by having a true-valued property for each such locale with the
|
||||
|
41
js/src/builtin/Number.js
Normal file
41
js/src/builtin/Number.js
Normal file
@ -0,0 +1,41 @@
|
||||
/* 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/. */
|
||||
|
||||
/*global num_CheckThisNumber: false, intl_NumberFormat: false, */
|
||||
|
||||
|
||||
var numberFormatCache = new Record();
|
||||
|
||||
|
||||
/**
|
||||
* Format this Number object into a string, using the locale and formatting options
|
||||
* provided.
|
||||
*
|
||||
* Spec: ECMAScript Language Specification, 5.1 edition, 15.7.4.3.
|
||||
* Spec: ECMAScript Internationalization API Specification, 13.2.1.
|
||||
*/
|
||||
function Number_toLocaleString() {
|
||||
// Step 1.
|
||||
callFunction(num_CheckThisNumber, this);
|
||||
var x = callFunction(std_Number_valueOf, this);
|
||||
|
||||
// Steps 2-3.
|
||||
var locales = arguments.length > 0 ? arguments[0] : undefined;
|
||||
var options = arguments.length > 1 ? arguments[1] : undefined;
|
||||
|
||||
// Step 4.
|
||||
var numberFormat;
|
||||
if (locales === undefined && options === undefined) {
|
||||
// This cache only optimizes for the old ES5 toLocaleString without
|
||||
// locales and options.
|
||||
if (numberFormatCache.numberFormat === undefined)
|
||||
numberFormatCache.numberFormat = intl_NumberFormat(locales, options);
|
||||
numberFormat = numberFormatCache.numberFormat;
|
||||
} else {
|
||||
numberFormat = intl_NumberFormat(locales, options);
|
||||
}
|
||||
|
||||
// Step 5.
|
||||
return intl_FormatNumber(numberFormat, x);
|
||||
}
|
@ -44,6 +44,7 @@ var std_Function_apply = Function.prototype.apply;
|
||||
var std_Math_floor = Math.floor;
|
||||
var std_Math_max = Math.max;
|
||||
var std_Math_min = Math.min;
|
||||
var std_Number_valueOf = Number.prototype.valueOf;
|
||||
var std_Object_create = Object.create;
|
||||
var std_Object_defineProperty = Object.defineProperty;
|
||||
var std_Object_getOwnPropertyNames = Object.getOwnPropertyNames;
|
||||
|
@ -861,9 +861,11 @@ JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
|
||||
structuredCloneCallbacks(NULL),
|
||||
telemetryCallback(NULL),
|
||||
propertyRemovals(0),
|
||||
#if !ENABLE_INTL_API
|
||||
thousandsSeparator(0),
|
||||
decimalSeparator(0),
|
||||
numGrouping(0),
|
||||
#endif
|
||||
mathCache_(NULL),
|
||||
dtoaState(NULL),
|
||||
trustedPrincipals_(NULL),
|
||||
@ -1011,7 +1013,9 @@ JSRuntime::~JSRuntime()
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !ENABLE_INTL_API
|
||||
FinishRuntimeNumberState(this);
|
||||
#endif
|
||||
FinishAtoms(this);
|
||||
|
||||
if (dtoaState)
|
||||
|
@ -1181,10 +1181,12 @@ struct JSRuntime : js::RuntimeFriendFields,
|
||||
*/
|
||||
uint32_t propertyRemovals;
|
||||
|
||||
/* Number localization, used by jsnum.c */
|
||||
#if !ENABLE_INTL_API
|
||||
/* Number localization, used by jsnum.cpp. */
|
||||
const char *thousandsSeparator;
|
||||
const char *decimalSeparator;
|
||||
const char *numGrouping;
|
||||
#endif
|
||||
|
||||
private:
|
||||
js::MathCache *mathCache_;
|
||||
|
@ -445,6 +445,26 @@ IsNumber(const Value &v)
|
||||
return v.isNumber() || (v.isObject() && v.toObject().hasClass(&NumberClass));
|
||||
}
|
||||
|
||||
JS_ALWAYS_INLINE bool
|
||||
num_nop(JSContext *cx, CallArgs args)
|
||||
{
|
||||
JS_ASSERT(IsNumber(args.thisv()));
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
JSBool
|
||||
js::num_CheckThisNumber(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
// CallNonGenericMethod will handle proxies correctly and throw exceptions
|
||||
// in the right circumstances, but will report date_CheckThisNumber as the
|
||||
// function name in the message. We need a better solution:
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=844677
|
||||
return CallNonGenericMethod<IsNumber, num_nop>(cx, args);
|
||||
}
|
||||
|
||||
inline double
|
||||
Extract(const Value &v)
|
||||
{
|
||||
@ -612,6 +632,7 @@ num_toString(JSContext *cx, unsigned argc, Value *vp)
|
||||
return CallNonGenericMethod<IsNumber, num_toString_impl>(cx, args);
|
||||
}
|
||||
|
||||
#if !ENABLE_INTL_API
|
||||
JS_ALWAYS_INLINE bool
|
||||
num_toLocaleString_impl(JSContext *cx, CallArgs args)
|
||||
{
|
||||
@ -744,6 +765,7 @@ num_toLocaleString(JSContext *cx, unsigned argc, Value *vp)
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return CallNonGenericMethod<IsNumber, num_toLocaleString_impl>(cx, args);
|
||||
}
|
||||
#endif
|
||||
|
||||
JS_ALWAYS_INLINE bool
|
||||
num_valueOf_impl(JSContext *cx, CallArgs args)
|
||||
@ -892,11 +914,19 @@ static JSFunctionSpec number_methods[] = {
|
||||
JS_FN(js_toSource_str, num_toSource, 0, 0),
|
||||
#endif
|
||||
JS_FN(js_toString_str, num_toString, 1, 0),
|
||||
JS_FN(js_toLocaleString_str, num_toLocaleString, 0, 0),
|
||||
JS_FN(js_valueOf_str, js_num_valueOf, 0, 0),
|
||||
JS_FN("toFixed", num_toFixed, 1, 0),
|
||||
JS_FN("toExponential", num_toExponential, 1, 0),
|
||||
JS_FN("toPrecision", num_toPrecision, 1, 0),
|
||||
|
||||
// This must be at the end because of bug 853075: functions listed after
|
||||
// self-hosted methods aren't available in self-hosted code.
|
||||
#if ENABLE_INTL_API
|
||||
{js_toLocaleString_str, {NULL, NULL}, 0,0, "Number_toLocaleString"},
|
||||
#else
|
||||
JS_FN(js_toLocaleString_str, num_toLocaleString, 0,0),
|
||||
#endif
|
||||
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
@ -1044,6 +1074,10 @@ js::InitRuntimeNumberState(JSRuntime *rt)
|
||||
|
||||
number_constants[NC_MIN_VALUE].dval = MOZ_DOUBLE_MIN_VALUE();
|
||||
|
||||
// XXX If ENABLE_INTL_API becomes true all the time at some point,
|
||||
// js::InitRuntimeNumberState is no longer fallible, and we should
|
||||
// change its return type.
|
||||
#if !ENABLE_INTL_API
|
||||
/* Copy locale-specific separators into the runtime strings. */
|
||||
const char *thousandsSeparator, *decimalPoint, *grouping;
|
||||
#ifdef HAVE_LOCALECONV
|
||||
@ -1087,9 +1121,11 @@ js::InitRuntimeNumberState(JSRuntime *rt)
|
||||
|
||||
js_memcpy(storage, grouping, groupingSize);
|
||||
rt->numGrouping = grouping;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
#if !ENABLE_INTL_API
|
||||
void
|
||||
js::FinishRuntimeNumberState(JSRuntime *rt)
|
||||
{
|
||||
@ -1100,6 +1136,7 @@ js::FinishRuntimeNumberState(JSRuntime *rt)
|
||||
char *storage = const_cast<char *>(rt->thousandsSeparator);
|
||||
js_free(storage);
|
||||
}
|
||||
#endif
|
||||
|
||||
JSObject *
|
||||
js_InitNumberClass(JSContext *cx, HandleObject obj)
|
||||
|
@ -23,8 +23,10 @@ namespace js {
|
||||
extern bool
|
||||
InitRuntimeNumberState(JSRuntime *rt);
|
||||
|
||||
#if !ENABLE_INTL_API
|
||||
extern void
|
||||
FinishRuntimeNumberState(JSRuntime *rt);
|
||||
#endif
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
@ -147,6 +149,15 @@ ToNumber(JSContext *cx, Value *vp)
|
||||
JSBool
|
||||
num_parseInt(JSContext *cx, unsigned argc, Value *vp);
|
||||
|
||||
/**
|
||||
* Checks that the this value provided meets the requirements for "this Number
|
||||
* object" in ES5.1, 15.7.4, and throws a TypeError if not.
|
||||
*
|
||||
* Usage: callFunction(num_CheckThisNumber, this);
|
||||
*/
|
||||
extern JSBool
|
||||
num_CheckThisNumber(JSContext *cx, unsigned argc, js::Value *vp);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
/*
|
||||
|
@ -1,20 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
var BUGNUMBER = "446494";
|
||||
var summary = "num.toLocaleString should handle exponents";
|
||||
var actual, expect;
|
||||
|
||||
printBugNumber(BUGNUMBER);
|
||||
printStatus(summary);
|
||||
|
||||
expect = '1e-10';
|
||||
actual = 1e-10.toLocaleString();
|
||||
reportCompare(expect, actual, summary + ': ' + expect);
|
||||
|
||||
expect = 'Infinity';
|
||||
actual = Infinity.toLocaleString();
|
||||
reportCompare(expect, actual, summary + ': ' + expect);
|
@ -475,6 +475,7 @@ JSFunctionSpec intrinsic_functions[] = {
|
||||
JS_FN("intl_Collator_availableLocales", intl_Collator_availableLocales, 0,0),
|
||||
JS_FN("intl_availableCollations", intl_availableCollations, 1,0),
|
||||
JS_FN("intl_CompareStrings", intl_CompareStrings, 3,0),
|
||||
JS_FN("intl_NumberFormat", intl_NumberFormat, 2,0),
|
||||
JS_FN("intl_NumberFormat_availableLocales", intl_NumberFormat_availableLocales, 0,0),
|
||||
JS_FN("intl_numberingSystem", intl_numberingSystem, 1,0),
|
||||
JS_FN("intl_FormatNumber", intl_FormatNumber, 2,0),
|
||||
@ -483,6 +484,9 @@ JSFunctionSpec intrinsic_functions[] = {
|
||||
JS_FN("intl_patternForSkeleton", intl_patternForSkeleton, 2,0),
|
||||
JS_FN("intl_FormatDateTime", intl_FormatDateTime, 2,0),
|
||||
|
||||
// See jsnum.h for descriptions of the num_* functions.
|
||||
JS_FN("num_CheckThisNumber", num_CheckThisNumber, 2,0),
|
||||
|
||||
#ifdef DEBUG
|
||||
JS_FN("Dump", intrinsic_Dump, 1,0),
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user