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