winecoreaudio.drv: Convert some OSStatus errors to HRESULT.
[wine] / dlls / jscript / number.c
1 /*
2  * Copyright 2008 Jacek Caban for CodeWeavers
3  *
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.
8  *
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.
13  *
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
17  */
18
19 #include "config.h"
20 #include "wine/port.h"
21
22 #include <math.h>
23 #include <assert.h>
24
25 #include "jscript.h"
26
27 #include "wine/debug.h"
28
29 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
30
31 typedef struct {
32     jsdisp_t dispex;
33
34     double value;
35 } NumberInstance;
36
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};
43
44 #define NUMBER_TOSTRING_BUF_SIZE 64
45 #define NUMBER_DTOA_SIZE 18
46
47 static inline NumberInstance *number_from_vdisp(vdisp_t *vdisp)
48 {
49     return (NumberInstance*)vdisp->u.jsdisp;
50 }
51
52 static inline NumberInstance *number_this(vdisp_t *jsthis)
53 {
54     return is_vclass(jsthis, JSCLASS_NUMBER) ? number_from_vdisp(jsthis) : NULL;
55 }
56
57 static inline void dtoa(double d, WCHAR *buf, int size, int *dec_point)
58 {
59     ULONGLONG l;
60     int i;
61
62     /* TODO: this function should print doubles with bigger precision */
63     assert(size>=2 && size<=NUMBER_DTOA_SIZE && d>=0);
64
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);
70
71     if(l%10 >= 5)
72         l = l/10+1;
73     else
74         l /= 10;
75
76     buf[size-1] = 0;
77     for(i=size-2; i>=0; i--) {
78         buf[i] = '0'+l%10;
79         l /= 10;
80     }
81
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';
91     }
92 }
93
94 static inline jsstr_t *number_to_fixed(double val, int prec)
95 {
96     WCHAR buf[NUMBER_DTOA_SIZE];
97     int dec_point, size, buf_size, buf_pos;
98     BOOL neg = FALSE;
99     jsstr_t *ret;
100     WCHAR *str;
101
102     if(val < 0) {
103         neg = TRUE;
104         val = -val;
105     }
106
107     if(val<=-1 || val>=1)
108         buf_size = log10(val)+prec+2;
109     else
110         buf_size = prec+1;
111     if(buf_size > NUMBER_DTOA_SIZE)
112         buf_size = NUMBER_DTOA_SIZE;
113
114     dtoa(val, buf, buf_size, &dec_point);
115     dec_point++;
116     size = 0;
117     if(neg)
118         size++;
119     if(dec_point > 0)
120         size += dec_point;
121     else
122         size++;
123     if(prec)
124         size += prec+1;
125
126     ret = jsstr_alloc_buf(size);
127     if(!ret)
128         return NULL;
129
130     str = ret->str;
131     size = buf_pos = 0;
132     if(neg)
133         str[size++] = '-';
134     if(dec_point > 0) {
135         for(;buf_pos<buf_size-1 && dec_point; dec_point--)
136             str[size++] = buf[buf_pos++];
137     }else {
138         str[size++] = '0';
139     }
140     for(; dec_point>0; dec_point--)
141         str[size++] = '0';
142     if(prec) {
143         str[size++] = '.';
144
145         for(; dec_point<0 && prec; dec_point++, prec--)
146             str[size++] = '0';
147         for(; buf_pos<buf_size-1 && prec; prec--)
148             str[size++] = buf[buf_pos++];
149         for(; prec; prec--) {
150             str[size++] = '0';
151         }
152     }
153     str[size++] = 0;
154     return ret;
155 }
156
157 static inline jsstr_t *number_to_exponential(double val, int prec)
158 {
159     WCHAR buf[NUMBER_DTOA_SIZE], *pbuf;
160     int dec_point, size, buf_size, exp_size = 1;
161     BOOL neg = FALSE;
162     jsstr_t *ret;
163     WCHAR *str;
164
165     if(val < 0) {
166         neg = TRUE;
167         val = -val;
168     }
169
170     buf_size = prec+2;
171     if(buf_size<2 || buf_size>NUMBER_DTOA_SIZE)
172         buf_size = NUMBER_DTOA_SIZE;
173     dtoa(val, buf, buf_size, &dec_point);
174     buf_size--;
175     if(prec == -1)
176         for(; buf_size>1 && buf[buf_size-1]=='0'; buf_size--)
177             buf[buf_size-1] = 0;
178
179     size = 10;
180     while(dec_point>=size || dec_point<=-size) {
181         size *= 10;
182         exp_size++;
183     }
184
185     if(buf_size == 1)
186         size = buf_size+2+exp_size; /* 2 = strlen(e+) */
187     else if(prec == -1)
188         size = buf_size+3+exp_size; /* 3 = strlen(.e+) */
189     else
190         size = prec+4+exp_size; /* 4 = strlen(0.e+) */
191     if(neg)
192         size++;
193
194     ret = jsstr_alloc_buf(size);
195     if(!ret)
196         return NULL;
197
198     str = ret->str;
199     size = 0;
200     pbuf = buf;
201     if(neg)
202         str[size++] = '-';
203     str[size++] = *pbuf++;
204     if(buf_size != 1) {
205         str[size++] = '.';
206         while(*pbuf)
207             str[size++] = *pbuf++;
208         for(; prec>buf_size-1; prec--)
209             str[size++] = '0';
210     }
211     str[size++] = 'e';
212     if(dec_point >= 0) {
213         str[size++] = '+';
214     }else {
215         str[size++] = '-';
216         dec_point = -dec_point;
217     }
218     size += exp_size;
219     do {
220         str[--size] = '0'+dec_point%10;
221         dec_point /= 10;
222     }while(dec_point>0);
223     size += exp_size;
224     str[size] = 0;
225
226     return ret;
227 }
228
229 /* ECMA-262 3rd Edition    15.7.4.2 */
230 static HRESULT Number_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
231         jsval_t *r)
232 {
233     NumberInstance *number;
234     INT radix = 10;
235     DOUBLE val;
236     jsstr_t *str;
237     HRESULT hres;
238
239     TRACE("\n");
240
241     if(!(number = number_this(jsthis)))
242         return throw_type_error(ctx, JS_E_NUMBER_EXPECTED, NULL);
243
244     if(argc) {
245         hres = to_int32(ctx, argv[0], &radix);
246         if(FAILED(hres))
247             return hres;
248
249         if(radix<2 || radix>36)
250             return throw_type_error(ctx, JS_E_INVALIDARG, NULL);
251     }
252
253     val = number->value;
254
255     if(radix==10 || isnan(val) || isinf(val)) {
256         hres = to_string(ctx, jsval_number(val), &str);
257         if(FAILED(hres))
258             return hres;
259     }else {
260         INT idx = 0;
261         DOUBLE integ, frac, log_radix = 0;
262         WCHAR buf[NUMBER_TOSTRING_BUF_SIZE+16];
263         BOOL exp = FALSE;
264
265         if(val<0) {
266             val = -val;
267             buf[idx++] = '-';
268         }
269
270         while(1) {
271             integ = floor(val);
272             frac = val-integ;
273
274             if(integ == 0)
275                 buf[idx++] = '0';
276             while(integ>=1 && idx<NUMBER_TOSTRING_BUF_SIZE) {
277                 buf[idx] = fmod(integ, radix);
278                 if(buf[idx]<10) buf[idx] += '0';
279                 else buf[idx] += 'a'-10;
280                 integ /= radix;
281                 idx++;
282             }
283
284             if(idx<NUMBER_TOSTRING_BUF_SIZE) {
285                 INT beg = buf[0]=='-'?1:0;
286                 INT end = idx-1;
287                 WCHAR wch;
288
289                 while(end > beg) {
290                     wch = buf[beg];
291                     buf[beg++] = buf[end];
292                     buf[end--] = wch;
293                 }
294             }
295
296             if(idx != NUMBER_TOSTRING_BUF_SIZE) buf[idx++] = '.';
297
298             while(frac>0 && idx<NUMBER_TOSTRING_BUF_SIZE) {
299                 frac *= radix;
300                 buf[idx] = fmod(frac, radix);
301                 frac -= buf[idx];
302                 if(buf[idx]<10) buf[idx] += '0';
303                 else buf[idx] += 'a'-10;
304                 idx++;
305             }
306
307             if(idx==NUMBER_TOSTRING_BUF_SIZE && !exp) {
308                 exp = TRUE;
309                 idx = (buf[0]=='-') ? 1 : 0;
310                 log_radix = floor(log(val)/log(radix));
311                 val *= pow(radix, -log_radix);
312                 continue;
313             }
314
315             break;
316         }
317
318         while(buf[idx-1] == '0') idx--;
319         if(buf[idx-1] == '.') idx--;
320
321         if(exp) {
322             if(log_radix==0)
323                 buf[idx] = 0;
324             else {
325                 static const WCHAR formatW[] = {'(','e','%','c','%','d',')',0};
326                 WCHAR ch;
327
328                 if(log_radix<0) {
329                     log_radix = -log_radix;
330                     ch = '-';
331                 }
332                 else ch = '+';
333                 sprintfW(&buf[idx], formatW, ch, (int)log_radix);
334             }
335         }
336         else buf[idx] = '\0';
337
338         str = jsstr_alloc(buf);
339         if(!str)
340             return E_OUTOFMEMORY;
341     }
342
343     if(r)
344         *r = jsval_string(str);
345     else
346         jsstr_release(str);
347     return S_OK;
348 }
349
350 static HRESULT Number_toLocaleString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
351         jsval_t *r)
352 {
353     FIXME("\n");
354     return E_NOTIMPL;
355 }
356
357 static HRESULT Number_toFixed(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
358         jsval_t *r)
359 {
360     NumberInstance *number;
361     DOUBLE val;
362     INT prec = 0;
363     jsstr_t *str;
364     HRESULT hres;
365
366     TRACE("\n");
367
368     if(!(number = number_this(jsthis)))
369         return throw_type_error(ctx, JS_E_NUMBER_EXPECTED, NULL);
370
371     if(argc) {
372         hres = to_int32(ctx, argv[0], &prec);
373         if(FAILED(hres))
374             return hres;
375
376         if(prec<0 || prec>20)
377             return throw_range_error(ctx, JS_E_FRACTION_DIGITS_OUT_OF_RANGE, NULL);
378     }
379
380     val = number->value;
381     if(isinf(val) || isnan(val)) {
382         hres = to_string(ctx, jsval_number(val), &str);
383         if(FAILED(hres))
384             return hres;
385     }else {
386         str = number_to_fixed(val, prec);
387         if(!str)
388             return E_OUTOFMEMORY;
389     }
390
391     if(r)
392         *r = jsval_string(str);
393     else
394         jsstr_release(str);
395     return S_OK;
396 }
397
398 static HRESULT Number_toExponential(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
399         jsval_t *r)
400 {
401     NumberInstance *number;
402     DOUBLE val;
403     INT prec = 0;
404     jsstr_t *str;
405     HRESULT hres;
406
407     TRACE("\n");
408
409     if(!(number = number_this(jsthis)))
410         return throw_type_error(ctx, JS_E_NUMBER_EXPECTED, NULL);
411
412     if(argc) {
413         hres = to_int32(ctx, argv[0], &prec);
414         if(FAILED(hres))
415             return hres;
416
417         if(prec<0 || prec>20)
418             return throw_range_error(ctx, JS_E_FRACTION_DIGITS_OUT_OF_RANGE, NULL);
419     }
420
421     val = number->value;
422     if(isinf(val) || isnan(val)) {
423         hres = to_string(ctx, jsval_number(val), &str);
424         if(FAILED(hres))
425             return hres;
426     }else {
427         if(!prec)
428             prec--;
429         str = number_to_exponential(val, prec);
430         if(!str)
431             return E_OUTOFMEMORY;
432     }
433
434     if(r)
435         *r = jsval_string(str);
436     else
437         jsstr_release(str);
438     return S_OK;
439 }
440
441 static HRESULT Number_toPrecision(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
442         jsval_t *r)
443 {
444     NumberInstance *number;
445     INT prec = 0, size;
446     jsstr_t *str;
447     DOUBLE val;
448     HRESULT hres;
449
450     if(!(number = number_this(jsthis)))
451         return throw_type_error(ctx, JS_E_NUMBER_EXPECTED, NULL);
452
453     if(argc) {
454         hres = to_int32(ctx, argv[0], &prec);
455         if(FAILED(hres))
456             return hres;
457
458         if(prec<1 || prec>21)
459             return throw_range_error(ctx, JS_E_PRECISION_OUT_OF_RANGE, NULL);
460     }
461
462     val = number->value;
463     if(isinf(val) || isnan(val) || !prec) {
464         hres = to_string(ctx, jsval_number(val), &str);
465         if(FAILED(hres))
466             return hres;
467     }else {
468         if(val != 0)
469             size = floor(log10(val>0 ? val : -val)) + 1;
470         else
471             size = 1;
472
473         if(size > prec)
474             str = number_to_exponential(val, prec-1);
475         else
476             str = number_to_fixed(val, prec-size);
477         if(!str)
478             return E_OUTOFMEMORY;
479     }
480
481     if(r)
482         *r = jsval_string(str);
483     else
484         jsstr_release(str);
485     return S_OK;
486 }
487
488 static HRESULT Number_valueOf(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
489         jsval_t *r)
490 {
491     NumberInstance *number;
492
493     TRACE("\n");
494
495     if(!(number = number_this(jsthis)))
496         return throw_type_error(ctx, JS_E_NUMBER_EXPECTED, NULL);
497
498     if(r)
499         *r = jsval_number(number->value);
500     return S_OK;
501 }
502
503 static HRESULT Number_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
504         jsval_t *r)
505 {
506     NumberInstance *number = number_from_vdisp(jsthis);
507
508     switch(flags) {
509     case INVOKE_FUNC:
510         return throw_type_error(ctx, JS_E_FUNCTION_EXPECTED, NULL);
511     case DISPATCH_PROPERTYGET:
512         *r = jsval_number(number->value);
513         break;
514
515     default:
516         FIXME("flags %x\n", flags);
517         return E_NOTIMPL;
518     }
519
520     return S_OK;
521 }
522
523 static const builtin_prop_t Number_props[] = {
524     {toExponentialW,         Number_toExponential,         PROPF_METHOD|1},
525     {toFixedW,               Number_toFixed,               PROPF_METHOD},
526     {toLocaleStringW,        Number_toLocaleString,        PROPF_METHOD},
527     {toPrecisionW,           Number_toPrecision,           PROPF_METHOD|1},
528     {toStringW,              Number_toString,              PROPF_METHOD|1},
529     {valueOfW,               Number_valueOf,               PROPF_METHOD}
530 };
531
532 static const builtin_info_t Number_info = {
533     JSCLASS_NUMBER,
534     {NULL, Number_value, 0},
535     sizeof(Number_props)/sizeof(*Number_props),
536     Number_props,
537     NULL,
538     NULL
539 };
540
541 static const builtin_info_t NumberInst_info = {
542     JSCLASS_NUMBER,
543     {NULL, Number_value, 0},
544     0, NULL,
545     NULL,
546     NULL
547 };
548
549 static HRESULT NumberConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
550         jsval_t *r)
551 {
552     double n;
553     HRESULT hres;
554
555     TRACE("\n");
556
557     switch(flags) {
558     case INVOKE_FUNC:
559         if(!argc) {
560             if(r)
561                 *r = jsval_number(0);
562             return S_OK;
563         }
564
565         hres = to_number(ctx, argv[0], &n);
566         if(FAILED(hres))
567             return hres;
568
569         if(r)
570             *r = jsval_number(n);
571         break;
572
573     case DISPATCH_CONSTRUCT: {
574         jsdisp_t *obj;
575
576         if(argc) {
577             hres = to_number(ctx, argv[0], &n);
578             if(FAILED(hres))
579                 return hres;
580         }else {
581             n = 0;
582         }
583
584         hres = create_number(ctx, n, &obj);
585         if(FAILED(hres))
586             return hres;
587
588         *r = jsval_obj(obj);
589         break;
590     }
591     default:
592         FIXME("unimplemented flags %x\n", flags);
593         return E_NOTIMPL;
594     }
595
596     return S_OK;
597 }
598
599 static HRESULT alloc_number(script_ctx_t *ctx, jsdisp_t *object_prototype, NumberInstance **ret)
600 {
601     NumberInstance *number;
602     HRESULT hres;
603
604     number = heap_alloc_zero(sizeof(NumberInstance));
605     if(!number)
606         return E_OUTOFMEMORY;
607
608     if(object_prototype)
609         hres = init_dispex(&number->dispex, ctx, &Number_info, object_prototype);
610     else
611         hres = init_dispex_from_constr(&number->dispex, ctx, &NumberInst_info, ctx->number_constr);
612     if(FAILED(hres)) {
613         heap_free(number);
614         return hres;
615     }
616
617     *ret = number;
618     return S_OK;
619 }
620
621 HRESULT create_number_constr(script_ctx_t *ctx, jsdisp_t *object_prototype, jsdisp_t **ret)
622 {
623     NumberInstance *number;
624     HRESULT hres;
625
626     static const WCHAR NumberW[] = {'N','u','m','b','e','r',0};
627
628     hres = alloc_number(ctx, object_prototype, &number);
629     if(FAILED(hres))
630         return hres;
631
632     number->value = 0;
633     hres = create_builtin_constructor(ctx, NumberConstr_value, NumberW, NULL,
634             PROPF_CONSTR|1, &number->dispex, ret);
635
636     jsdisp_release(&number->dispex);
637     return hres;
638 }
639
640 HRESULT create_number(script_ctx_t *ctx, double value, jsdisp_t **ret)
641 {
642     NumberInstance *number;
643     HRESULT hres;
644
645     hres = alloc_number(ctx, NULL, &number);
646     if(FAILED(hres))
647         return hres;
648
649     number->value = value;
650
651     *ret = &number->dispex;
652     return S_OK;
653 }