windowscodecs: Silence fixme for IID_CMetaBitmapRenderTarget.
[wine.git] / dlls / jscript / number.c
blob27c3d5ecfee601ff0564ddbe8481b4932ceeb340
1 /*
2 * Copyright 2008 Jacek Caban for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #include <math.h>
20 #include <locale.h>
21 #include <assert.h>
23 #include "jscript.h"
25 #include "wine/debug.h"
27 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
29 typedef struct {
30 jsdisp_t dispex;
32 double value;
33 } NumberInstance;
35 #define NUMBER_TOSTRING_BUF_SIZE 64
36 #define NUMBER_DTOA_SIZE 18
38 static inline NumberInstance *number_from_jsdisp(jsdisp_t *jsdisp)
40 return CONTAINING_RECORD(jsdisp, NumberInstance, dispex);
43 static inline HRESULT numberval_this(jsval_t vthis, DOUBLE *ret)
45 jsdisp_t *jsdisp;
46 if(is_number(vthis))
47 *ret = get_number(vthis);
48 else if(is_object_instance(vthis) && (jsdisp = to_jsdisp(get_object(vthis))) && is_class(jsdisp, JSCLASS_NUMBER))
49 *ret = number_from_jsdisp(jsdisp)->value;
50 else
51 return JS_E_NUMBER_EXPECTED;
52 return S_OK;
55 static inline void number_to_str(double d, WCHAR *buf, int size, int *dec_point)
57 ULONGLONG l;
58 int i;
60 /* TODO: this function should print doubles with bigger precision */
61 assert(size>=2 && size<=NUMBER_DTOA_SIZE && d>=0);
63 if(d == 0)
64 *dec_point = 0;
65 else
66 *dec_point = floor(log10(d));
67 l = d*pow(10, size-*dec_point-1);
69 if(l%10 >= 5)
70 l = l/10+1;
71 else
72 l /= 10;
74 buf[size-1] = 0;
75 for(i=size-2; i>=0; i--) {
76 buf[i] = '0'+l%10;
77 l /= 10;
80 /* log10 was wrong by 1 or rounding changed number of digits */
81 if(l) {
82 (*dec_point)++;
83 memmove(buf+1, buf, size-2);
84 buf[0] = '0'+l;
85 }else if(buf[0]=='0' && buf[1]>='1' && buf[1]<='9') {
86 (*dec_point)--;
87 memmove(buf, buf+1, size-2);
88 buf[size-2] = '0';
92 static inline jsstr_t *number_to_fixed(double val, int prec)
94 WCHAR buf[NUMBER_DTOA_SIZE];
95 int dec_point, size, buf_size, buf_pos;
96 BOOL neg = FALSE;
97 jsstr_t *ret;
98 WCHAR *str;
100 TRACE("%lf %d\n", val, prec);
102 if(val < 0) {
103 neg = TRUE;
104 val = -val;
107 if(val >= 1)
108 buf_size = log10(val)+prec+2;
109 else
110 buf_size = prec ? prec+1 : 2;
111 if(buf_size > NUMBER_DTOA_SIZE)
112 buf_size = NUMBER_DTOA_SIZE;
114 number_to_str(val, buf, buf_size, &dec_point);
115 dec_point++;
116 size = 0;
117 if(neg)
118 size++;
119 if(dec_point > 0)
120 size += dec_point;
121 else
122 size++;
123 if(prec)
124 size += prec+1;
126 ret = jsstr_alloc_buf(size, &str);
127 if(!ret)
128 return NULL;
130 size = buf_pos = 0;
131 if(neg)
132 str[size++] = '-';
133 if(dec_point > 0) {
134 for(;buf_pos<buf_size-1 && dec_point; dec_point--)
135 str[size++] = buf[buf_pos++];
136 }else {
137 str[size++] = '0';
139 for(; dec_point>0; dec_point--)
140 str[size++] = '0';
141 if(prec) {
142 str[size++] = '.';
144 for(; dec_point<0 && prec; dec_point++, prec--)
145 str[size++] = '0';
146 for(; buf_pos<buf_size-1 && prec; prec--)
147 str[size++] = buf[buf_pos++];
148 for(; prec; prec--) {
149 str[size++] = '0';
152 str[size++] = 0;
153 return ret;
156 static inline jsstr_t *number_to_exponential(double val, int prec)
158 WCHAR buf[NUMBER_DTOA_SIZE], *pbuf;
159 int dec_point, size, buf_size, exp_size = 1;
160 BOOL neg = FALSE;
161 jsstr_t *ret;
162 WCHAR *str;
164 if(val < 0) {
165 neg = TRUE;
166 val = -val;
169 buf_size = prec+2;
170 if(buf_size<2 || buf_size>NUMBER_DTOA_SIZE)
171 buf_size = NUMBER_DTOA_SIZE;
172 number_to_str(val, buf, buf_size, &dec_point);
173 buf_size--;
174 if(prec == -1)
175 for(; buf_size>1 && buf[buf_size-1]=='0'; buf_size--)
176 buf[buf_size-1] = 0;
178 size = 10;
179 while(dec_point>=size || dec_point<=-size) {
180 size *= 10;
181 exp_size++;
184 if(buf_size == 1)
185 size = buf_size+2+exp_size; /* 2 = strlen(e+) */
186 else if(prec == -1)
187 size = buf_size+3+exp_size; /* 3 = strlen(.e+) */
188 else
189 size = prec+4+exp_size; /* 4 = strlen(0.e+) */
190 if(neg)
191 size++;
193 ret = jsstr_alloc_buf(size, &str);
194 if(!ret)
195 return NULL;
197 size = 0;
198 pbuf = buf;
199 if(neg)
200 str[size++] = '-';
201 str[size++] = *pbuf++;
202 if(buf_size != 1) {
203 str[size++] = '.';
204 while(*pbuf)
205 str[size++] = *pbuf++;
206 for(; prec>buf_size-1; prec--)
207 str[size++] = '0';
209 str[size++] = 'e';
210 if(dec_point >= 0) {
211 str[size++] = '+';
212 }else {
213 str[size++] = '-';
214 dec_point = -dec_point;
216 size += exp_size;
217 do {
218 str[--size] = '0'+dec_point%10;
219 dec_point /= 10;
220 }while(dec_point>0);
221 size += exp_size;
222 str[size] = 0;
224 return ret;
227 /* ECMA-262 3rd Edition 15.7.4.2 */
228 static HRESULT Number_toString(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
229 jsval_t *r)
231 INT radix = 10;
232 DOUBLE val;
233 jsstr_t *str;
234 HRESULT hres;
236 TRACE("\n");
238 hres = numberval_this(vthis, &val);
239 if(FAILED(hres))
240 return hres;
242 if(argc && (ctx->version < SCRIPTLANGUAGEVERSION_ES5 || !is_undefined(argv[0]))) {
243 hres = to_int32(ctx, argv[0], &radix);
244 if(FAILED(hres))
245 return hres;
247 if(radix < 2 || radix > 36)
248 return JS_E_INVALIDARG;
251 if(radix==10 || !isfinite(val)) {
252 hres = to_string(ctx, jsval_number(val), &str);
253 if(FAILED(hres))
254 return hres;
255 }else {
256 INT idx = 0;
257 DOUBLE integ, frac, log_radix = 0;
258 WCHAR buf[NUMBER_TOSTRING_BUF_SIZE+16];
259 BOOL exp = FALSE;
261 if(val<0) {
262 val = -val;
263 buf[idx++] = '-';
266 while(1) {
267 integ = floor(val);
268 frac = val-integ;
270 if(integ == 0)
271 buf[idx++] = '0';
272 while(integ>=1 && idx<NUMBER_TOSTRING_BUF_SIZE) {
273 buf[idx] = fmod(integ, radix);
274 if(buf[idx]<10) buf[idx] += '0';
275 else buf[idx] += 'a'-10;
276 integ /= radix;
277 idx++;
280 if(idx<NUMBER_TOSTRING_BUF_SIZE) {
281 INT beg = buf[0]=='-'?1:0;
282 INT end = idx-1;
283 WCHAR wch;
285 while(end > beg) {
286 wch = buf[beg];
287 buf[beg++] = buf[end];
288 buf[end--] = wch;
292 if(idx != NUMBER_TOSTRING_BUF_SIZE) buf[idx++] = '.';
294 while(frac>0 && idx<NUMBER_TOSTRING_BUF_SIZE) {
295 frac *= radix;
296 buf[idx] = fmod(frac, radix);
297 frac -= buf[idx];
298 if(buf[idx]<10) buf[idx] += '0';
299 else buf[idx] += 'a'-10;
300 idx++;
303 if(idx==NUMBER_TOSTRING_BUF_SIZE && !exp) {
304 exp = TRUE;
305 idx = (buf[0]=='-') ? 1 : 0;
306 log_radix = floor(log(val)/log(radix));
307 val *= pow(radix, -log_radix);
308 continue;
311 break;
314 while(buf[idx-1] == '0') idx--;
315 if(buf[idx-1] == '.') idx--;
317 if(exp) {
318 if(log_radix==0)
319 buf[idx] = 0;
320 else {
321 WCHAR ch;
323 if(log_radix<0) {
324 log_radix = -log_radix;
325 ch = '-';
327 else ch = '+';
328 swprintf(&buf[idx], ARRAY_SIZE(buf) - idx, L"(e%c%d)", ch, (int)log_radix);
331 else buf[idx] = '\0';
333 str = jsstr_alloc(buf);
334 if(!str)
335 return E_OUTOFMEMORY;
338 if(r)
339 *r = jsval_string(str);
340 else
341 jsstr_release(str);
342 return S_OK;
345 HRESULT localize_number(script_ctx_t *ctx, DOUBLE val, BOOL new_format, jsstr_t **ret)
347 WCHAR buf[316], decimal[8], thousands[8], *numstr;
348 NUMBERFMTW *format = NULL, format_buf;
349 LCID lcid = ctx->lcid;
350 _locale_t locale;
351 unsigned convlen;
352 jsstr_t *str;
353 int len;
355 /* FIXME: Localize this */
356 if(!isfinite(val))
357 return to_string(ctx, jsval_number(val), ret);
359 /* Native never uses an exponent, even if the number is very large, it will in fact
360 return all the digits (with thousands separators). jscript.dll uses two digits for
361 fraction even if they are zero (likely default numDigits) and always returns them,
362 while mshtml's jscript uses 3 digits and trims trailing zeros (on same locale).
363 This is even for very small numbers, such as 0.0000999, which will simply be 0. */
364 if(!(locale = _create_locale(LC_ALL, "C")))
365 return E_OUTOFMEMORY;
366 len = _swprintf_l(buf, ARRAY_SIZE(buf), L"%.3f", locale, val);
367 _free_locale(locale);
369 if(new_format) {
370 WCHAR grouping[10];
372 format = &format_buf;
373 format->NumDigits = 3;
374 while(buf[--len] == '0')
375 format->NumDigits--;
377 /* same logic as VarFormatNumber */
378 grouping[2] = '\0';
379 if(!GetLocaleInfoW(lcid, LOCALE_SGROUPING, grouping, ARRAY_SIZE(grouping)))
380 format->Grouping = 3;
381 else
382 format->Grouping = (grouping[2] == '2' ? 32 : grouping[0] - '0');
384 if(!GetLocaleInfoW(lcid, LOCALE_ILZERO | LOCALE_RETURN_NUMBER, (WCHAR*)&format->LeadingZero, 2))
385 format->LeadingZero = 0;
386 if(!GetLocaleInfoW(lcid, LOCALE_INEGNUMBER | LOCALE_RETURN_NUMBER, (WCHAR*)&format->NegativeOrder, 2))
387 format->NegativeOrder = 1;
388 format->lpDecimalSep = decimal;
389 if(!GetLocaleInfoW(lcid, LOCALE_SDECIMAL, decimal, ARRAY_SIZE(decimal)))
390 wcscpy(decimal, L".");
391 format->lpThousandSep = thousands;
392 if(!GetLocaleInfoW(lcid, LOCALE_STHOUSAND, thousands, ARRAY_SIZE(thousands)))
393 wcscpy(thousands, L",");
396 if(!(convlen = GetNumberFormatW(lcid, 0, buf, format, NULL, 0)) ||
397 !(str = jsstr_alloc_buf(convlen - 1, &numstr)))
398 return E_OUTOFMEMORY;
400 if(!GetNumberFormatW(lcid, 0, buf, format, numstr, convlen)) {
401 jsstr_release(str);
402 return E_OUTOFMEMORY;
405 *ret = str;
406 return S_OK;
409 static HRESULT Number_toLocaleString(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
410 jsval_t *r)
412 jsstr_t *str;
413 HRESULT hres;
414 DOUBLE val;
416 TRACE("\n");
418 hres = numberval_this(vthis, &val);
419 if(FAILED(hres)) {
420 if(hres == JS_E_NUMBER_EXPECTED && ctx->version >= SCRIPTLANGUAGEVERSION_ES5)
421 return throw_error(ctx, JS_E_WRONG_THIS, L"Number");
422 return hres;
425 if(r) {
426 hres = localize_number(ctx, val, ctx->version >= SCRIPTLANGUAGEVERSION_ES5, &str);
427 if(FAILED(hres))
428 return hres;
429 *r = jsval_string(str);
431 return S_OK;
434 static HRESULT Number_toFixed(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
435 jsval_t *r)
437 DOUBLE val;
438 INT prec = 0;
439 jsstr_t *str;
440 HRESULT hres;
442 TRACE("\n");
444 hres = numberval_this(vthis, &val);
445 if(FAILED(hres))
446 return hres;
448 if(argc) {
449 hres = to_int32(ctx, argv[0], &prec);
450 if(FAILED(hres))
451 return hres;
453 if(prec < 0 || prec > 20)
454 return JS_E_FRACTION_DIGITS_OUT_OF_RANGE;
457 if(!isfinite(val)) {
458 hres = to_string(ctx, jsval_number(val), &str);
459 if(FAILED(hres))
460 return hres;
461 }else {
462 str = number_to_fixed(val, prec);
463 if(!str)
464 return E_OUTOFMEMORY;
467 if(r)
468 *r = jsval_string(str);
469 else
470 jsstr_release(str);
471 return S_OK;
474 static HRESULT Number_toExponential(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
475 jsval_t *r)
477 DOUBLE val;
478 INT prec = 0;
479 jsstr_t *str;
480 HRESULT hres;
482 TRACE("\n");
484 hres = numberval_this(vthis, &val);
485 if(FAILED(hres))
486 return hres;
488 if(argc) {
489 hres = to_int32(ctx, argv[0], &prec);
490 if(FAILED(hres))
491 return hres;
493 if(prec < 0 || prec > 20)
494 return JS_E_FRACTION_DIGITS_OUT_OF_RANGE;
497 if(!isfinite(val)) {
498 hres = to_string(ctx, jsval_number(val), &str);
499 if(FAILED(hres))
500 return hres;
501 }else {
502 if(!prec)
503 prec--;
504 str = number_to_exponential(val, prec);
505 if(!str)
506 return E_OUTOFMEMORY;
509 if(r)
510 *r = jsval_string(str);
511 else
512 jsstr_release(str);
513 return S_OK;
516 static HRESULT Number_toPrecision(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
517 jsval_t *r)
519 INT prec = 0, size;
520 jsstr_t *str;
521 DOUBLE val;
522 HRESULT hres;
524 hres = numberval_this(vthis, &val);
525 if(FAILED(hres))
526 return hres;
528 if(argc && (ctx->version < 2 || !is_undefined(argv[0]))) {
529 hres = to_int32(ctx, argv[0], &prec);
530 if(FAILED(hres))
531 return hres;
533 if(prec<1 || prec>21)
534 return JS_E_PRECISION_OUT_OF_RANGE;
537 if(!isfinite(val) || !prec) {
538 hres = to_string(ctx, jsval_number(val), &str);
539 if(FAILED(hres))
540 return hres;
541 }else {
542 if(val != 0)
543 size = floor(log10(val>0 ? val : -val)) + 1;
544 else
545 size = 1;
547 if(size > prec)
548 str = number_to_exponential(val, prec-1);
549 else
550 str = number_to_fixed(val, prec-size);
551 if(!str)
552 return E_OUTOFMEMORY;
555 if(r)
556 *r = jsval_string(str);
557 else
558 jsstr_release(str);
559 return S_OK;
562 static HRESULT Number_valueOf(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
563 jsval_t *r)
565 HRESULT hres;
566 DOUBLE val;
568 TRACE("\n");
570 hres = numberval_this(vthis, &val);
571 if(FAILED(hres))
572 return hres;
574 if(r)
575 *r = jsval_number(val);
576 return S_OK;
579 static const builtin_prop_t Number_props[] = {
580 {L"toExponential", Number_toExponential, PROPF_METHOD|1},
581 {L"toFixed", Number_toFixed, PROPF_METHOD},
582 {L"toLocaleString", Number_toLocaleString, PROPF_METHOD},
583 {L"toPrecision", Number_toPrecision, PROPF_METHOD|1},
584 {L"toString", Number_toString, PROPF_METHOD|1},
585 {L"valueOf", Number_valueOf, PROPF_METHOD}
588 static const builtin_info_t Number_info = {
589 JSCLASS_NUMBER,
590 NULL,
591 ARRAY_SIZE(Number_props),
592 Number_props,
593 NULL,
594 NULL
597 static const builtin_info_t NumberInst_info = {
598 JSCLASS_NUMBER,
599 NULL,
600 0, NULL,
601 NULL,
602 NULL
605 static HRESULT NumberConstr_value(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
606 jsval_t *r)
608 double n;
609 HRESULT hres;
611 TRACE("\n");
613 switch(flags) {
614 case INVOKE_FUNC:
615 if(!argc) {
616 if(r)
617 *r = jsval_number(0);
618 return S_OK;
621 hres = to_number(ctx, argv[0], &n);
622 if(FAILED(hres))
623 return hres;
625 if(r)
626 *r = jsval_number(n);
627 break;
629 case DISPATCH_CONSTRUCT: {
630 jsdisp_t *obj;
632 if(argc) {
633 hres = to_number(ctx, argv[0], &n);
634 if(FAILED(hres))
635 return hres;
636 }else {
637 n = 0;
640 if(r) {
641 hres = create_number(ctx, n, &obj);
642 if(FAILED(hres))
643 return hres;
644 *r = jsval_obj(obj);
646 break;
648 default:
649 FIXME("unimplemented flags %x\n", flags);
650 return E_NOTIMPL;
653 return S_OK;
656 static HRESULT alloc_number(script_ctx_t *ctx, jsdisp_t *object_prototype, NumberInstance **ret)
658 NumberInstance *number;
659 HRESULT hres;
661 number = calloc(1, sizeof(NumberInstance));
662 if(!number)
663 return E_OUTOFMEMORY;
665 if(object_prototype)
666 hres = init_dispex(&number->dispex, ctx, &Number_info, object_prototype);
667 else
668 hres = init_dispex_from_constr(&number->dispex, ctx, &NumberInst_info, ctx->number_constr);
669 if(FAILED(hres)) {
670 free(number);
671 return hres;
674 *ret = number;
675 return S_OK;
678 HRESULT create_number_constr(script_ctx_t *ctx, jsdisp_t *object_prototype, jsdisp_t **ret)
680 NumberInstance *number;
681 HRESULT hres;
683 hres = alloc_number(ctx, object_prototype, &number);
684 if(FAILED(hres))
685 return hres;
687 number->value = 0;
688 hres = create_builtin_constructor(ctx, NumberConstr_value, L"Number", NULL,
689 PROPF_CONSTR|1, &number->dispex, ret);
691 jsdisp_release(&number->dispex);
692 return hres;
695 HRESULT create_number(script_ctx_t *ctx, double value, jsdisp_t **ret)
697 NumberInstance *number;
698 HRESULT hres;
700 hres = alloc_number(ctx, NULL, &number);
701 if(FAILED(hres))
702 return hres;
704 number->value = value;
706 *ret = &number->dispex;
707 return S_OK;