[PATCH v2 1/6] jscript: Implement Number.prototype.toLocaleString.

Gabriel Ivăncescu gabrielopcode at gmail.com
Wed Apr 20 11:56:34 CDT 2022


Signed-off-by: Gabriel Ivăncescu <gabrielopcode at gmail.com>
---

I kept the fraction removal since I couldn't think of a better way for
now. However, the other improvements have been done.

 dlls/jscript/jscript.h    |  1 +
 dlls/jscript/number.c     | 82 ++++++++++++++++++++++++++++++++++++++-
 dlls/jscript/tests/api.js |  7 ++++
 3 files changed, 88 insertions(+), 2 deletions(-)

diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h
index 7ed4425..406efa9 100644
--- a/dlls/jscript/jscript.h
+++ b/dlls/jscript/jscript.h
@@ -449,6 +449,7 @@ HRESULT regexp_string_match(script_ctx_t*,jsdisp_t*,jsstr_t*,jsval_t*) DECLSPEC_
 
 BOOL bool_obj_value(jsdisp_t*) DECLSPEC_HIDDEN;
 unsigned array_get_length(jsdisp_t*) DECLSPEC_HIDDEN;
+HRESULT localize_number(script_ctx_t*,DOUBLE,jsstr_t**) DECLSPEC_HIDDEN;
 
 HRESULT JSGlobal_eval(script_ctx_t*,jsval_t,WORD,unsigned,jsval_t*,jsval_t*) DECLSPEC_HIDDEN;
 HRESULT Object_get_proto_(script_ctx_t*,jsval_t,WORD,unsigned,jsval_t*,jsval_t*) DECLSPEC_HIDDEN;
diff --git a/dlls/jscript/number.c b/dlls/jscript/number.c
index be733fb..64404ba 100644
--- a/dlls/jscript/number.c
+++ b/dlls/jscript/number.c
@@ -17,6 +17,7 @@
  */
 
 #include <math.h>
+#include <locale.h>
 #include <assert.h>
 
 #include "jscript.h"
@@ -341,11 +342,88 @@ static HRESULT Number_toString(script_ctx_t *ctx, jsval_t vthis, WORD flags, uns
     return S_OK;
 }
 
+HRESULT localize_number(script_ctx_t *ctx, DOUBLE val, jsstr_t **ret)
+{
+    WCHAR buf[314], *numstr, *p, *frac = NULL;
+    BOOL remove_fraction = FALSE;
+    jsstr_t *str, *tmp;
+    _locale_t locale;
+    unsigned convlen;
+    int len;
+
+    /* FIXME: Localize this? */
+    if(!isfinite(val))
+        return to_string(ctx, jsval_number(val), ret);
+
+    /* Native never uses an exponent, even if the number is very large, it will in fact
+       return all the digits (with thousands separators). jscript.dll uses two digits for
+       fraction even if they are zero (likely default numDigits) and always returns them,
+       while mshtml's jscript only if they are non-zero (on same locale settings); this
+       applies even for very small numbers, such as 0.0000999, which will simply be 0 */
+    if(!(locale = _create_locale(LC_ALL, "C")))
+        return E_OUTOFMEMORY;
+    len = _swprintf_l(buf, ARRAY_SIZE(buf), L"%.2f", locale, val);
+    _free_locale(locale);
+
+    if(!(convlen = GetNumberFormatW(ctx->lcid, 0, buf, NULL, NULL, 0)) ||
+       !(str = jsstr_alloc_buf(convlen - 1, &numstr)))
+        return E_OUTOFMEMORY;
+
+    if(!GetNumberFormatW(ctx->lcid, 0, buf, NULL, numstr, convlen)) {
+        jsstr_release(str);
+        return E_OUTOFMEMORY;
+    }
+
+    if(ctx->version >= SCRIPTLANGUAGEVERSION_ES5) {
+        while(len--) {
+            if(buf[len] != '0') {
+                if(buf[len] == '.')
+                    remove_fraction = TRUE;
+                break;
+            }
+        }
+    }
+
+    if(remove_fraction && GetLocaleInfoW(ctx->lcid, LOCALE_SDECIMAL, buf, ARRAY_SIZE(buf))) {
+        p = numstr;
+        while(*p) {
+            if(!(p = wcsstr(p, buf)))
+                break;
+            frac = p++;
+        }
+        if(frac) {
+            tmp = jsstr_alloc_len(numstr, frac - numstr);
+            jsstr_release(str);
+            if(!tmp)
+                return E_OUTOFMEMORY;
+            str = tmp;
+        }
+    }
+
+    *ret = str;
+    return S_OK;
+}
+
 static HRESULT Number_toLocaleString(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
         jsval_t *r)
 {
-    FIXME("\n");
-    return E_NOTIMPL;
+    jsstr_t *str;
+    HRESULT hres;
+    DOUBLE val;
+
+    TRACE("\n");
+
+    hres = numberval_this(vthis, &val);
+    if(FAILED(hres))
+        return hres;
+
+    if(r) {
+        hres = localize_number(ctx, val, &str);
+        if(FAILED(hres))
+            return hres;
+        *r = jsval_string(str);
+    }
+    return S_OK;
 }
 
 static HRESULT Number_toFixed(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
diff --git a/dlls/jscript/tests/api.js b/dlls/jscript/tests/api.js
index 1efc023..8653946 100644
--- a/dlls/jscript/tests/api.js
+++ b/dlls/jscript/tests/api.js
@@ -1365,6 +1365,11 @@ ok(tmp === "0", "num().toString = " + tmp);
 tmp = (new Number(5.5)).toString(2);
 ok(tmp === "101.1", "num(5.5).toString(2) = " + tmp);
 
+tmp = (new Number(12)).toLocaleString();
+ok(tmp.indexOf(String.fromCharCode(0)) == -1, "invalid null byte");
+tmp = Number.prototype.toLocaleString.call(NaN);
+ok(tmp.indexOf(String.fromCharCode(0)) == -1, "invalid null byte");
+
 tmp = (new Number(3)).toFixed(3);
 ok(tmp === "3.000", "num(3).toFixed(3) = " + tmp);
 tmp = (new Number(3)).toFixed();
@@ -2594,6 +2599,8 @@ testException(function() {arr.test();}, "E_NO_PROPERTY");
 testException(function() {[1,2,3].sort(nullDisp);}, "E_JSCRIPT_EXPECTED");
 testException(function() {Number.prototype.toString.call(arr);}, "E_NOT_NUM");
 testException(function() {Number.prototype.toFixed.call(arr);}, "E_NOT_NUM");
+testException(function() {Number.prototype.toLocaleString.call(arr);}, "E_NOT_NUM");
+testException(function() {Number.prototype.toLocaleString.call(null);}, "E_NOT_NUM");
 testException(function() {(new Number(3)).toString(1);}, "E_INVALID_CALL_ARG");
 testException(function() {(new Number(3)).toFixed(21);}, "E_FRACTION_DIGITS_OUT_OF_RANGE");
 testException(function() {(new Number(1)).toPrecision(0);}, "E_PRECISION_OUT_OF_RANGE");
-- 
2.34.1




More information about the wine-devel mailing list