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
20 #include "wine/port.h"
27 #include "wine/debug.h"
29 WINE_DEFAULT_DEBUG_CHANNEL(jscript
);
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
)
62 /* TODO: this function should print doubles with bigger precision */
63 assert(size
>=2 && size
<=NUMBER_DTOA_SIZE
&& d
>=0);
68 *dec_point
= floor(log10(d
));
69 l
= d
*pow(10, size
-*dec_point
-1);
77 for(i
=size
-2; i
>=0; i
--) {
82 /* log10 was wrong by 1 or rounding changed number of digits */
85 memmove(buf
+1, buf
, size
-2);
87 }else if(buf
[0]=='0' && buf
[1]>='1' && buf
[1]<='9') {
89 memmove(buf
, buf
+1, size
-2);
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
;
106 if(val
<=-1 || val
>=1)
107 buf_size
= log10(val
)+prec
+2;
110 if(buf_size
> NUMBER_DTOA_SIZE
)
111 buf_size
= NUMBER_DTOA_SIZE
;
113 dtoa(val
, buf
, buf_size
, &dec_point
);
125 str
= SysAllocStringLen(NULL
, size
);
130 for(;buf_pos
<buf_size
-1 && dec_point
; dec_point
--)
131 str
[size
++] = buf
[buf_pos
++];
135 for(; dec_point
>0; dec_point
--)
140 for(; dec_point
<0 && prec
; dec_point
++, prec
--)
142 for(; buf_pos
<buf_size
-1 && prec
; prec
--)
143 str
[size
++] = buf
[buf_pos
++];
144 for(; prec
; prec
--) {
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;
166 if(buf_size
<2 || buf_size
>NUMBER_DTOA_SIZE
)
167 buf_size
= NUMBER_DTOA_SIZE
;
168 dtoa(val
, buf
, buf_size
, &dec_point
);
171 for(; buf_size
>1 && buf
[buf_size
-1]=='0'; buf_size
--)
175 while(dec_point
>=size
|| dec_point
<=-size
) {
181 size
= buf_size
+2+exp_size
; /* 2 = strlen(e+) */
183 size
= buf_size
+3+exp_size
; /* 3 = strlen(.e+) */
185 size
= prec
+4+exp_size
; /* 4 = strlen(0.e+) */
188 str
= SysAllocStringLen(NULL
, size
);
194 str
[size
++] = *pbuf
++;
198 str
[size
++] = *pbuf
++;
199 for(; prec
>buf_size
-1; prec
--)
207 dec_point
= -dec_point
;
211 str
[--size
] = '0'+dec_point
%10;
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
, VARIANT
*argv
,
222 VARIANT
*retv
, jsexcept_t
*ei
)
224 NumberInstance
*number
;
232 if(!(number
= number_this(jsthis
)))
233 return throw_type_error(ctx
, ei
, JS_E_NUMBER_EXPECTED
, NULL
);
236 hres
= to_int32(ctx
, argv
, ei
, &radix
);
240 if(radix
<2 || radix
>36)
241 return throw_type_error(ctx
, ei
, JS_E_INVALIDARG
, NULL
);
246 if(radix
==10 || isnan(val
) || isinf(val
)) {
249 num_set_val(&v
, val
);
250 hres
= to_string(ctx
, &v
, ei
, &str
);
255 DOUBLE integ
, frac
, log_radix
= 0;
256 WCHAR buf
[NUMBER_TOSTRING_BUF_SIZE
+16];
270 while(integ
>=1 && idx
<NUMBER_TOSTRING_BUF_SIZE
) {
271 buf
[idx
] = fmod(integ
, radix
);
272 if(buf
[idx
]<10) buf
[idx
] += '0';
273 else buf
[idx
] += 'a'-10;
278 if(idx
<NUMBER_TOSTRING_BUF_SIZE
) {
279 INT beg
= buf
[0]=='-'?1:0;
285 buf
[beg
++] = buf
[end
];
290 if(idx
!= NUMBER_TOSTRING_BUF_SIZE
) buf
[idx
++] = '.';
292 while(frac
>0 && idx
<NUMBER_TOSTRING_BUF_SIZE
) {
294 buf
[idx
] = fmod(frac
, radix
);
296 if(buf
[idx
]<10) buf
[idx
] += '0';
297 else buf
[idx
] += 'a'-10;
301 if(idx
==NUMBER_TOSTRING_BUF_SIZE
&& !exp
) {
303 idx
= (buf
[0]=='-') ? 1 : 0;
304 log_radix
= floor(log(val
)/log(radix
));
305 val
*= pow(radix
, -log_radix
);
312 while(buf
[idx
-1] == '0') idx
--;
313 if(buf
[idx
-1] == '.') idx
--;
319 static const WCHAR formatW
[] = {'(','e','%','c','%','d',')',0};
323 log_radix
= -log_radix
;
327 sprintfW(&buf
[idx
], formatW
, ch
, (int)log_radix
);
330 else buf
[idx
] = '\0';
332 str
= SysAllocString(buf
);
334 return E_OUTOFMEMORY
;
338 V_VT(retv
) = VT_BSTR
;
346 static HRESULT
Number_toLocaleString(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, VARIANT
*argv
,
347 VARIANT
*retv
, jsexcept_t
*ei
)
353 static HRESULT
Number_toFixed(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, VARIANT
*argv
,
354 VARIANT
*retv
, jsexcept_t
*ei
)
356 NumberInstance
*number
;
364 if(!(number
= number_this(jsthis
)))
365 return throw_type_error(ctx
, ei
, JS_E_NUMBER_EXPECTED
, NULL
);
368 hres
= to_int32(ctx
, argv
, ei
, &prec
);
372 if(prec
<0 || prec
>20)
373 return throw_range_error(ctx
, ei
, JS_E_FRACTION_DIGITS_OUT_OF_RANGE
, NULL
);
377 if(isinf(val
) || isnan(val
)) {
380 num_set_val(&v
, val
);
381 hres
= to_string(ctx
, &v
, ei
, &str
);
385 number_to_fixed(val
, prec
, &str
);
389 V_VT(retv
) = VT_BSTR
;
397 static HRESULT
Number_toExponential(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, VARIANT
*argv
,
398 VARIANT
*retv
, jsexcept_t
*ei
)
400 NumberInstance
*number
;
408 if(!(number
= number_this(jsthis
)))
409 return throw_type_error(ctx
, ei
, JS_E_NUMBER_EXPECTED
, NULL
);
412 hres
= to_int32(ctx
, argv
, ei
, &prec
);
416 if(prec
<0 || prec
>20)
417 return throw_range_error(ctx
, ei
, JS_E_FRACTION_DIGITS_OUT_OF_RANGE
, NULL
);
421 if(isinf(val
) || isnan(val
)) {
424 num_set_val(&v
, val
);
425 hres
= to_string(ctx
, &v
, ei
, &str
);
431 number_to_exponential(val
, prec
, &str
);
435 V_VT(retv
) = VT_BSTR
;
443 static HRESULT
Number_toPrecision(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, VARIANT
*argv
,
444 VARIANT
*retv
, jsexcept_t
*ei
)
446 NumberInstance
*number
;
452 if(!(number
= number_this(jsthis
)))
453 return throw_type_error(ctx
, ei
, JS_E_NUMBER_EXPECTED
, NULL
);
456 hres
= to_int32(ctx
, argv
, ei
, &prec
);
460 if(prec
<1 || prec
>21)
461 return throw_range_error(ctx
, ei
, JS_E_PRECISION_OUT_OF_RANGE
, NULL
);
465 if(isinf(val
) || isnan(val
) || !prec
) {
468 num_set_val(&v
, val
);
469 hres
= to_string(ctx
, &v
, ei
, &str
);
474 size
= floor(log10(val
>0 ? val
: -val
)) + 1;
479 number_to_exponential(val
, prec
-1, &str
);
481 number_to_fixed(val
, prec
-size
, &str
);
485 V_VT(retv
) = VT_BSTR
;
493 static HRESULT
Number_valueOf(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, VARIANT
*argv
,
494 VARIANT
*retv
, jsexcept_t
*ei
)
496 NumberInstance
*number
;
500 if(!(number
= number_this(jsthis
)))
501 return throw_type_error(ctx
, ei
, JS_E_NUMBER_EXPECTED
, NULL
);
504 num_set_val(retv
, number
->value
);
508 static HRESULT
Number_value(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, VARIANT
*argv
,
509 VARIANT
*retv
, jsexcept_t
*ei
)
511 NumberInstance
*number
= number_from_vdisp(jsthis
);
515 return throw_type_error(ctx
, ei
, JS_E_FUNCTION_EXPECTED
, NULL
);
516 case DISPATCH_PROPERTYGET
:
517 num_set_val(retv
, number
->value
);
521 FIXME("flags %x\n", flags
);
528 static const builtin_prop_t Number_props
[] = {
529 {toExponentialW
, Number_toExponential
, PROPF_METHOD
|1},
530 {toFixedW
, Number_toFixed
, PROPF_METHOD
},
531 {toLocaleStringW
, Number_toLocaleString
, PROPF_METHOD
},
532 {toPrecisionW
, Number_toPrecision
, PROPF_METHOD
|1},
533 {toStringW
, Number_toString
, PROPF_METHOD
|1},
534 {valueOfW
, Number_valueOf
, PROPF_METHOD
}
537 static const builtin_info_t Number_info
= {
539 {NULL
, Number_value
, 0},
540 sizeof(Number_props
)/sizeof(*Number_props
),
546 static const builtin_info_t NumberInst_info
= {
548 {NULL
, Number_value
, 0},
554 static HRESULT
NumberConstr_value(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, VARIANT
*argv
,
555 VARIANT
*retv
, jsexcept_t
*ei
)
566 num_set_int(retv
, 0);
570 hres
= to_number(ctx
, argv
, ei
, &n
);
575 num_set_val(retv
, n
);
578 case DISPATCH_CONSTRUCT
: {
582 hres
= to_number(ctx
, argv
, ei
, &n
);
589 hres
= create_number(ctx
, n
, &obj
);
593 var_set_jsdisp(retv
, obj
);
597 FIXME("unimplemented flags %x\n", flags
);
604 static HRESULT
alloc_number(script_ctx_t
*ctx
, jsdisp_t
*object_prototype
, NumberInstance
**ret
)
606 NumberInstance
*number
;
609 number
= heap_alloc_zero(sizeof(NumberInstance
));
611 return E_OUTOFMEMORY
;
614 hres
= init_dispex(&number
->dispex
, ctx
, &Number_info
, object_prototype
);
616 hres
= init_dispex_from_constr(&number
->dispex
, ctx
, &NumberInst_info
, ctx
->number_constr
);
624 HRESULT
create_number_constr(script_ctx_t
*ctx
, jsdisp_t
*object_prototype
, jsdisp_t
**ret
)
626 NumberInstance
*number
;
629 static const WCHAR NumberW
[] = {'N','u','m','b','e','r',0};
631 hres
= alloc_number(ctx
, object_prototype
, &number
);
636 hres
= create_builtin_constructor(ctx
, NumberConstr_value
, NumberW
, NULL
,
637 PROPF_CONSTR
|1, &number
->dispex
, ret
);
639 jsdisp_release(&number
->dispex
);
643 HRESULT
create_number(script_ctx_t
*ctx
, double value
, jsdisp_t
**ret
)
645 NumberInstance
*number
;
648 hres
= alloc_number(ctx
, NULL
, &number
);
652 number
->value
= value
;
654 *ret
= &number
->dispex
;