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