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] = ''+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] = ''+l;
87 }else if(buf[0]=='' && buf[1]>='1' && buf[1]<='9') {
88 (*dec_point)--;
89 memmove(buf, buf+1, size-2);
90 buf[size-2] = '';
91 }
92 }
93
94 static inline void number_to_fixed(double val, int prec, BSTR *out)
95 {
96 WCHAR buf[NUMBER_DTOA_SIZE];
97 int dec_point, size, buf_size, buf_pos;
98 BOOL neg = FALSE;
99 BSTR str;
100
101 if(val < 0) {
102 neg = TRUE;
103 val = -val;
104 }
105
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;
112
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;
124
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++] = '';
134 }
135 for(; dec_point>0; dec_point--)
136 str[size++] = '';
137 if(prec) {
138 str[size++] = '.';
139
140 for(; dec_point<0 && prec; dec_point++, prec--)
141 str[size++] = '';
142 for(; buf_pos<buf_size-1 && prec; prec--)
143 str[size++] = buf[buf_pos++];
144 for(; prec; prec--) {
145 str[size++] = '';
146 }
147 }
148 str[size++] = 0;
149
150 *out = str;
151 }
152
153 static inline void number_to_exponential(double val, int prec, BSTR *out)
154 {
155 WCHAR buf[NUMBER_DTOA_SIZE], *pbuf;
156 int dec_point, size, buf_size, exp_size = 1;
157 BOOL neg = FALSE;
158 BSTR str;
159
160 if(val < 0) {
161 neg = TRUE;
162 val = -val;
163 }
164
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]==''; buf_size--)
172 buf[buf_size-1] = 0;
173
174 size = 10;
175 while(dec_point>=size || dec_point<=-size) {
176 size *= 10;
177 exp_size++;
178 }
179
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);
189
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++] = '';
201 }
202 str[size++] = 'e';
203 if(dec_point >= 0) {
204 str[size++] = '+';
205 }else {
206 str[size++] = '-';
207 dec_point = -dec_point;
208 }
209 size += exp_size;
210 do {
211 str[--size] = ''+dec_point%10;
212 dec_point /= 10;
213 }while(dec_point>0);
214 size += exp_size;
215 str[size] = 0;
216
217 *out = str;
218 }
219
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)
223 {
224 NumberInstance *number;
225 INT radix = 10;
226 DOUBLE val;
227 BSTR str;
228 HRESULT hres;
229
230 TRACE("\n");
231
232 if(!(number = number_this(jsthis)))
233 return throw_type_error(ctx, ei, JS_E_NUMBER_EXPECTED, NULL);
234
235 if(arg_cnt(dp)) {
236 hres = to_int32(ctx, get_arg(dp, 0), ei, &radix);
237 if(FAILED(hres))
238 return hres;
239
240 if(radix<2 || radix>36)
241 return throw_type_error(ctx, ei, JS_E_INVALIDARG, NULL);
242 }
243
244 val = number->value;
245
246 if(radix==10 || isnan(val) || isinf(val)) {
247 VARIANT v;
248
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;
258
259 if(val<0) {
260 val = -val;
261 buf[idx++] = '-';
262 }
263
264 while(1) {
265 integ = floor(val);
266 frac = val-integ;
267
268 if(integ == 0)
269 buf[idx++] = '';
270 while(integ>=1 && idx<NUMBER_TOSTRING_BUF_SIZE) {
271 buf[idx] = fmod(integ, radix);
272 if(buf[idx]<10) buf[idx] += '';
273 else buf[idx] += 'a'-10;
274 integ /= radix;
275 idx++;
276 }
277
278 if(idx<NUMBER_TOSTRING_BUF_SIZE) {
279 INT beg = buf[0]=='-'?1:0;
280 INT end = idx-1;
281 WCHAR wch;
282
283 while(end > beg) {
284 wch = buf[beg];
285 buf[beg++] = buf[end];
286 buf[end--] = wch;
287 }
288 }
289
290 if(idx != NUMBER_TOSTRING_BUF_SIZE) buf[idx++] = '.';
291
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] += '';
297 else buf[idx] += 'a'-10;
298 idx++;
299 }
300
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;
307 }
308
309 break;
310 }
311
312 while(buf[idx-1] == '') idx--;
313 if(buf[idx-1] == '.') idx--;
314
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;
321
322 if(log_radix<0) {
323 log_radix = -log_radix;
324 ch = '-';
325 }
326 else ch = '+';
327 sprintfW(&buf[idx], formatW, ch, (int)log_radix);
328 }
329 }
330 else buf[idx] = '\0';
331
332 str = SysAllocString(buf);
333 if(!str)
334 return E_OUTOFMEMORY;
335 }
336
337 if(retv) {
338 V_VT(retv) = VT_BSTR;
339 V_BSTR(retv) = str;
340 }else {
341 SysFreeString(str);
342 }
343 return S_OK;
344 }
345
346 static HRESULT Number_toLocaleString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
347 VARIANT *retv, jsexcept_t *ei)
348 {
349 FIXME("\n");
350 return E_NOTIMPL;
351 }
352
353 static HRESULT Number_toFixed(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
354 VARIANT *retv, jsexcept_t *ei)
355 {
356 NumberInstance *number;
357 DOUBLE val;
358 INT prec = 0;
359 BSTR str;
360 HRESULT hres;
361
362 TRACE("\n");
363
364 if(!(number = number_this(jsthis)))
365 return throw_type_error(ctx, ei, JS_E_NUMBER_EXPECTED, NULL);
366
367 if(arg_cnt(dp)) {
368 hres = to_int32(ctx, get_arg(dp, 0), ei, &prec);
369 if(FAILED(hres))
370 return hres;
371
372 if(prec<0 || prec>20)
373 return throw_range_error(ctx, ei, JS_E_FRACTION_DIGITS_OUT_OF_RANGE, NULL);
374 }
375
376 val = number->value;
377 if(isinf(val) || isnan(val)) {
378 VARIANT v;
379
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);
386 }
387
388 if(retv) {
389 V_VT(retv) = VT_BSTR;
390 V_BSTR(retv) = str;
391 }else {
392 SysFreeString(str);
393 }
394 return S_OK;
395 }
396
397 static HRESULT Number_toExponential(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
398 VARIANT *retv, jsexcept_t *ei)
399 {
400 NumberInstance *number;
401 DOUBLE val;
402 INT prec = 0;
403 BSTR str;
404 HRESULT hres;
405
406 TRACE("\n");
407
408 if(!(number = number_this(jsthis)))
409 return throw_type_error(ctx, ei, JS_E_NUMBER_EXPECTED, NULL);
410
411 if(arg_cnt(dp)) {
412 hres = to_int32(ctx, get_arg(dp, 0), ei, &prec);
413 if(FAILED(hres))
414 return hres;
415
416 if(prec<0 || prec>20)
417 return throw_range_error(ctx, ei, JS_E_FRACTION_DIGITS_OUT_OF_RANGE, NULL);
418 }
419
420 val = number->value;
421 if(isinf(val) || isnan(val)) {
422 VARIANT v;
423
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);
432 }
433
434 if(retv) {
435 V_VT(retv) = VT_BSTR;
436 V_BSTR(retv) = str;
437 }else {
438 SysFreeString(str);
439 }
440 return S_OK;
441 }
442
443 static HRESULT Number_toPrecision(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
444 VARIANT *retv, jsexcept_t *ei)
445 {
446 NumberInstance *number;
447 INT prec = 0, size;
448 DOUBLE val;
449 BSTR str;
450 HRESULT hres;
451
452 if(!(number = number_this(jsthis)))
453 return throw_type_error(ctx, ei, JS_E_NUMBER_EXPECTED, NULL);
454
455 if(arg_cnt(dp)) {
456 hres = to_int32(ctx, get_arg(dp, 0), ei, &prec);
457 if(FAILED(hres))
458 return hres;
459
460 if(prec<1 || prec>21)
461 return throw_range_error(ctx, ei, JS_E_PRECISION_OUT_OF_RANGE, NULL);
462 }
463
464 val = number->value;
465 if(isinf(val) || isnan(val) || !prec) {
466 VARIANT v;
467
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;
477
478 if(size > prec)
479 number_to_exponential(val, prec-1, &str);
480 else
481 number_to_fixed(val, prec-size, &str);
482 }
483
484 if(retv) {
485 V_VT(retv) = VT_BSTR;
486 V_BSTR(retv) = str;
487 }else {
488 SysFreeString(str);
489 }
490 return S_OK;
491 }
492
493 static HRESULT Number_valueOf(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
494 VARIANT *retv, jsexcept_t *ei)
495 {
496 NumberInstance *number;
497
498 TRACE("\n");
499
500 if(!(number = number_this(jsthis)))
501 return throw_type_error(ctx, ei, JS_E_NUMBER_EXPECTED, NULL);
502
503 if(retv)
504 num_set_val(retv, number->value);
505 return S_OK;
506 }
507
508 static HRESULT Number_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
509 VARIANT *retv, jsexcept_t *ei)
510 {
511 NumberInstance *number = number_from_vdisp(jsthis);
512
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;
519
520 default:
521 FIXME("flags %x\n", flags);
522 return E_NOTIMPL;
523 }
524
525 return S_OK;
526 }
527
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}
535 };
536
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
544 };
545
546 static HRESULT NumberConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
547 VARIANT *retv, jsexcept_t *ei)
548 {
549 double n;
550 HRESULT hres;
551
552 TRACE("\n");
553
554 switch(flags) {
555 case INVOKE_FUNC:
556 if(!arg_cnt(dp)) {
557 if(retv) {
558 V_VT(retv) = VT_I4;
559 V_I4(retv) = 0;
560 }
561 return S_OK;
562 }
563
564 hres = to_number(ctx, get_arg(dp, 0), ei, &n);
565 if(FAILED(hres))
566 return hres;
567
568 if(retv)
569 num_set_val(retv, n);
570 break;
571
572 case DISPATCH_CONSTRUCT: {
573 jsdisp_t *obj;
574
575 if(arg_cnt(dp)) {
576 hres = to_number(ctx, get_arg(dp, 0), ei, &n);
577 if(FAILED(hres))
578 return hres;
579 }else {
580 n = 0;
581 }
582
583 hres = create_number(ctx, n, &obj);
584 if(FAILED(hres))
585 return hres;
586
587 var_set_jsdisp(retv, obj);
588 break;
589 }
590 default:
591 FIXME("unimplemented flags %x\n", flags);
592 return E_NOTIMPL;
593 }
594
595 return S_OK;
596 }
597
598 static HRESULT alloc_number(script_ctx_t *ctx, jsdisp_t *object_prototype, NumberInstance **ret)
599 {
600 NumberInstance *number;
601 HRESULT hres;
602
603 number = heap_alloc_zero(sizeof(NumberInstance));
604 if(!number)
605 return E_OUTOFMEMORY;
606
607 if(object_prototype)
608 hres = init_dispex(&number->dispex, ctx, &Number_info, object_prototype);
609 else
610 hres = init_dispex_from_constr(&number->dispex, ctx, &Number_info, ctx->number_constr);
611 if(FAILED(hres))
612 return hres;
613
614 *ret = number;
615 return S_OK;
616 }
617
618 HRESULT create_number_constr(script_ctx_t *ctx, jsdisp_t *object_prototype, jsdisp_t **ret)
619 {
620 NumberInstance *number;
621 HRESULT hres;
622
623 static const WCHAR NumberW[] = {'N','u','m','b','e','r',0};
624
625 hres = alloc_number(ctx, object_prototype, &number);
626 if(FAILED(hres))
627 return hres;
628
629 number->value = 0;
630 hres = create_builtin_function(ctx, NumberConstr_value, NumberW, NULL,
631 PROPF_CONSTR|1, &number->dispex, ret);
632
633 jsdisp_release(&number->dispex);
634 return hres;
635 }
636
637 HRESULT create_number(script_ctx_t *ctx, double value, jsdisp_t **ret)
638 {
639 NumberInstance *number;
640 HRESULT hres;
641
642 hres = alloc_number(ctx, NULL, &number);
643 if(FAILED(hres))
644 return hres;
645
646 number->value = value;
647
648 *ret = &number->dispex;
649 return S_OK;
650 }
651
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.