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
24 #include "wine/debug.h"
26 WINE_DEFAULT_DEBUG_CHANNEL(jscript
);
34 #define NUMBER_TOSTRING_BUF_SIZE 64
35 #define NUMBER_DTOA_SIZE 18
37 static inline NumberInstance
*number_from_jsdisp(jsdisp_t
*jsdisp
)
39 return CONTAINING_RECORD(jsdisp
, NumberInstance
, dispex
);
42 static inline NumberInstance
*number_from_vdisp(vdisp_t
*vdisp
)
44 return number_from_jsdisp(vdisp
->u
.jsdisp
);
47 static inline NumberInstance
*number_this(vdisp_t
*jsthis
)
49 return is_vclass(jsthis
, JSCLASS_NUMBER
) ? number_from_vdisp(jsthis
) : NULL
;
52 static inline void number_to_str(double d
, WCHAR
*buf
, int size
, int *dec_point
)
57 /* TODO: this function should print doubles with bigger precision */
58 assert(size
>=2 && size
<=NUMBER_DTOA_SIZE
&& d
>=0);
63 *dec_point
= floor(log10(d
));
64 l
= d
*pow(10, size
-*dec_point
-1);
72 for(i
=size
-2; i
>=0; i
--) {
77 /* log10 was wrong by 1 or rounding changed number of digits */
80 memmove(buf
+1, buf
, size
-2);
82 }else if(buf
[0]=='0' && buf
[1]>='1' && buf
[1]<='9') {
84 memmove(buf
, buf
+1, size
-2);
89 static inline jsstr_t
*number_to_fixed(double val
, int prec
)
91 WCHAR buf
[NUMBER_DTOA_SIZE
];
92 int dec_point
, size
, buf_size
, buf_pos
;
97 TRACE("%lf %d\n", val
, prec
);
105 buf_size
= log10(val
)+prec
+2;
107 buf_size
= prec
? prec
+1 : 2;
108 if(buf_size
> NUMBER_DTOA_SIZE
)
109 buf_size
= NUMBER_DTOA_SIZE
;
111 number_to_str(val
, buf
, buf_size
, &dec_point
);
123 ret
= jsstr_alloc_buf(size
, &str
);
131 for(;buf_pos
<buf_size
-1 && dec_point
; dec_point
--)
132 str
[size
++] = buf
[buf_pos
++];
136 for(; dec_point
>0; dec_point
--)
141 for(; dec_point
<0 && prec
; dec_point
++, prec
--)
143 for(; buf_pos
<buf_size
-1 && prec
; prec
--)
144 str
[size
++] = buf
[buf_pos
++];
145 for(; prec
; prec
--) {
153 static inline jsstr_t
*number_to_exponential(double val
, int prec
)
155 WCHAR buf
[NUMBER_DTOA_SIZE
], *pbuf
;
156 int dec_point
, size
, buf_size
, exp_size
= 1;
167 if(buf_size
<2 || buf_size
>NUMBER_DTOA_SIZE
)
168 buf_size
= NUMBER_DTOA_SIZE
;
169 number_to_str(val
, buf
, buf_size
, &dec_point
);
172 for(; buf_size
>1 && buf
[buf_size
-1]=='0'; buf_size
--)
176 while(dec_point
>=size
|| dec_point
<=-size
) {
182 size
= buf_size
+2+exp_size
; /* 2 = strlen(e+) */
184 size
= buf_size
+3+exp_size
; /* 3 = strlen(.e+) */
186 size
= prec
+4+exp_size
; /* 4 = strlen(0.e+) */
190 ret
= jsstr_alloc_buf(size
, &str
);
198 str
[size
++] = *pbuf
++;
202 str
[size
++] = *pbuf
++;
203 for(; prec
>buf_size
-1; prec
--)
211 dec_point
= -dec_point
;
215 str
[--size
] = '0'+dec_point
%10;
224 /* ECMA-262 3rd Edition 15.7.4.2 */
225 static HRESULT
Number_toString(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
228 NumberInstance
*number
;
236 if(!(number
= number_this(jsthis
)))
237 return JS_E_NUMBER_EXPECTED
;
240 hres
= to_int32(ctx
, argv
[0], &radix
);
244 if(radix
< 2 || radix
> 36)
245 return JS_E_INVALIDARG
;
250 if(radix
==10 || !isfinite(val
)) {
251 hres
= to_string(ctx
, jsval_number(val
), &str
);
256 DOUBLE integ
, frac
, log_radix
= 0;
257 WCHAR buf
[NUMBER_TOSTRING_BUF_SIZE
+16];
271 while(integ
>=1 && idx
<NUMBER_TOSTRING_BUF_SIZE
) {
272 buf
[idx
] = fmod(integ
, radix
);
273 if(buf
[idx
]<10) buf
[idx
] += '0';
274 else buf
[idx
] += 'a'-10;
279 if(idx
<NUMBER_TOSTRING_BUF_SIZE
) {
280 INT beg
= buf
[0]=='-'?1:0;
286 buf
[beg
++] = buf
[end
];
291 if(idx
!= NUMBER_TOSTRING_BUF_SIZE
) buf
[idx
++] = '.';
293 while(frac
>0 && idx
<NUMBER_TOSTRING_BUF_SIZE
) {
295 buf
[idx
] = fmod(frac
, radix
);
297 if(buf
[idx
]<10) buf
[idx
] += '0';
298 else buf
[idx
] += 'a'-10;
302 if(idx
==NUMBER_TOSTRING_BUF_SIZE
&& !exp
) {
304 idx
= (buf
[0]=='-') ? 1 : 0;
305 log_radix
= floor(log(val
)/log(radix
));
306 val
*= pow(radix
, -log_radix
);
313 while(buf
[idx
-1] == '0') idx
--;
314 if(buf
[idx
-1] == '.') idx
--;
323 log_radix
= -log_radix
;
327 swprintf(&buf
[idx
], ARRAY_SIZE(buf
) - idx
, L
"(e%c%d)", ch
, (int)log_radix
);
330 else buf
[idx
] = '\0';
332 str
= jsstr_alloc(buf
);
334 return E_OUTOFMEMORY
;
338 *r
= jsval_string(str
);
344 static HRESULT
Number_toLocaleString(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
351 static HRESULT
Number_toFixed(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
354 NumberInstance
*number
;
362 if(!(number
= number_this(jsthis
)))
363 return JS_E_NUMBER_EXPECTED
;
366 hres
= to_int32(ctx
, argv
[0], &prec
);
370 if(prec
< 0 || prec
> 20)
371 return JS_E_FRACTION_DIGITS_OUT_OF_RANGE
;
376 hres
= to_string(ctx
, jsval_number(val
), &str
);
380 str
= number_to_fixed(val
, prec
);
382 return E_OUTOFMEMORY
;
386 *r
= jsval_string(str
);
392 static HRESULT
Number_toExponential(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
395 NumberInstance
*number
;
403 if(!(number
= number_this(jsthis
)))
404 return JS_E_NUMBER_EXPECTED
;
407 hres
= to_int32(ctx
, argv
[0], &prec
);
411 if(prec
< 0 || prec
> 20)
412 return JS_E_FRACTION_DIGITS_OUT_OF_RANGE
;
417 hres
= to_string(ctx
, jsval_number(val
), &str
);
423 str
= number_to_exponential(val
, prec
);
425 return E_OUTOFMEMORY
;
429 *r
= jsval_string(str
);
435 static HRESULT
Number_toPrecision(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
438 NumberInstance
*number
;
444 if(!(number
= number_this(jsthis
)))
445 return JS_E_NUMBER_EXPECTED
;
448 hres
= to_int32(ctx
, argv
[0], &prec
);
452 if(prec
<1 || prec
>21)
453 return JS_E_PRECISION_OUT_OF_RANGE
;
457 if(!isfinite(val
) || !prec
) {
458 hres
= to_string(ctx
, jsval_number(val
), &str
);
463 size
= floor(log10(val
>0 ? val
: -val
)) + 1;
468 str
= number_to_exponential(val
, prec
-1);
470 str
= number_to_fixed(val
, prec
-size
);
472 return E_OUTOFMEMORY
;
476 *r
= jsval_string(str
);
482 static HRESULT
Number_valueOf(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
485 NumberInstance
*number
;
489 if(!(number
= number_this(jsthis
)))
490 return JS_E_NUMBER_EXPECTED
;
493 *r
= jsval_number(number
->value
);
497 static HRESULT
Number_get_value(script_ctx_t
*ctx
, jsdisp_t
*jsthis
, jsval_t
*r
)
499 NumberInstance
*number
= number_from_jsdisp(jsthis
);
501 TRACE("(%p)\n", number
);
503 *r
= jsval_number(number
->value
);
507 static const builtin_prop_t Number_props
[] = {
508 {L
"toExponential", Number_toExponential
, PROPF_METHOD
|1},
509 {L
"toFixed", Number_toFixed
, PROPF_METHOD
},
510 {L
"toLocaleString", Number_toLocaleString
, PROPF_METHOD
},
511 {L
"toPrecision", Number_toPrecision
, PROPF_METHOD
|1},
512 {L
"toString", Number_toString
, PROPF_METHOD
|1},
513 {L
"valueOf", Number_valueOf
, PROPF_METHOD
}
516 static const builtin_info_t Number_info
= {
518 {NULL
, NULL
,0, Number_get_value
},
519 ARRAY_SIZE(Number_props
),
525 static const builtin_info_t NumberInst_info
= {
527 {NULL
, NULL
,0, Number_get_value
},
533 static HRESULT
NumberConstr_value(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
545 *r
= jsval_number(0);
549 hres
= to_number(ctx
, argv
[0], &n
);
554 *r
= jsval_number(n
);
557 case DISPATCH_CONSTRUCT
: {
561 hres
= to_number(ctx
, argv
[0], &n
);
568 hres
= create_number(ctx
, n
, &obj
);
576 FIXME("unimplemented flags %x\n", flags
);
583 static HRESULT
alloc_number(script_ctx_t
*ctx
, jsdisp_t
*object_prototype
, NumberInstance
**ret
)
585 NumberInstance
*number
;
588 number
= heap_alloc_zero(sizeof(NumberInstance
));
590 return E_OUTOFMEMORY
;
593 hres
= init_dispex(&number
->dispex
, ctx
, &Number_info
, object_prototype
);
595 hres
= init_dispex_from_constr(&number
->dispex
, ctx
, &NumberInst_info
, ctx
->number_constr
);
605 HRESULT
create_number_constr(script_ctx_t
*ctx
, jsdisp_t
*object_prototype
, jsdisp_t
**ret
)
607 NumberInstance
*number
;
610 hres
= alloc_number(ctx
, object_prototype
, &number
);
615 hres
= create_builtin_constructor(ctx
, NumberConstr_value
, L
"Number", NULL
,
616 PROPF_CONSTR
|1, &number
->dispex
, ret
);
618 jsdisp_release(&number
->dispex
);
622 HRESULT
create_number(script_ctx_t
*ctx
, double value
, jsdisp_t
**ret
)
624 NumberInstance
*number
;
627 hres
= alloc_number(ctx
, NULL
, &number
);
631 number
->value
= value
;
633 *ret
= &number
->dispex
;