jscript: Always use jsval-based to_string implementation.
[wine.git] / dlls / jscript / number.c
blob714632c877f61907b22570e35d0ad8a6f9661524
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 "config.h"
20 #include "wine/port.h"
22 #include <math.h>
23 #include <assert.h>
25 #include "jscript.h"
27 #include "wine/debug.h"
29 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
31 typedef struct {
32 jsdisp_t dispex;
34 double value;
35 } NumberInstance;
37 static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0};
38 static const WCHAR toLocaleStringW[] = {'t','o','L','o','c','a','l','e','S','t','r','i','n','g',0};
39 static const WCHAR toFixedW[] = {'t','o','F','i','x','e','d',0};
40 static const WCHAR toExponentialW[] = {'t','o','E','x','p','o','n','e','n','t','i','a','l',0};
41 static const WCHAR toPrecisionW[] = {'t','o','P','r','e','c','i','s','i','o','n',0};
42 static const WCHAR valueOfW[] = {'v','a','l','u','e','O','f',0};
44 #define NUMBER_TOSTRING_BUF_SIZE 64
45 #define NUMBER_DTOA_SIZE 18
47 static inline NumberInstance *number_from_vdisp(vdisp_t *vdisp)
49 return (NumberInstance*)vdisp->u.jsdisp;
52 static inline NumberInstance *number_this(vdisp_t *jsthis)
54 return is_vclass(jsthis, JSCLASS_NUMBER) ? number_from_vdisp(jsthis) : NULL;
57 static inline void dtoa(double d, WCHAR *buf, int size, int *dec_point)
59 ULONGLONG l;
60 int i;
62 /* TODO: this function should print doubles with bigger precision */
63 assert(size>=2 && size<=NUMBER_DTOA_SIZE && d>=0);
65 if(d == 0)
66 *dec_point = 0;
67 else
68 *dec_point = floor(log10(d));
69 l = d*pow(10, size-*dec_point-1);
71 if(l%10 >= 5)
72 l = l/10+1;
73 else
74 l /= 10;
76 buf[size-1] = 0;
77 for(i=size-2; i>=0; i--) {
78 buf[i] = '0'+l%10;
79 l /= 10;
82 /* log10 was wrong by 1 or rounding changed number of digits */
83 if(l) {
84 (*dec_point)++;
85 memmove(buf+1, buf, size-2);
86 buf[0] = '0'+l;
87 }else if(buf[0]=='0' && buf[1]>='1' && buf[1]<='9') {
88 (*dec_point)--;
89 memmove(buf, buf+1, size-2);
90 buf[size-2] = '0';
94 static inline void number_to_fixed(double val, int prec, BSTR *out)
96 WCHAR buf[NUMBER_DTOA_SIZE];
97 int dec_point, size, buf_size, buf_pos;
98 BOOL neg = FALSE;
99 BSTR str;
101 if(val < 0) {
102 neg = TRUE;
103 val = -val;
106 if(val<=-1 || val>=1)
107 buf_size = log10(val)+prec+2;
108 else
109 buf_size = prec+1;
110 if(buf_size > NUMBER_DTOA_SIZE)
111 buf_size = NUMBER_DTOA_SIZE;
113 dtoa(val, buf, buf_size, &dec_point);
114 dec_point++;
115 size = 0;
116 if(neg)
117 size++;
118 if(dec_point > 0)
119 size += dec_point;
120 else
121 size++;
122 if(prec)
123 size += prec+1;
125 str = SysAllocStringLen(NULL, size);
126 size = buf_pos = 0;
127 if(neg)
128 str[size++] = '-';
129 if(dec_point > 0) {
130 for(;buf_pos<buf_size-1 && dec_point; dec_point--)
131 str[size++] = buf[buf_pos++];
132 }else {
133 str[size++] = '0';
135 for(; dec_point>0; dec_point--)
136 str[size++] = '0';
137 if(prec) {
138 str[size++] = '.';
140 for(; dec_point<0 && prec; dec_point++, prec--)
141 str[size++] = '0';
142 for(; buf_pos<buf_size-1 && prec; prec--)
143 str[size++] = buf[buf_pos++];
144 for(; prec; prec--) {
145 str[size++] = '0';
148 str[size++] = 0;
150 *out = str;
153 static inline void number_to_exponential(double val, int prec, BSTR *out)
155 WCHAR buf[NUMBER_DTOA_SIZE], *pbuf;
156 int dec_point, size, buf_size, exp_size = 1;
157 BOOL neg = FALSE;
158 BSTR str;
160 if(val < 0) {
161 neg = TRUE;
162 val = -val;
165 buf_size = prec+2;
166 if(buf_size<2 || buf_size>NUMBER_DTOA_SIZE)
167 buf_size = NUMBER_DTOA_SIZE;
168 dtoa(val, buf, buf_size, &dec_point);
169 buf_size--;
170 if(prec == -1)
171 for(; buf_size>1 && buf[buf_size-1]=='0'; buf_size--)
172 buf[buf_size-1] = 0;
174 size = 10;
175 while(dec_point>=size || dec_point<=-size) {
176 size *= 10;
177 exp_size++;
180 if(buf_size == 1)
181 size = buf_size+2+exp_size; /* 2 = strlen(e+) */
182 else if(prec == -1)
183 size = buf_size+3+exp_size; /* 3 = strlen(.e+) */
184 else
185 size = prec+4+exp_size; /* 4 = strlen(0.e+) */
186 if(neg)
187 size++;
188 str = SysAllocStringLen(NULL, size);
190 size = 0;
191 pbuf = buf;
192 if(neg)
193 str[size++] = '-';
194 str[size++] = *pbuf++;
195 if(buf_size != 1) {
196 str[size++] = '.';
197 while(*pbuf)
198 str[size++] = *pbuf++;
199 for(; prec>buf_size-1; prec--)
200 str[size++] = '0';
202 str[size++] = 'e';
203 if(dec_point >= 0) {
204 str[size++] = '+';
205 }else {
206 str[size++] = '-';
207 dec_point = -dec_point;
209 size += exp_size;
210 do {
211 str[--size] = '0'+dec_point%10;
212 dec_point /= 10;
213 }while(dec_point>0);
214 size += exp_size;
215 str[size] = 0;
217 *out = str;
220 /* ECMA-262 3rd Edition 15.7.4.2 */
221 static HRESULT Number_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
222 jsval_t *r, jsexcept_t *ei)
224 NumberInstance *number;
225 INT radix = 10;
226 DOUBLE val;
227 BSTR str;
228 HRESULT hres;
230 TRACE("\n");
232 if(!(number = number_this(jsthis)))
233 return throw_type_error(ctx, ei, JS_E_NUMBER_EXPECTED, NULL);
235 if(argc) {
236 hres = to_int32(ctx, argv[0], ei, &radix);
237 if(FAILED(hres))
238 return hres;
240 if(radix<2 || radix>36)
241 return throw_type_error(ctx, ei, JS_E_INVALIDARG, NULL);
244 val = number->value;
246 if(radix==10 || isnan(val) || isinf(val)) {
247 hres = to_string(ctx, jsval_number(val), ei, &str);
248 if(FAILED(hres))
249 return hres;
250 }else {
251 INT idx = 0;
252 DOUBLE integ, frac, log_radix = 0;
253 WCHAR buf[NUMBER_TOSTRING_BUF_SIZE+16];
254 BOOL exp = FALSE;
256 if(val<0) {
257 val = -val;
258 buf[idx++] = '-';
261 while(1) {
262 integ = floor(val);
263 frac = val-integ;
265 if(integ == 0)
266 buf[idx++] = '0';
267 while(integ>=1 && idx<NUMBER_TOSTRING_BUF_SIZE) {
268 buf[idx] = fmod(integ, radix);
269 if(buf[idx]<10) buf[idx] += '0';
270 else buf[idx] += 'a'-10;
271 integ /= radix;
272 idx++;
275 if(idx<NUMBER_TOSTRING_BUF_SIZE) {
276 INT beg = buf[0]=='-'?1:0;
277 INT end = idx-1;
278 WCHAR wch;
280 while(end > beg) {
281 wch = buf[beg];
282 buf[beg++] = buf[end];
283 buf[end--] = wch;
287 if(idx != NUMBER_TOSTRING_BUF_SIZE) buf[idx++] = '.';
289 while(frac>0 && idx<NUMBER_TOSTRING_BUF_SIZE) {
290 frac *= radix;
291 buf[idx] = fmod(frac, radix);
292 frac -= buf[idx];
293 if(buf[idx]<10) buf[idx] += '0';
294 else buf[idx] += 'a'-10;
295 idx++;
298 if(idx==NUMBER_TOSTRING_BUF_SIZE && !exp) {
299 exp = TRUE;
300 idx = (buf[0]=='-') ? 1 : 0;
301 log_radix = floor(log(val)/log(radix));
302 val *= pow(radix, -log_radix);
303 continue;
306 break;
309 while(buf[idx-1] == '0') idx--;
310 if(buf[idx-1] == '.') idx--;
312 if(exp) {
313 if(log_radix==0)
314 buf[idx] = 0;
315 else {
316 static const WCHAR formatW[] = {'(','e','%','c','%','d',')',0};
317 WCHAR ch;
319 if(log_radix<0) {
320 log_radix = -log_radix;
321 ch = '-';
323 else ch = '+';
324 sprintfW(&buf[idx], formatW, ch, (int)log_radix);
327 else buf[idx] = '\0';
329 str = SysAllocString(buf);
330 if(!str)
331 return E_OUTOFMEMORY;
334 if(r)
335 *r = jsval_string(str);
336 else
337 SysFreeString(str);
338 return S_OK;
341 static HRESULT Number_toLocaleString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
342 jsval_t *r, jsexcept_t *ei)
344 FIXME("\n");
345 return E_NOTIMPL;
348 static HRESULT Number_toFixed(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
349 jsval_t *r, jsexcept_t *ei)
351 NumberInstance *number;
352 DOUBLE val;
353 INT prec = 0;
354 BSTR str;
355 HRESULT hres;
357 TRACE("\n");
359 if(!(number = number_this(jsthis)))
360 return throw_type_error(ctx, ei, JS_E_NUMBER_EXPECTED, NULL);
362 if(argc) {
363 hres = to_int32(ctx, argv[0], ei, &prec);
364 if(FAILED(hres))
365 return hres;
367 if(prec<0 || prec>20)
368 return throw_range_error(ctx, ei, JS_E_FRACTION_DIGITS_OUT_OF_RANGE, NULL);
371 val = number->value;
372 if(isinf(val) || isnan(val)) {
373 hres = to_string(ctx, jsval_number(val), ei, &str);
374 if(FAILED(hres))
375 return hres;
376 }else {
377 number_to_fixed(val, prec, &str);
380 if(r)
381 *r = jsval_string(str);
382 else
383 SysFreeString(str);
384 return S_OK;
387 static HRESULT Number_toExponential(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
388 jsval_t *r, jsexcept_t *ei)
390 NumberInstance *number;
391 DOUBLE val;
392 INT prec = 0;
393 BSTR str;
394 HRESULT hres;
396 TRACE("\n");
398 if(!(number = number_this(jsthis)))
399 return throw_type_error(ctx, ei, JS_E_NUMBER_EXPECTED, NULL);
401 if(argc) {
402 hres = to_int32(ctx, argv[0], ei, &prec);
403 if(FAILED(hres))
404 return hres;
406 if(prec<0 || prec>20)
407 return throw_range_error(ctx, ei, JS_E_FRACTION_DIGITS_OUT_OF_RANGE, NULL);
410 val = number->value;
411 if(isinf(val) || isnan(val)) {
412 hres = to_string(ctx, jsval_number(val), ei, &str);
413 if(FAILED(hres))
414 return hres;
415 }else {
416 if(!prec)
417 prec--;
418 number_to_exponential(val, prec, &str);
421 if(r)
422 *r = jsval_string(str);
423 else
424 SysFreeString(str);
425 return S_OK;
428 static HRESULT Number_toPrecision(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
429 jsval_t *r, jsexcept_t *ei)
431 NumberInstance *number;
432 INT prec = 0, size;
433 DOUBLE val;
434 BSTR str;
435 HRESULT hres;
437 if(!(number = number_this(jsthis)))
438 return throw_type_error(ctx, ei, JS_E_NUMBER_EXPECTED, NULL);
440 if(argc) {
441 hres = to_int32(ctx, argv[0], ei, &prec);
442 if(FAILED(hres))
443 return hres;
445 if(prec<1 || prec>21)
446 return throw_range_error(ctx, ei, JS_E_PRECISION_OUT_OF_RANGE, NULL);
449 val = number->value;
450 if(isinf(val) || isnan(val) || !prec) {
451 hres = to_string(ctx, jsval_number(val), ei, &str);
452 if(FAILED(hres))
453 return hres;
454 }else {
455 if(val != 0)
456 size = floor(log10(val>0 ? val : -val)) + 1;
457 else
458 size = 1;
460 if(size > prec)
461 number_to_exponential(val, prec-1, &str);
462 else
463 number_to_fixed(val, prec-size, &str);
466 if(r)
467 *r = jsval_string(str);
468 else
469 SysFreeString(str);
470 return S_OK;
473 static HRESULT Number_valueOf(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
474 jsval_t *r, jsexcept_t *ei)
476 NumberInstance *number;
478 TRACE("\n");
480 if(!(number = number_this(jsthis)))
481 return throw_type_error(ctx, ei, JS_E_NUMBER_EXPECTED, NULL);
483 if(r)
484 *r = jsval_number(number->value);
485 return S_OK;
488 static HRESULT Number_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
489 jsval_t *r, jsexcept_t *ei)
491 NumberInstance *number = number_from_vdisp(jsthis);
493 switch(flags) {
494 case INVOKE_FUNC:
495 return throw_type_error(ctx, ei, JS_E_FUNCTION_EXPECTED, NULL);
496 case DISPATCH_PROPERTYGET:
497 *r = jsval_number(number->value);
498 break;
500 default:
501 FIXME("flags %x\n", flags);
502 return E_NOTIMPL;
505 return S_OK;
508 static const builtin_prop_t Number_props[] = {
509 {toExponentialW, Number_toExponential, PROPF_METHOD|1},
510 {toFixedW, Number_toFixed, PROPF_METHOD},
511 {toLocaleStringW, Number_toLocaleString, PROPF_METHOD},
512 {toPrecisionW, Number_toPrecision, PROPF_METHOD|1},
513 {toStringW, Number_toString, PROPF_METHOD|1},
514 {valueOfW, Number_valueOf, PROPF_METHOD}
517 static const builtin_info_t Number_info = {
518 JSCLASS_NUMBER,
519 {NULL, Number_value, 0},
520 sizeof(Number_props)/sizeof(*Number_props),
521 Number_props,
522 NULL,
523 NULL
526 static const builtin_info_t NumberInst_info = {
527 JSCLASS_NUMBER,
528 {NULL, Number_value, 0},
529 0, NULL,
530 NULL,
531 NULL
534 static HRESULT NumberConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
535 jsval_t *r, jsexcept_t *ei)
537 double n;
538 HRESULT hres;
540 TRACE("\n");
542 switch(flags) {
543 case INVOKE_FUNC:
544 if(!argc) {
545 if(r)
546 *r = jsval_number(0);
547 return S_OK;
550 hres = to_number(ctx, argv[0], ei, &n);
551 if(FAILED(hres))
552 return hres;
554 if(r)
555 *r = jsval_number(n);
556 break;
558 case DISPATCH_CONSTRUCT: {
559 jsdisp_t *obj;
561 if(argc) {
562 hres = to_number(ctx, argv[0], ei, &n);
563 if(FAILED(hres))
564 return hres;
565 }else {
566 n = 0;
569 hres = create_number(ctx, n, &obj);
570 if(FAILED(hres))
571 return hres;
573 *r = jsval_obj(obj);
574 break;
576 default:
577 FIXME("unimplemented flags %x\n", flags);
578 return E_NOTIMPL;
581 return S_OK;
584 static HRESULT alloc_number(script_ctx_t *ctx, jsdisp_t *object_prototype, NumberInstance **ret)
586 NumberInstance *number;
587 HRESULT hres;
589 number = heap_alloc_zero(sizeof(NumberInstance));
590 if(!number)
591 return E_OUTOFMEMORY;
593 if(object_prototype)
594 hres = init_dispex(&number->dispex, ctx, &Number_info, object_prototype);
595 else
596 hres = init_dispex_from_constr(&number->dispex, ctx, &NumberInst_info, ctx->number_constr);
597 if(FAILED(hres))
598 return hres;
600 *ret = number;
601 return S_OK;
604 HRESULT create_number_constr(script_ctx_t *ctx, jsdisp_t *object_prototype, jsdisp_t **ret)
606 NumberInstance *number;
607 HRESULT hres;
609 static const WCHAR NumberW[] = {'N','u','m','b','e','r',0};
611 hres = alloc_number(ctx, object_prototype, &number);
612 if(FAILED(hres))
613 return hres;
615 number->value = 0;
616 hres = create_builtin_constructor(ctx, NumberConstr_value, NumberW, NULL,
617 PROPF_CONSTR|1, &number->dispex, ret);
619 jsdisp_release(&number->dispex);
620 return hres;
623 HRESULT create_number(script_ctx_t *ctx, double value, jsdisp_t **ret)
625 NumberInstance *number;
626 HRESULT hres;
628 hres = alloc_number(ctx, NULL, &number);
629 if(FAILED(hres))
630 return hres;
632 number->value = value;
634 *ret = &number->dispex;
635 return S_OK;