1
2 /*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) NGINX, Inc.
5 */
6
7
8 #include <njs_main.h>
9
10
11 /*
12 * 2^53 - 1 is the largest integer n such that n and n + 1
13 * as well as -n and -n - 1 are all exactly representable
14 * in the IEEE-754 format.
15 */
16 #define NJS_MAX_SAFE_INTEGER ((1LL << 53) - 1)
17
18
19 static njs_int_t njs_number_to_string_radix(njs_vm_t *vm, njs_value_t *string,
20 double number, uint32_t radix);
21
22
23 double
njs_key_to_index(const njs_value_t * value)24 njs_key_to_index(const njs_value_t *value)
25 {
26 njs_array_t *array;
27
28 if (njs_fast_path(njs_is_numeric(value))) {
29 return njs_number(value);
30
31 } else if (njs_is_string(value)) {
32 return njs_string_to_index(value);
33
34 } else if (njs_is_array(value)) {
35
36 array = njs_array(value);
37
38 if (njs_lvlhsh_is_empty(&array->object.hash)) {
39
40 if (array->length == 0) {
41 /* An empty array value is zero. */
42 return 0;
43 }
44
45 if (array->length == 1 && njs_is_valid(&array->start[0])) {
46 /* A single value array is the zeroth array value. */
47 return njs_key_to_index(&array->start[0]);
48 }
49 }
50 }
51
52 return NAN;
53 }
54
55
56 double
njs_number_dec_parse(const u_char ** start,const u_char * end,njs_bool_t literal)57 njs_number_dec_parse(const u_char **start, const u_char *end,
58 njs_bool_t literal)
59 {
60 return njs_strtod(start, end, literal);
61 }
62
63
64 uint64_t
njs_number_oct_parse(const u_char ** start,const u_char * end)65 njs_number_oct_parse(const u_char **start, const u_char *end)
66 {
67 u_char c;
68 uint64_t num;
69 const u_char *p, *_;
70
71 p = *start;
72
73 num = 0;
74 _ = p - 1;
75
76 for (; p < end; p++) {
77 /* Values less than '0' become >= 208. */
78 c = *p - '0';
79
80 if (njs_slow_path(c > 7)) {
81 if (*p == '_' && (p - _) > 1) {
82 _ = p;
83 continue;
84 }
85
86 break;
87 }
88
89 num = num * 8 + c;
90 }
91
92 *start = p;
93
94 return num;
95 }
96
97
98 uint64_t
njs_number_bin_parse(const u_char ** start,const u_char * end)99 njs_number_bin_parse(const u_char **start, const u_char *end)
100 {
101 u_char c;
102 uint64_t num;
103 const u_char *p, *_;
104
105 p = *start;
106
107 num = 0;
108 _ = p - 1;
109
110 for (; p < end; p++) {
111 /* Values less than '0' become >= 208. */
112 c = *p - '0';
113
114 if (njs_slow_path(c > 1)) {
115 if (*p == '_' && (p - _) > 1) {
116 _ = p;
117 continue;
118 }
119
120 break;
121 }
122
123 num = num * 2 + c;
124 }
125
126 *start = p;
127
128 return num;
129 }
130
131
132 uint64_t
njs_number_hex_parse(const u_char ** start,const u_char * end,njs_bool_t literal)133 njs_number_hex_parse(const u_char **start, const u_char *end,
134 njs_bool_t literal)
135 {
136 uint64_t num;
137 njs_int_t n;
138 const u_char *p, *_;
139
140 p = *start;
141
142 num = 0;
143 _ = p - 1;
144
145 for (; p < end; p++) {
146 n = njs_char_to_hex(*p);
147
148 if (njs_slow_path(n < 0)) {
149 if (literal && *p == '_' && (p - _) > 1) {
150 _ = p;
151 continue;
152 }
153
154 break;
155 }
156
157 num = num * 16 + n;
158 }
159
160 *start = p;
161
162 return num;
163 }
164
165
166 int64_t
njs_number_radix_parse(const u_char ** start,const u_char * end,uint8_t radix)167 njs_number_radix_parse(const u_char **start, const u_char *end, uint8_t radix)
168 {
169 uint8_t d;
170 int64_t num;
171 uint64_t n;
172 const u_char *p;
173
174 static const int8_t digits[256]
175 njs_aligned(32) =
176 {
177 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
178 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
179 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
180 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
181 -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
182 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
183 -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
184 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
185 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
186 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
187 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
188 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
189 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
190 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
191 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
192 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
193 };
194
195 num = -1;
196 n = 0;
197
198 for (p = *start; p < end; p++) {
199 d = digits[*p];
200
201 if (njs_slow_path(d >= radix)) {
202 break;
203 }
204
205 n = (n * radix) + d;
206 num = n;
207 }
208
209 *start = p;
210
211 return num;
212 }
213
214
215 njs_int_t
njs_number_to_string(njs_vm_t * vm,njs_value_t * string,const njs_value_t * number)216 njs_number_to_string(njs_vm_t *vm, njs_value_t *string,
217 const njs_value_t *number)
218 {
219 double num;
220 size_t size;
221 const njs_value_t *value;
222 u_char buf[128];
223
224 num = njs_number(number);
225
226 if (isnan(num)) {
227 value = &njs_string_nan;
228
229 } else if (isinf(num)) {
230
231 if (num < 0) {
232 value = &njs_string_minus_infinity;
233
234 } else {
235 value = &njs_string_plus_infinity;
236 }
237
238 } else {
239 size = njs_dtoa(num, (char *) buf);
240
241 return njs_string_new(vm, string, buf, size, size);
242 }
243
244 *string = *value;
245
246 return NJS_OK;
247 }
248
249
250 njs_int_t
njs_int64_to_string(njs_vm_t * vm,njs_value_t * value,int64_t i64)251 njs_int64_to_string(njs_vm_t *vm, njs_value_t *value, int64_t i64)
252 {
253 size_t size;
254 u_char *dst, *p;
255 u_char buf[128];
256
257 if (njs_fast_path(i64 >= 0 && i64 < 0x3fffffffffffLL)) {
258 /* Fits to short_string. */
259 dst = njs_string_short_start(value);
260
261 p = njs_sprintf(dst, dst + NJS_STRING_SHORT, "%L", i64);
262
263 njs_string_short_set(value, p - dst, p - dst);
264
265 return NJS_OK;
266 }
267
268 size = njs_dtoa(i64, (char *) buf);
269
270 return njs_string_new(vm, value, buf, size, size);
271 }
272
273
274 njs_int_t
njs_number_to_chain(njs_vm_t * vm,njs_chb_t * chain,double num)275 njs_number_to_chain(njs_vm_t *vm, njs_chb_t *chain, double num)
276 {
277 size_t size;
278 u_char *p;
279
280 if (isnan(num)) {
281 njs_chb_append_literal(chain, "NaN");
282 return njs_length("NaN");
283
284 }
285
286 if (isinf(num)) {
287 if (num < 0) {
288 njs_chb_append_literal(chain, "-Infinity");
289 return njs_length("-Infinity");
290
291 } else {
292 njs_chb_append_literal(chain, "Infinity");
293 return njs_length("Infinity");
294 }
295 }
296
297 p = njs_chb_reserve(chain, 64);
298 if (njs_slow_path(p == NULL)) {
299 return NJS_ERROR;
300 }
301
302 size = njs_dtoa(num, (char *) p);
303
304 njs_chb_written(chain, size);
305
306 return size;
307 }
308
309
310 static njs_int_t
njs_number_constructor(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)311 njs_number_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
312 njs_index_t unused)
313 {
314 njs_int_t ret;
315 njs_value_t *value;
316 njs_object_value_t *object;
317
318 if (nargs == 1) {
319 value = njs_value_arg(&njs_value_zero);
320
321 } else {
322 value = &args[1];
323
324 if (njs_slow_path(!njs_is_number(value))) {
325 ret = njs_value_to_numeric(vm, value, value);
326 if (njs_slow_path(ret != NJS_OK)) {
327 return NJS_ERROR;
328 }
329 }
330 }
331
332 if (vm->top_frame->ctor) {
333 object = njs_object_value_alloc(vm, NJS_OBJ_TYPE_NUMBER, 0, value);
334 if (njs_slow_path(object == NULL)) {
335 return NJS_ERROR;
336 }
337
338 njs_set_object_value(&vm->retval, object);
339
340 } else {
341 njs_set_number(&vm->retval, njs_number(value));
342 }
343
344 return NJS_OK;
345 }
346
347
348 static njs_int_t
njs_number_is_integer(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)349 njs_number_is_integer(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
350 njs_index_t unused)
351 {
352 double num;
353 const njs_value_t *value;
354
355 value = &njs_value_false;
356
357 if (nargs > 1 && njs_is_number(&args[1])) {
358 num = njs_number(&args[1]);
359
360 if (num == trunc(num) && !isinf(num)) {
361 value = &njs_value_true;
362 }
363 }
364
365 vm->retval = *value;
366
367 return NJS_OK;
368 }
369
370
371
372 static njs_int_t
njs_number_is_safe_integer(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)373 njs_number_is_safe_integer(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
374 njs_index_t unused)
375 {
376 double num;
377 const njs_value_t *value;
378
379 value = &njs_value_false;
380
381 if (nargs > 1 && njs_is_number(&args[1])) {
382 num = njs_number(&args[1]);
383
384 if (num == (int64_t) num && fabs(num) <= NJS_MAX_SAFE_INTEGER) {
385 value = &njs_value_true;
386 }
387 }
388
389 vm->retval = *value;
390
391 return NJS_OK;
392 }
393
394
395 static njs_int_t
njs_number_is_nan(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)396 njs_number_is_nan(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
397 njs_index_t unused)
398 {
399 const njs_value_t *value;
400
401 value = &njs_value_false;
402
403 if (nargs > 1
404 && njs_is_number(&args[1])
405 && isnan(njs_number(&args[1])))
406 {
407 value = &njs_value_true;
408 }
409
410 vm->retval = *value;
411
412 return NJS_OK;
413 }
414
415
416 static njs_int_t
njs_number_is_finite(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)417 njs_number_is_finite(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
418 njs_index_t unused)
419 {
420 double num;
421 const njs_value_t *value;
422
423 value = &njs_value_false;
424
425 if (nargs > 1 && njs_is_number(&args[1])) {
426 num = njs_number(&args[1]);
427
428 if (!isnan(num) && !isinf(num)) {
429 value = &njs_value_true;
430 }
431 }
432
433 vm->retval = *value;
434
435 return NJS_OK;
436 }
437
438
439 static const njs_object_prop_t njs_number_constructor_properties[] =
440 {
441 {
442 .type = NJS_PROPERTY,
443 .name = njs_string("name"),
444 .value = njs_string("Number"),
445 .configurable = 1,
446 },
447
448 {
449 .type = NJS_PROPERTY,
450 .name = njs_string("length"),
451 .value = njs_value(NJS_NUMBER, 1, 1.0),
452 .configurable = 1,
453 },
454
455 {
456 .type = NJS_PROPERTY_HANDLER,
457 .name = njs_string("prototype"),
458 .value = njs_prop_handler(njs_object_prototype_create),
459 },
460
461 {
462 .type = NJS_PROPERTY,
463 .name = njs_string("EPSILON"),
464 .value = njs_value(NJS_NUMBER, 1, DBL_EPSILON),
465 },
466
467 {
468 .type = NJS_PROPERTY,
469 .name = njs_long_string("MAX_SAFE_INTEGER"),
470 .value = njs_value(NJS_NUMBER, 1, NJS_MAX_SAFE_INTEGER),
471 },
472
473 {
474 .type = NJS_PROPERTY,
475 .name = njs_long_string("MIN_SAFE_INTEGER"),
476 .value = njs_value(NJS_NUMBER, 1, -NJS_MAX_SAFE_INTEGER),
477 },
478
479 {
480 .type = NJS_PROPERTY,
481 .name = njs_string("MAX_VALUE"),
482 .value = njs_value(NJS_NUMBER, 1, DBL_MAX),
483 },
484
485 {
486 .type = NJS_PROPERTY,
487 .name = njs_string("MIN_VALUE"),
488 .value = njs_value(NJS_NUMBER, 1, DBL_MIN),
489 },
490
491 {
492 .type = NJS_PROPERTY,
493 .name = njs_string("NaN"),
494 .value = njs_value(NJS_NUMBER, 0, NAN),
495 },
496
497 {
498 .type = NJS_PROPERTY,
499 .name = njs_long_string("POSITIVE_INFINITY"),
500 .value = njs_value(NJS_NUMBER, 1, INFINITY),
501 },
502
503 {
504 .type = NJS_PROPERTY,
505 .name = njs_long_string("NEGATIVE_INFINITY"),
506 .value = njs_value(NJS_NUMBER, 1, -INFINITY),
507 },
508
509 {
510 .type = NJS_PROPERTY,
511 .name = njs_string("isFinite"),
512 .value = njs_native_function(njs_number_is_finite, 1),
513 .writable = 1,
514 .configurable = 1,
515 },
516
517 {
518 .type = NJS_PROPERTY,
519 .name = njs_string("isInteger"),
520 .value = njs_native_function(njs_number_is_integer, 1),
521 .writable = 1,
522 .configurable = 1,
523 },
524
525 {
526 .type = NJS_PROPERTY,
527 .name = njs_string("isSafeInteger"),
528 .value = njs_native_function(njs_number_is_safe_integer, 1),
529 .writable = 1,
530 .configurable = 1,
531 },
532
533 {
534 .type = NJS_PROPERTY,
535 .name = njs_string("isNaN"),
536 .value = njs_native_function(njs_number_is_nan, 1),
537 .writable = 1,
538 .configurable = 1,
539 },
540
541 {
542 .type = NJS_PROPERTY,
543 .name = njs_string("parseFloat"),
544 .value = njs_native_function(njs_number_parse_float, 1),
545 .writable = 1,
546 .configurable = 1,
547 },
548
549 {
550 .type = NJS_PROPERTY,
551 .name = njs_string("parseInt"),
552 .value = njs_native_function(njs_number_parse_int, 2),
553 .writable = 1,
554 .configurable = 1,
555 },
556 };
557
558
559 const njs_object_init_t njs_number_constructor_init = {
560 njs_number_constructor_properties,
561 njs_nitems(njs_number_constructor_properties),
562 };
563
564
565 static njs_int_t
njs_number_prototype_value_of(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)566 njs_number_prototype_value_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
567 njs_index_t unused)
568 {
569 njs_value_t *value;
570
571 value = &args[0];
572
573 if (value->type != NJS_NUMBER) {
574
575 if (njs_is_object_number(value)) {
576 value = njs_object_value(value);
577
578 } else {
579 njs_type_error(vm, "unexpected value type:%s",
580 njs_type_string(value->type));
581 return NJS_ERROR;
582 }
583 }
584
585 vm->retval = *value;
586
587 return NJS_OK;
588 }
589
590
591 static njs_int_t
njs_number_prototype_to_string(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)592 njs_number_prototype_to_string(njs_vm_t *vm, njs_value_t *args,
593 njs_uint_t nargs, njs_index_t unused)
594 {
595 double number;
596 int32_t radix;
597 njs_int_t ret;
598 njs_value_t *value;
599
600 value = &args[0];
601
602 if (value->type != NJS_NUMBER) {
603
604 if (njs_is_object_number(value)) {
605 value = njs_object_value(value);
606
607 } else {
608 njs_type_error(vm, "unexpected value type:%s",
609 njs_type_string(value->type));
610 return NJS_ERROR;
611 }
612 }
613
614 if (nargs > 1) {
615 ret = njs_value_to_int32(vm, &args[1], &radix);
616 if (njs_slow_path(ret != NJS_OK)) {
617 return ret;
618 }
619
620 if (radix < 2 || radix > 36 || radix != (int) radix) {
621 njs_range_error(vm, NULL);
622 return NJS_ERROR;
623 }
624
625 number = njs_number(value);
626
627 if (radix != 10 && !isnan(number) && !isinf(number) && number != 0) {
628 return njs_number_to_string_radix(vm, &vm->retval, number, radix);
629 }
630 }
631
632 return njs_number_to_string(vm, &vm->retval, value);
633 }
634
635
636 static njs_int_t
njs_number_prototype_to_fixed(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)637 njs_number_prototype_to_fixed(njs_vm_t *vm, njs_value_t *args,
638 njs_uint_t nargs, njs_index_t unused)
639 {
640 u_char *p;
641 int64_t frac;
642 double number;
643 size_t length, size;
644 njs_int_t ret, point, prefix, postfix;
645 njs_value_t *value;
646 u_char buf[128], buf2[128];
647
648 /* 128 > 100 + 21 + njs_length(".-\0"). */
649
650 value = &args[0];
651
652 if (value->type != NJS_NUMBER) {
653 if (njs_is_object_number(value)) {
654 value = njs_object_value(value);
655
656 } else {
657 njs_type_error(vm, "unexpected value type:%s",
658 njs_type_string(value->type));
659 return NJS_ERROR;
660 }
661 }
662
663 ret = njs_value_to_integer(vm, njs_arg(args, nargs, 1), &frac);
664 if (njs_slow_path(ret != NJS_OK)) {
665 return ret;
666 }
667
668 if (njs_slow_path(frac < 0 || frac > 100)) {
669 njs_range_error(vm, "digits argument must be between 0 and 100");
670 return NJS_ERROR;
671 }
672
673 number = njs_number(value);
674
675 if (njs_slow_path(isnan(number) || fabs(number) >= 1e21)) {
676 return njs_number_to_string(vm, &vm->retval, value);
677 }
678
679 point = 0;
680 length = njs_fixed_dtoa(number, (njs_int_t) frac, (char *) buf, &point);
681
682 prefix = 0;
683 postfix = 0;
684
685 if (point <= 0) {
686 prefix = -point + 1;
687 point = 1;
688 }
689
690 if (prefix + (njs_int_t) length < point + frac) {
691 postfix = point + frac - length - prefix;
692 }
693
694 size = prefix + length + postfix + !!(number < 0);
695
696 if (frac > 0) {
697 size += njs_length(".");
698 }
699
700 p = buf2;
701
702 while (--prefix >= 0) {
703 *p++ = '0';
704 }
705
706 if (length != 0) {
707 p = njs_cpymem(p, buf, length);
708 }
709
710 while (--postfix >= 0) {
711 *p++ = '0';
712 }
713
714 p = njs_string_alloc(vm, &vm->retval, size, size);
715 if (njs_slow_path(p == NULL)) {
716 return NJS_ERROR;
717 }
718
719 if (number < 0) {
720 *p++ = '-';
721 }
722
723 p = njs_cpymem(p, buf2, point);
724
725 if (frac > 0) {
726 *p++ = '.';
727
728 memcpy(p, &buf2[point], frac);
729 }
730
731 return NJS_OK;
732 }
733
734
735 static njs_int_t
njs_number_prototype_to_precision(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)736 njs_number_prototype_to_precision(njs_vm_t *vm, njs_value_t *args,
737 njs_uint_t nargs, njs_index_t unused)
738 {
739 double number;
740 size_t size;
741 int64_t precision;
742 njs_int_t ret;
743 njs_value_t *value;
744 u_char buf[128];
745
746 /* 128 > 100 + 21 + njs_length(".-\0"). */
747
748 value = &args[0];
749
750 if (value->type != NJS_NUMBER) {
751 if (njs_is_object_number(value)) {
752 value = njs_object_value(value);
753
754 } else {
755 njs_type_error(vm, "unexpected value type:%s",
756 njs_type_string(value->type));
757 return NJS_ERROR;
758 }
759 }
760
761 if (njs_is_undefined(njs_arg(args, nargs, 1))) {
762 return njs_number_to_string(vm, &vm->retval, value);
763 }
764
765 ret = njs_value_to_integer(vm, njs_argument(args, 1), &precision);
766 if (njs_slow_path(ret != NJS_OK)) {
767 return ret;
768 }
769
770 number = njs_number(value);
771
772 if (njs_slow_path(isnan(number) || isinf(number))) {
773 return njs_number_to_string(vm, &vm->retval, value);
774 }
775
776 if (njs_slow_path(precision < 1 || precision > 100)) {
777 njs_range_error(vm, "precision argument must be between 1 and 100");
778 return NJS_ERROR;
779 }
780
781 size = njs_dtoa_precision(number, (char *) buf, (size_t) precision);
782
783 return njs_string_new(vm, &vm->retval, buf, size, size);
784 }
785
786
787 static njs_int_t
njs_number_prototype_to_exponential(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)788 njs_number_prototype_to_exponential(njs_vm_t *vm, njs_value_t *args,
789 njs_uint_t nargs, njs_index_t unused)
790 {
791 double number;
792 size_t size;
793 int64_t frac;
794 njs_int_t ret;
795 njs_value_t *value, *value_frac;
796 u_char buf[128];
797
798 value = &args[0];
799
800 if (value->type != NJS_NUMBER) {
801 if (njs_is_object_number(value)) {
802 value = njs_object_value(value);
803
804 } else {
805 njs_type_error(vm, "unexpected value type:%s",
806 njs_type_string(value->type));
807 return NJS_ERROR;
808 }
809 }
810
811 value_frac = njs_arg(args, nargs, 1);
812
813 ret = njs_value_to_integer(vm, value_frac, &frac);
814 if (njs_slow_path(ret != NJS_OK)) {
815 return ret;
816 }
817
818 number = njs_number(value);
819
820 if (njs_slow_path(isnan(number) || isinf(number))) {
821 return njs_number_to_string(vm, &vm->retval, value);
822 }
823
824 if (njs_is_defined(value_frac)) {
825 if (njs_slow_path(frac < 0 || frac > 100)) {
826 njs_range_error(vm, "digits argument must be between 0 and 100");
827 return NJS_ERROR;
828 }
829
830 } else {
831 frac = -1;
832 }
833
834 size = njs_dtoa_exponential(number, (char *) buf, (njs_int_t) frac);
835
836 return njs_string_new(vm, &vm->retval, buf, size, size);
837 }
838
839
840 /*
841 * The radix equal to 2 produces the longest value for a number.
842 */
843
844 #define NJS_STRING_RADIX_INTERGRAL_LEN (1 + 1024)
845 #define NJS_STRING_RADIX_FRACTION_LEN (1 + 1075)
846 #define NJS_STRING_RADIX_LEN \
847 (NJS_STRING_RADIX_INTERGRAL_LEN + NJS_STRING_RADIX_FRACTION_LEN)
848
849
850 njs_inline double
njs_number_next_double(double n)851 njs_number_next_double(double n)
852 {
853 njs_diyfp_t v;
854
855 v = njs_d2diyfp(n);
856
857 if (signbit(n)) {
858 if (v.significand == 0) {
859 return 0.0;
860 }
861
862 v.significand--;
863
864 } else {
865 v.significand++;
866 }
867
868 return njs_diyfp2d(v);
869 }
870
871
872 static njs_int_t
njs_number_to_string_radix(njs_vm_t * vm,njs_value_t * string,double number,uint32_t radix)873 njs_number_to_string_radix(njs_vm_t *vm, njs_value_t *string,
874 double number, uint32_t radix)
875 {
876 int digit;
877 char ch;
878 double n, remainder, integer, fraction, delta;
879 u_char *p, *end;
880 uint32_t size;
881 u_char buf[NJS_STRING_RADIX_LEN];
882
883 static const char *digits = "0123456789abcdefghijklmnopqrstuvwxyz";
884
885 p = buf + NJS_STRING_RADIX_INTERGRAL_LEN;
886 end = p;
887
888 n = number;
889
890 if (n < 0) {
891 n = -n;
892 }
893
894 integer = floor(n);
895 fraction = n - integer;
896
897 delta = 0.5 * (njs_number_next_double(n) - n);
898 delta = njs_max(njs_number_next_double(0.0), delta);
899
900 if (fraction >= delta && delta != 0) {
901 *p++ = '.';
902
903 do {
904 fraction *= radix;
905 delta *= radix;
906
907 digit = (int) fraction;
908 *p++ = digits[digit];
909
910 fraction -= digit;
911
912 if ((fraction > 0.5 || (fraction == 0.5 && (digit & 1)))
913 && (fraction + delta > 1))
914 {
915 while (p-- != buf) {
916 if (p == buf + NJS_STRING_RADIX_INTERGRAL_LEN) {
917 integer += 1;
918 break;
919 }
920
921 ch = *p;
922 digit = (ch > '9') ? ch - 'a' + 10 : ch - '0';
923
924 if ((uint32_t) (digit + 1) < radix) {
925 *p++ = digits[digit + 1];
926 break;
927 }
928 }
929
930 break;
931 }
932
933 } while (fraction >= delta);
934
935 end = p;
936 }
937
938 p = buf + NJS_STRING_RADIX_INTERGRAL_LEN;
939
940 while (njs_d2diyfp(integer / radix).exp > 0) {
941 integer /= radix;
942 *(--p) = '0';
943 }
944
945 do {
946 remainder = fmod(integer, radix);
947 *(--p) = digits[(int) remainder];
948 integer = (integer - remainder) / radix;
949
950 } while (integer > 0);
951
952 if (number < 0) {
953 *(--p) = '-';
954 }
955
956 size = (uint32_t) (end - p);
957
958 return njs_string_new(vm, string, p, size, size);
959 }
960
961
962 static const njs_object_prop_t njs_number_prototype_properties[] =
963 {
964 {
965 .type = NJS_PROPERTY_HANDLER,
966 .name = njs_string("__proto__"),
967 .value = njs_prop_handler(njs_primitive_prototype_get_proto),
968 .writable = 1,
969 .configurable = 1,
970 },
971
972 {
973 .type = NJS_PROPERTY_HANDLER,
974 .name = njs_string("constructor"),
975 .value = njs_prop_handler(njs_object_prototype_create_constructor),
976 .writable = 1,
977 .configurable = 1,
978 },
979
980 {
981 .type = NJS_PROPERTY,
982 .name = njs_string("valueOf"),
983 .value = njs_native_function(njs_number_prototype_value_of, 0),
984 .writable = 1,
985 .configurable = 1,
986 },
987
988 {
989 .type = NJS_PROPERTY,
990 .name = njs_string("toString"),
991 .value = njs_native_function(njs_number_prototype_to_string, 1),
992 .writable = 1,
993 .configurable = 1,
994 },
995
996 {
997 .type = NJS_PROPERTY,
998 .name = njs_string("toFixed"),
999 .value = njs_native_function(njs_number_prototype_to_fixed, 1),
1000 .writable = 1,
1001 .configurable = 1,
1002 },
1003
1004 {
1005 .type = NJS_PROPERTY,
1006 .name = njs_string("toPrecision"),
1007 .value = njs_native_function(njs_number_prototype_to_precision, 1),
1008 .writable = 1,
1009 .configurable = 1,
1010 },
1011
1012 {
1013 .type = NJS_PROPERTY,
1014 .name = njs_string("toExponential"),
1015 .value = njs_native_function(njs_number_prototype_to_exponential, 1),
1016 .writable = 1,
1017 .configurable = 1,
1018 },
1019 };
1020
1021
1022 const njs_object_init_t njs_number_prototype_init = {
1023 njs_number_prototype_properties,
1024 njs_nitems(njs_number_prototype_properties),
1025 };
1026
1027
1028 njs_int_t
njs_number_global_is_nan(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1029 njs_number_global_is_nan(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1030 njs_index_t unused)
1031 {
1032 double num;
1033 njs_int_t ret;
1034
1035 ret = njs_value_to_number(vm, njs_arg(args, nargs, 1), &num);
1036 if (njs_slow_path(ret != NJS_OK)) {
1037 return ret;
1038 }
1039
1040 njs_set_boolean(&vm->retval, isnan(num));
1041
1042 return NJS_OK;
1043 }
1044
1045
1046 njs_int_t
njs_number_global_is_finite(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1047 njs_number_global_is_finite(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1048 njs_index_t unused)
1049 {
1050 double num;
1051 njs_int_t ret;
1052
1053 ret = njs_value_to_number(vm, njs_arg(args, nargs, 1), &num);
1054 if (njs_slow_path(ret != NJS_OK)) {
1055 return ret;
1056 }
1057
1058 njs_set_boolean(&vm->retval, !(isnan(num) || isinf(num)));
1059
1060 return NJS_OK;
1061 }
1062
1063
1064 njs_int_t
njs_number_parse_int(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1065 njs_number_parse_int(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1066 njs_index_t unused)
1067 {
1068 double num;
1069 int64_t n;
1070 int32_t radix;
1071 njs_int_t ret;
1072 njs_str_t string;
1073 njs_bool_t minus, test_prefix;
1074 njs_value_t *value;
1075 const u_char *p, *end;
1076
1077 num = NAN;
1078
1079 if (nargs < 2) {
1080 goto done;
1081 }
1082
1083 value = njs_argument(args, 1);
1084
1085 if (!njs_is_string(value)) {
1086 ret = njs_value_to_string(vm, value, value);
1087 if (njs_slow_path(ret != NJS_OK)) {
1088 return ret;
1089 }
1090 }
1091
1092 njs_string_get(value, &string);
1093
1094 end = string.start + string.length;
1095
1096 for (p = string.start; p < end; p++) {
1097 if (*p != ' ') {
1098 goto found;
1099 }
1100 }
1101
1102 goto done;
1103
1104 found:
1105
1106 minus = 0;
1107
1108 if (p[0] == '-') {
1109 p++;
1110 minus = 1;
1111
1112 } else if (p[0] == '+') {
1113 p++;
1114 }
1115
1116 test_prefix = (end - p > 1);
1117 radix = 0;
1118
1119 if (nargs > 2) {
1120 ret = njs_value_to_int32(vm, njs_argument(args, 2), &radix);
1121 if (njs_slow_path(ret != NJS_OK)) {
1122 return ret;
1123 }
1124
1125 if (radix != 0) {
1126 if (radix < 2 || radix > 36) {
1127 goto done;
1128 }
1129
1130 if (radix != 16) {
1131 test_prefix = 0;
1132 }
1133 }
1134 }
1135
1136 if (radix == 0) {
1137 radix = 10;
1138 }
1139
1140 if (test_prefix && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
1141 p += 2;
1142 radix = 16;
1143 }
1144
1145 n = njs_number_radix_parse(&p, end, radix);
1146
1147 if (n >= 0) {
1148 num = n;
1149 num = minus ? -num : num;
1150 }
1151
1152 done:
1153
1154 njs_set_number(&vm->retval, num);
1155
1156 return NJS_OK;
1157 }
1158
1159
1160 njs_int_t
njs_number_parse_float(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1161 njs_number_parse_float(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1162 njs_index_t unused)
1163 {
1164 double num;
1165 njs_int_t ret;
1166
1167 num = NAN;
1168
1169 if (nargs > 1) {
1170 ret = njs_value_to_string(vm, &args[1], &args[1]);
1171 if (njs_slow_path(ret != NJS_OK)) {
1172 return ret;
1173 }
1174
1175 num = njs_string_to_number(&args[1], 1);
1176 }
1177
1178 njs_set_number(&vm->retval, num);
1179
1180 return NJS_OK;
1181 }
1182
1183
1184 const njs_object_type_init_t njs_number_type_init = {
1185 .constructor = njs_native_ctor(njs_number_constructor, 1, 0),
1186 .constructor_props = &njs_number_constructor_init,
1187 .prototype_props = &njs_number_prototype_init,
1188 .prototype_value = { .object_value = {
1189 .value = njs_value(NJS_NUMBER, 0, 0.0),
1190 .object = { .type = NJS_OBJECT_VALUE } }
1191 },
1192 };
1193