msvcp90: Added num_put<char> class stub.
[wine/multimedia.git] / dlls / jscript / number.c
blob3d2283aec348676c057bfca8d333bb657610087f
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, DISPPARAMS *dp,
222 VARIANT *retv, 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(arg_cnt(dp)) {
236 hres = to_int32(ctx, get_arg(dp, 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 VARIANT v;
249 num_set_val(&v, val);
250 hres = to_string(ctx, &v, ei, &str);
251 if(FAILED(hres))
252 return hres;
253 }else {
254 INT idx = 0;
255 DOUBLE integ, frac, log_radix = 0;
256 WCHAR buf[NUMBER_TOSTRING_BUF_SIZE+16];
257 BOOL exp = FALSE;
259 if(val<0) {
260 val = -val;
261 buf[idx++] = '-';
264 while(1) {
265 integ = floor(val);
266 frac = val-integ;
268 if(integ == 0)
269 buf[idx++] = '0';
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;
274 integ /= radix;
275 idx++;
278 if(idx<NUMBER_TOSTRING_BUF_SIZE) {
279 INT beg = buf[0]=='-'?1:0;
280 INT end = idx-1;
281 WCHAR wch;
283 while(end > beg) {
284 wch = buf[beg];
285 buf[beg++] = buf[end];
286 buf[end--] = wch;
290 if(idx != NUMBER_TOSTRING_BUF_SIZE) buf[idx++] = '.';
292 while(frac>0 && idx<NUMBER_TOSTRING_BUF_SIZE) {
293 frac *= radix;
294 buf[idx] = fmod(frac, radix);
295 frac -= buf[idx];
296 if(buf[idx]<10) buf[idx] += '0';
297 else buf[idx] += 'a'-10;
298 idx++;
301 if(idx==NUMBER_TOSTRING_BUF_SIZE && !exp) {
302 exp = TRUE;
303 idx = (buf[0]=='-') ? 1 : 0;
304 log_radix = floor(log(val)/log(radix));
305 val *= pow(radix, -log_radix);
306 continue;
309 break;
312 while(buf[idx-1] == '0') idx--;
313 if(buf[idx-1] == '.') idx--;
315 if(exp) {
316 if(log_radix==0)
317 buf[idx] = 0;
318 else {
319 static const WCHAR formatW[] = {'(','e','%','c','%','d',')',0};
320 WCHAR ch;
322 if(log_radix<0) {
323 log_radix = -log_radix;
324 ch = '-';
326 else ch = '+';
327 sprintfW(&buf[idx], formatW, ch, (int)log_radix);
330 else buf[idx] = '\0';
332 str = SysAllocString(buf);
333 if(!str)
334 return E_OUTOFMEMORY;
337 if(retv) {
338 V_VT(retv) = VT_BSTR;
339 V_BSTR(retv) = str;
340 }else {
341 SysFreeString(str);
343 return S_OK;
346 static HRESULT Number_toLocaleString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
347 VARIANT *retv, jsexcept_t *ei)
349 FIXME("\n");
350 return E_NOTIMPL;
353 static HRESULT Number_toFixed(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
354 VARIANT *retv, jsexcept_t *ei)
356 NumberInstance *number;
357 DOUBLE val;
358 INT prec = 0;
359 BSTR str;
360 HRESULT hres;
362 TRACE("\n");
364 if(!(number = number_this(jsthis)))
365 return throw_type_error(ctx, ei, JS_E_NUMBER_EXPECTED, NULL);
367 if(arg_cnt(dp)) {
368 hres = to_int32(ctx, get_arg(dp, 0), ei, &prec);
369 if(FAILED(hres))
370 return hres;
372 if(prec<0 || prec>20)
373 return throw_range_error(ctx, ei, JS_E_FRACTION_DIGITS_OUT_OF_RANGE, NULL);
376 val = number->value;
377 if(isinf(val) || isnan(val)) {
378 VARIANT v;
380 num_set_val(&v, val);
381 hres = to_string(ctx, &v, ei, &str);
382 if(FAILED(hres))
383 return hres;
384 }else {
385 number_to_fixed(val, prec, &str);
388 if(retv) {
389 V_VT(retv) = VT_BSTR;
390 V_BSTR(retv) = str;
391 }else {
392 SysFreeString(str);
394 return S_OK;
397 static HRESULT Number_toExponential(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
398 VARIANT *retv, jsexcept_t *ei)
400 NumberInstance *number;
401 DOUBLE val;
402 INT prec = 0;
403 BSTR str;
404 HRESULT hres;
406 TRACE("\n");
408 if(!(number = number_this(jsthis)))
409 return throw_type_error(ctx, ei, JS_E_NUMBER_EXPECTED, NULL);
411 if(arg_cnt(dp)) {
412 hres = to_int32(ctx, get_arg(dp, 0), ei, &prec);
413 if(FAILED(hres))
414 return hres;
416 if(prec<0 || prec>20)
417 return throw_range_error(ctx, ei, JS_E_FRACTION_DIGITS_OUT_OF_RANGE, NULL);
420 val = number->value;
421 if(isinf(val) || isnan(val)) {
422 VARIANT v;
424 num_set_val(&v, val);
425 hres = to_string(ctx, &v, ei, &str);
426 if(FAILED(hres))
427 return hres;
428 }else {
429 if(!prec)
430 prec--;
431 number_to_exponential(val, prec, &str);
434 if(retv) {
435 V_VT(retv) = VT_BSTR;
436 V_BSTR(retv) = str;
437 }else {
438 SysFreeString(str);
440 return S_OK;
443 static HRESULT Number_toPrecision(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
444 VARIANT *retv, jsexcept_t *ei)
446 NumberInstance *number;
447 INT prec = 0, size;
448 DOUBLE val;
449 BSTR str;
450 HRESULT hres;
452 if(!(number = number_this(jsthis)))
453 return throw_type_error(ctx, ei, JS_E_NUMBER_EXPECTED, NULL);
455 if(arg_cnt(dp)) {
456 hres = to_int32(ctx, get_arg(dp, 0), ei, &prec);
457 if(FAILED(hres))
458 return hres;
460 if(prec<1 || prec>21)
461 return throw_range_error(ctx, ei, JS_E_PRECISION_OUT_OF_RANGE, NULL);
464 val = number->value;
465 if(isinf(val) || isnan(val) || !prec) {
466 VARIANT v;
468 num_set_val(&v, val);
469 hres = to_string(ctx, &v, ei, &str);
470 if(FAILED(hres))
471 return hres;
472 }else {
473 if(val != 0)
474 size = floor(log10(val>0 ? val : -val)) + 1;
475 else
476 size = 1;
478 if(size > prec)
479 number_to_exponential(val, prec-1, &str);
480 else
481 number_to_fixed(val, prec-size, &str);
484 if(retv) {
485 V_VT(retv) = VT_BSTR;
486 V_BSTR(retv) = str;
487 }else {
488 SysFreeString(str);
490 return S_OK;
493 static HRESULT Number_valueOf(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
494 VARIANT *retv, jsexcept_t *ei)
496 NumberInstance *number;
498 TRACE("\n");
500 if(!(number = number_this(jsthis)))
501 return throw_type_error(ctx, ei, JS_E_NUMBER_EXPECTED, NULL);
503 if(retv)
504 num_set_val(retv, number->value);
505 return S_OK;
508 static HRESULT Number_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
509 VARIANT *retv, jsexcept_t *ei)
511 NumberInstance *number = number_from_vdisp(jsthis);
513 switch(flags) {
514 case INVOKE_FUNC:
515 return throw_type_error(ctx, ei, JS_E_FUNCTION_EXPECTED, NULL);
516 case DISPATCH_PROPERTYGET:
517 num_set_val(retv, number->value);
518 break;
520 default:
521 FIXME("flags %x\n", flags);
522 return E_NOTIMPL;
525 return S_OK;
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 = {
538 JSCLASS_NUMBER,
539 {NULL, Number_value, 0},
540 sizeof(Number_props)/sizeof(*Number_props),
541 Number_props,
542 NULL,
543 NULL
546 static HRESULT NumberConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
547 VARIANT *retv, jsexcept_t *ei)
549 double n;
550 HRESULT hres;
552 TRACE("\n");
554 switch(flags) {
555 case INVOKE_FUNC:
556 if(!arg_cnt(dp)) {
557 if(retv)
558 num_set_int(retv, 0);
559 return S_OK;
562 hres = to_number(ctx, get_arg(dp, 0), ei, &n);
563 if(FAILED(hres))
564 return hres;
566 if(retv)
567 num_set_val(retv, n);
568 break;
570 case DISPATCH_CONSTRUCT: {
571 jsdisp_t *obj;
573 if(arg_cnt(dp)) {
574 hres = to_number(ctx, get_arg(dp, 0), ei, &n);
575 if(FAILED(hres))
576 return hres;
577 }else {
578 n = 0;
581 hres = create_number(ctx, n, &obj);
582 if(FAILED(hres))
583 return hres;
585 var_set_jsdisp(retv, obj);
586 break;
588 default:
589 FIXME("unimplemented flags %x\n", flags);
590 return E_NOTIMPL;
593 return S_OK;
596 static HRESULT alloc_number(script_ctx_t *ctx, jsdisp_t *object_prototype, NumberInstance **ret)
598 NumberInstance *number;
599 HRESULT hres;
601 number = heap_alloc_zero(sizeof(NumberInstance));
602 if(!number)
603 return E_OUTOFMEMORY;
605 if(object_prototype)
606 hres = init_dispex(&number->dispex, ctx, &Number_info, object_prototype);
607 else
608 hres = init_dispex_from_constr(&number->dispex, ctx, &Number_info, ctx->number_constr);
609 if(FAILED(hres))
610 return hres;
612 *ret = number;
613 return S_OK;
616 HRESULT create_number_constr(script_ctx_t *ctx, jsdisp_t *object_prototype, jsdisp_t **ret)
618 NumberInstance *number;
619 HRESULT hres;
621 static const WCHAR NumberW[] = {'N','u','m','b','e','r',0};
623 hres = alloc_number(ctx, object_prototype, &number);
624 if(FAILED(hres))
625 return hres;
627 number->value = 0;
628 hres = create_builtin_function(ctx, NumberConstr_value, NumberW, NULL,
629 PROPF_CONSTR|1, &number->dispex, ret);
631 jsdisp_release(&number->dispex);
632 return hres;
635 HRESULT create_number(script_ctx_t *ctx, double value, jsdisp_t **ret)
637 NumberInstance *number;
638 HRESULT hres;
640 hres = alloc_number(ctx, NULL, &number);
641 if(FAILED(hres))
642 return hres;
644 number->value = value;
646 *ret = &number->dispex;
647 return S_OK;