msvcp90: Implement time_get<char> ctors and dtors (Valgrind).
[wine/multimedia.git] / dlls / jscript / number.c
blob0a648d053101b50a327fdfeb3f5c15105c49bcba
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_jsdisp(jsdisp_t *jsdisp)
49 return CONTAINING_RECORD(jsdisp, NumberInstance, dispex);
52 static inline NumberInstance *number_from_vdisp(vdisp_t *vdisp)
54 return number_from_jsdisp(vdisp->u.jsdisp);
57 static inline NumberInstance *number_this(vdisp_t *jsthis)
59 return is_vclass(jsthis, JSCLASS_NUMBER) ? number_from_vdisp(jsthis) : NULL;
62 static inline void number_to_str(double d, WCHAR *buf, int size, int *dec_point)
64 ULONGLONG l;
65 int i;
67 /* TODO: this function should print doubles with bigger precision */
68 assert(size>=2 && size<=NUMBER_DTOA_SIZE && d>=0);
70 if(d == 0)
71 *dec_point = 0;
72 else
73 *dec_point = floor(log10(d));
74 l = d*pow(10, size-*dec_point-1);
76 if(l%10 >= 5)
77 l = l/10+1;
78 else
79 l /= 10;
81 buf[size-1] = 0;
82 for(i=size-2; i>=0; i--) {
83 buf[i] = '0'+l%10;
84 l /= 10;
87 /* log10 was wrong by 1 or rounding changed number of digits */
88 if(l) {
89 (*dec_point)++;
90 memmove(buf+1, buf, size-2);
91 buf[0] = '0'+l;
92 }else if(buf[0]=='0' && buf[1]>='1' && buf[1]<='9') {
93 (*dec_point)--;
94 memmove(buf, buf+1, size-2);
95 buf[size-2] = '0';
99 static inline jsstr_t *number_to_fixed(double val, int prec)
101 WCHAR buf[NUMBER_DTOA_SIZE];
102 int dec_point, size, buf_size, buf_pos;
103 BOOL neg = FALSE;
104 jsstr_t *ret;
105 WCHAR *str;
107 TRACE("%lf %d\n", val, prec);
109 if(val < 0) {
110 neg = TRUE;
111 val = -val;
114 if(val >= 1)
115 buf_size = log10(val)+prec+2;
116 else
117 buf_size = prec ? prec+1 : 2;
118 if(buf_size > NUMBER_DTOA_SIZE)
119 buf_size = NUMBER_DTOA_SIZE;
121 number_to_str(val, buf, buf_size, &dec_point);
122 dec_point++;
123 size = 0;
124 if(neg)
125 size++;
126 if(dec_point > 0)
127 size += dec_point;
128 else
129 size++;
130 if(prec)
131 size += prec+1;
133 str = jsstr_alloc_buf(size, &ret);
134 if(!ret)
135 return NULL;
137 size = buf_pos = 0;
138 if(neg)
139 str[size++] = '-';
140 if(dec_point > 0) {
141 for(;buf_pos<buf_size-1 && dec_point; dec_point--)
142 str[size++] = buf[buf_pos++];
143 }else {
144 str[size++] = '0';
146 for(; dec_point>0; dec_point--)
147 str[size++] = '0';
148 if(prec) {
149 str[size++] = '.';
151 for(; dec_point<0 && prec; dec_point++, prec--)
152 str[size++] = '0';
153 for(; buf_pos<buf_size-1 && prec; prec--)
154 str[size++] = buf[buf_pos++];
155 for(; prec; prec--) {
156 str[size++] = '0';
159 str[size++] = 0;
160 return ret;
163 static inline jsstr_t *number_to_exponential(double val, int prec)
165 WCHAR buf[NUMBER_DTOA_SIZE], *pbuf;
166 int dec_point, size, buf_size, exp_size = 1;
167 BOOL neg = FALSE;
168 jsstr_t *ret;
169 WCHAR *str;
171 if(val < 0) {
172 neg = TRUE;
173 val = -val;
176 buf_size = prec+2;
177 if(buf_size<2 || buf_size>NUMBER_DTOA_SIZE)
178 buf_size = NUMBER_DTOA_SIZE;
179 number_to_str(val, buf, buf_size, &dec_point);
180 buf_size--;
181 if(prec == -1)
182 for(; buf_size>1 && buf[buf_size-1]=='0'; buf_size--)
183 buf[buf_size-1] = 0;
185 size = 10;
186 while(dec_point>=size || dec_point<=-size) {
187 size *= 10;
188 exp_size++;
191 if(buf_size == 1)
192 size = buf_size+2+exp_size; /* 2 = strlen(e+) */
193 else if(prec == -1)
194 size = buf_size+3+exp_size; /* 3 = strlen(.e+) */
195 else
196 size = prec+4+exp_size; /* 4 = strlen(0.e+) */
197 if(neg)
198 size++;
200 str = jsstr_alloc_buf(size, &ret);
201 if(!ret)
202 return NULL;
204 size = 0;
205 pbuf = buf;
206 if(neg)
207 str[size++] = '-';
208 str[size++] = *pbuf++;
209 if(buf_size != 1) {
210 str[size++] = '.';
211 while(*pbuf)
212 str[size++] = *pbuf++;
213 for(; prec>buf_size-1; prec--)
214 str[size++] = '0';
216 str[size++] = 'e';
217 if(dec_point >= 0) {
218 str[size++] = '+';
219 }else {
220 str[size++] = '-';
221 dec_point = -dec_point;
223 size += exp_size;
224 do {
225 str[--size] = '0'+dec_point%10;
226 dec_point /= 10;
227 }while(dec_point>0);
228 size += exp_size;
229 str[size] = 0;
231 return ret;
234 /* ECMA-262 3rd Edition 15.7.4.2 */
235 static HRESULT Number_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
236 jsval_t *r)
238 NumberInstance *number;
239 INT radix = 10;
240 DOUBLE val;
241 jsstr_t *str;
242 HRESULT hres;
244 TRACE("\n");
246 if(!(number = number_this(jsthis)))
247 return throw_type_error(ctx, JS_E_NUMBER_EXPECTED, NULL);
249 if(argc) {
250 hres = to_int32(ctx, argv[0], &radix);
251 if(FAILED(hres))
252 return hres;
254 if(radix<2 || radix>36)
255 return throw_type_error(ctx, JS_E_INVALIDARG, NULL);
258 val = number->value;
260 if(radix==10 || isnan(val) || isinf(val)) {
261 hres = to_string(ctx, jsval_number(val), &str);
262 if(FAILED(hres))
263 return hres;
264 }else {
265 INT idx = 0;
266 DOUBLE integ, frac, log_radix = 0;
267 WCHAR buf[NUMBER_TOSTRING_BUF_SIZE+16];
268 BOOL exp = FALSE;
270 if(val<0) {
271 val = -val;
272 buf[idx++] = '-';
275 while(1) {
276 integ = floor(val);
277 frac = val-integ;
279 if(integ == 0)
280 buf[idx++] = '0';
281 while(integ>=1 && idx<NUMBER_TOSTRING_BUF_SIZE) {
282 buf[idx] = fmod(integ, radix);
283 if(buf[idx]<10) buf[idx] += '0';
284 else buf[idx] += 'a'-10;
285 integ /= radix;
286 idx++;
289 if(idx<NUMBER_TOSTRING_BUF_SIZE) {
290 INT beg = buf[0]=='-'?1:0;
291 INT end = idx-1;
292 WCHAR wch;
294 while(end > beg) {
295 wch = buf[beg];
296 buf[beg++] = buf[end];
297 buf[end--] = wch;
301 if(idx != NUMBER_TOSTRING_BUF_SIZE) buf[idx++] = '.';
303 while(frac>0 && idx<NUMBER_TOSTRING_BUF_SIZE) {
304 frac *= radix;
305 buf[idx] = fmod(frac, radix);
306 frac -= buf[idx];
307 if(buf[idx]<10) buf[idx] += '0';
308 else buf[idx] += 'a'-10;
309 idx++;
312 if(idx==NUMBER_TOSTRING_BUF_SIZE && !exp) {
313 exp = TRUE;
314 idx = (buf[0]=='-') ? 1 : 0;
315 log_radix = floor(log(val)/log(radix));
316 val *= pow(radix, -log_radix);
317 continue;
320 break;
323 while(buf[idx-1] == '0') idx--;
324 if(buf[idx-1] == '.') idx--;
326 if(exp) {
327 if(log_radix==0)
328 buf[idx] = 0;
329 else {
330 static const WCHAR formatW[] = {'(','e','%','c','%','d',')',0};
331 WCHAR ch;
333 if(log_radix<0) {
334 log_radix = -log_radix;
335 ch = '-';
337 else ch = '+';
338 sprintfW(&buf[idx], formatW, ch, (int)log_radix);
341 else buf[idx] = '\0';
343 str = jsstr_alloc(buf);
344 if(!str)
345 return E_OUTOFMEMORY;
348 if(r)
349 *r = jsval_string(str);
350 else
351 jsstr_release(str);
352 return S_OK;
355 static HRESULT Number_toLocaleString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
356 jsval_t *r)
358 FIXME("\n");
359 return E_NOTIMPL;
362 static HRESULT Number_toFixed(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
363 jsval_t *r)
365 NumberInstance *number;
366 DOUBLE val;
367 INT prec = 0;
368 jsstr_t *str;
369 HRESULT hres;
371 TRACE("\n");
373 if(!(number = number_this(jsthis)))
374 return throw_type_error(ctx, JS_E_NUMBER_EXPECTED, NULL);
376 if(argc) {
377 hres = to_int32(ctx, argv[0], &prec);
378 if(FAILED(hres))
379 return hres;
381 if(prec<0 || prec>20)
382 return throw_range_error(ctx, JS_E_FRACTION_DIGITS_OUT_OF_RANGE, NULL);
385 val = number->value;
386 if(isinf(val) || isnan(val)) {
387 hres = to_string(ctx, jsval_number(val), &str);
388 if(FAILED(hres))
389 return hres;
390 }else {
391 str = number_to_fixed(val, prec);
392 if(!str)
393 return E_OUTOFMEMORY;
396 if(r)
397 *r = jsval_string(str);
398 else
399 jsstr_release(str);
400 return S_OK;
403 static HRESULT Number_toExponential(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
404 jsval_t *r)
406 NumberInstance *number;
407 DOUBLE val;
408 INT prec = 0;
409 jsstr_t *str;
410 HRESULT hres;
412 TRACE("\n");
414 if(!(number = number_this(jsthis)))
415 return throw_type_error(ctx, JS_E_NUMBER_EXPECTED, NULL);
417 if(argc) {
418 hres = to_int32(ctx, argv[0], &prec);
419 if(FAILED(hres))
420 return hres;
422 if(prec<0 || prec>20)
423 return throw_range_error(ctx, JS_E_FRACTION_DIGITS_OUT_OF_RANGE, NULL);
426 val = number->value;
427 if(isinf(val) || isnan(val)) {
428 hres = to_string(ctx, jsval_number(val), &str);
429 if(FAILED(hres))
430 return hres;
431 }else {
432 if(!prec)
433 prec--;
434 str = number_to_exponential(val, prec);
435 if(!str)
436 return E_OUTOFMEMORY;
439 if(r)
440 *r = jsval_string(str);
441 else
442 jsstr_release(str);
443 return S_OK;
446 static HRESULT Number_toPrecision(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
447 jsval_t *r)
449 NumberInstance *number;
450 INT prec = 0, size;
451 jsstr_t *str;
452 DOUBLE val;
453 HRESULT hres;
455 if(!(number = number_this(jsthis)))
456 return throw_type_error(ctx, JS_E_NUMBER_EXPECTED, NULL);
458 if(argc) {
459 hres = to_int32(ctx, argv[0], &prec);
460 if(FAILED(hres))
461 return hres;
463 if(prec<1 || prec>21)
464 return throw_range_error(ctx, JS_E_PRECISION_OUT_OF_RANGE, NULL);
467 val = number->value;
468 if(isinf(val) || isnan(val) || !prec) {
469 hres = to_string(ctx, jsval_number(val), &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 str = number_to_exponential(val, prec-1);
480 else
481 str = number_to_fixed(val, prec-size);
482 if(!str)
483 return E_OUTOFMEMORY;
486 if(r)
487 *r = jsval_string(str);
488 else
489 jsstr_release(str);
490 return S_OK;
493 static HRESULT Number_valueOf(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
494 jsval_t *r)
496 NumberInstance *number;
498 TRACE("\n");
500 if(!(number = number_this(jsthis)))
501 return throw_type_error(ctx, JS_E_NUMBER_EXPECTED, NULL);
503 if(r)
504 *r = jsval_number(number->value);
505 return S_OK;
508 static HRESULT Number_get_value(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
510 NumberInstance *number = number_from_jsdisp(jsthis);
512 TRACE("(%p)\n", number);
514 *r = jsval_number(number->value);
515 return S_OK;
518 static const builtin_prop_t Number_props[] = {
519 {toExponentialW, Number_toExponential, PROPF_METHOD|1},
520 {toFixedW, Number_toFixed, PROPF_METHOD},
521 {toLocaleStringW, Number_toLocaleString, PROPF_METHOD},
522 {toPrecisionW, Number_toPrecision, PROPF_METHOD|1},
523 {toStringW, Number_toString, PROPF_METHOD|1},
524 {valueOfW, Number_valueOf, PROPF_METHOD}
527 static const builtin_info_t Number_info = {
528 JSCLASS_NUMBER,
529 {NULL, NULL,0, Number_get_value},
530 sizeof(Number_props)/sizeof(*Number_props),
531 Number_props,
532 NULL,
533 NULL
536 static const builtin_info_t NumberInst_info = {
537 JSCLASS_NUMBER,
538 {NULL, NULL,0, Number_get_value},
539 0, NULL,
540 NULL,
541 NULL
544 static HRESULT NumberConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
545 jsval_t *r)
547 double n;
548 HRESULT hres;
550 TRACE("\n");
552 switch(flags) {
553 case INVOKE_FUNC:
554 if(!argc) {
555 if(r)
556 *r = jsval_number(0);
557 return S_OK;
560 hres = to_number(ctx, argv[0], &n);
561 if(FAILED(hres))
562 return hres;
564 if(r)
565 *r = jsval_number(n);
566 break;
568 case DISPATCH_CONSTRUCT: {
569 jsdisp_t *obj;
571 if(argc) {
572 hres = to_number(ctx, argv[0], &n);
573 if(FAILED(hres))
574 return hres;
575 }else {
576 n = 0;
579 hres = create_number(ctx, n, &obj);
580 if(FAILED(hres))
581 return hres;
583 *r = jsval_obj(obj);
584 break;
586 default:
587 FIXME("unimplemented flags %x\n", flags);
588 return E_NOTIMPL;
591 return S_OK;
594 static HRESULT alloc_number(script_ctx_t *ctx, jsdisp_t *object_prototype, NumberInstance **ret)
596 NumberInstance *number;
597 HRESULT hres;
599 number = heap_alloc_zero(sizeof(NumberInstance));
600 if(!number)
601 return E_OUTOFMEMORY;
603 if(object_prototype)
604 hres = init_dispex(&number->dispex, ctx, &Number_info, object_prototype);
605 else
606 hres = init_dispex_from_constr(&number->dispex, ctx, &NumberInst_info, ctx->number_constr);
607 if(FAILED(hres)) {
608 heap_free(number);
609 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_constructor(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;