1 /*
2 * Number built-ins
3 */
4
5 #include "duk_internal.h"
6
7 #if defined(DUK_USE_NUMBER_BUILTIN)
8
duk__push_this_number_plain(duk_hthread * thr)9 DUK_LOCAL duk_double_t duk__push_this_number_plain(duk_hthread *thr) {
10 duk_hobject *h;
11
12 /* Number built-in accepts a plain number or a Number object (whose
13 * internal value is operated on). Other types cause TypeError.
14 */
15
16 duk_push_this(thr);
17 if (duk_is_number(thr, -1)) {
18 DUK_DDD(DUK_DDDPRINT("plain number value: %!T", (duk_tval *) duk_get_tval(thr, -1)));
19 goto done;
20 }
21 h = duk_get_hobject(thr, -1);
22 if (!h ||
23 (DUK_HOBJECT_GET_CLASS_NUMBER(h) != DUK_HOBJECT_CLASS_NUMBER)) {
24 DUK_DDD(DUK_DDDPRINT("unacceptable this value: %!T", (duk_tval *) duk_get_tval(thr, -1)));
25 DUK_ERROR_TYPE(thr, "number expected");
26 DUK_WO_NORETURN(return 0.0;);
27 }
28 duk_xget_owndataprop_stridx_short(thr, -1, DUK_STRIDX_INT_VALUE);
29 DUK_ASSERT(duk_is_number(thr, -1));
30 DUK_DDD(DUK_DDDPRINT("number object: %!T, internal value: %!T",
31 (duk_tval *) duk_get_tval(thr, -2), (duk_tval *) duk_get_tval(thr, -1)));
32 duk_remove_m2(thr);
33
34 done:
35 return duk_get_number(thr, -1);
36 }
37
duk_bi_number_constructor(duk_hthread * thr)38 DUK_INTERNAL duk_ret_t duk_bi_number_constructor(duk_hthread *thr) {
39 duk_idx_t nargs;
40 duk_hobject *h_this;
41
42 /*
43 * The Number constructor uses ToNumber(arg) for number coercion
44 * (coercing an undefined argument to NaN). However, if the
45 * argument is not given at all, +0 must be used instead. To do
46 * this, a vararg function is used.
47 */
48
49 nargs = duk_get_top(thr);
50 if (nargs == 0) {
51 duk_push_int(thr, 0);
52 }
53 duk_to_number(thr, 0);
54 duk_set_top(thr, 1);
55 DUK_ASSERT_TOP(thr, 1);
56
57 if (!duk_is_constructor_call(thr)) {
58 return 1;
59 }
60
61 /*
62 * E5 Section 15.7.2.1 requires that the constructed object
63 * must have the original Number.prototype as its internal
64 * prototype. However, since Number.prototype is non-writable
65 * and non-configurable, this doesn't have to be enforced here:
66 * The default object (bound to 'this') is OK, though we have
67 * to change its class.
68 *
69 * Internal value set to ToNumber(arg) or +0; if no arg given,
70 * ToNumber(undefined) = NaN, so special treatment is needed
71 * (above). String internal value is immutable.
72 */
73
74 /* XXX: helper */
75 duk_push_this(thr);
76 h_this = duk_known_hobject(thr, -1);
77 DUK_HOBJECT_SET_CLASS_NUMBER(h_this, DUK_HOBJECT_CLASS_NUMBER);
78
79 DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_this) == thr->builtins[DUK_BIDX_NUMBER_PROTOTYPE]);
80 DUK_ASSERT(DUK_HOBJECT_GET_CLASS_NUMBER(h_this) == DUK_HOBJECT_CLASS_NUMBER);
81 DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE(h_this));
82
83 duk_dup_0(thr); /* -> [ val obj val ] */
84 duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE);
85 return 0; /* no return value -> don't replace created value */
86 }
87
duk_bi_number_prototype_value_of(duk_hthread * thr)88 DUK_INTERNAL duk_ret_t duk_bi_number_prototype_value_of(duk_hthread *thr) {
89 (void) duk__push_this_number_plain(thr);
90 return 1;
91 }
92
duk_bi_number_prototype_to_string(duk_hthread * thr)93 DUK_INTERNAL duk_ret_t duk_bi_number_prototype_to_string(duk_hthread *thr) {
94 duk_small_int_t radix;
95 duk_small_uint_t n2s_flags;
96
97 (void) duk__push_this_number_plain(thr);
98 if (duk_is_undefined(thr, 0)) {
99 radix = 10;
100 } else {
101 radix = (duk_small_int_t) duk_to_int_check_range(thr, 0, 2, 36);
102 }
103 DUK_DDD(DUK_DDDPRINT("radix=%ld", (long) radix));
104
105 n2s_flags = 0;
106
107 duk_numconv_stringify(thr,
108 radix /*radix*/,
109 0 /*digits*/,
110 n2s_flags /*flags*/);
111 return 1;
112 }
113
duk_bi_number_prototype_to_locale_string(duk_hthread * thr)114 DUK_INTERNAL duk_ret_t duk_bi_number_prototype_to_locale_string(duk_hthread *thr) {
115 /* XXX: just use toString() for now; permitted although not recommended.
116 * nargs==1, so radix is passed to toString().
117 */
118 return duk_bi_number_prototype_to_string(thr);
119 }
120
121 /*
122 * toFixed(), toExponential(), toPrecision()
123 */
124
125 /* XXX: shared helper for toFixed(), toExponential(), toPrecision()? */
126
duk_bi_number_prototype_to_fixed(duk_hthread * thr)127 DUK_INTERNAL duk_ret_t duk_bi_number_prototype_to_fixed(duk_hthread *thr) {
128 duk_small_int_t frac_digits;
129 duk_double_t d;
130 duk_small_int_t c;
131 duk_small_uint_t n2s_flags;
132
133 /* In ES5.1 frac_digits is coerced first; in ES2015 the 'this number
134 * value' check is done first.
135 */
136 d = duk__push_this_number_plain(thr);
137 frac_digits = (duk_small_int_t) duk_to_int_check_range(thr, 0, 0, 20);
138
139 c = (duk_small_int_t) DUK_FPCLASSIFY(d);
140 if (c == DUK_FP_NAN || c == DUK_FP_INFINITE) {
141 goto use_to_string;
142 }
143
144 if (d >= 1.0e21 || d <= -1.0e21) {
145 goto use_to_string;
146 }
147
148 n2s_flags = DUK_N2S_FLAG_FIXED_FORMAT |
149 DUK_N2S_FLAG_FRACTION_DIGITS;
150
151 duk_numconv_stringify(thr,
152 10 /*radix*/,
153 frac_digits /*digits*/,
154 n2s_flags /*flags*/);
155 return 1;
156
157 use_to_string:
158 DUK_ASSERT_TOP(thr, 2);
159 duk_to_string(thr, -1);
160 return 1;
161 }
162
duk_bi_number_prototype_to_exponential(duk_hthread * thr)163 DUK_INTERNAL duk_ret_t duk_bi_number_prototype_to_exponential(duk_hthread *thr) {
164 duk_bool_t frac_undefined;
165 duk_small_int_t frac_digits;
166 duk_double_t d;
167 duk_small_int_t c;
168 duk_small_uint_t n2s_flags;
169
170 d = duk__push_this_number_plain(thr);
171
172 frac_undefined = duk_is_undefined(thr, 0);
173 duk_to_int(thr, 0); /* for side effects */
174
175 c = (duk_small_int_t) DUK_FPCLASSIFY(d);
176 if (c == DUK_FP_NAN || c == DUK_FP_INFINITE) {
177 goto use_to_string;
178 }
179
180 frac_digits = (duk_small_int_t) duk_to_int_check_range(thr, 0, 0, 20);
181
182 n2s_flags = DUK_N2S_FLAG_FORCE_EXP |
183 (frac_undefined ? 0 : DUK_N2S_FLAG_FIXED_FORMAT);
184
185 duk_numconv_stringify(thr,
186 10 /*radix*/,
187 frac_digits + 1 /*leading digit + fractions*/,
188 n2s_flags /*flags*/);
189 return 1;
190
191 use_to_string:
192 DUK_ASSERT_TOP(thr, 2);
193 duk_to_string(thr, -1);
194 return 1;
195 }
196
duk_bi_number_prototype_to_precision(duk_hthread * thr)197 DUK_INTERNAL duk_ret_t duk_bi_number_prototype_to_precision(duk_hthread *thr) {
198 /* The specification has quite awkward order of coercion and
199 * checks for toPrecision(). The operations below are a bit
200 * reordered, within constraints of observable side effects.
201 */
202
203 duk_double_t d;
204 duk_small_int_t prec;
205 duk_small_int_t c;
206 duk_small_uint_t n2s_flags;
207
208 DUK_ASSERT_TOP(thr, 1);
209
210 d = duk__push_this_number_plain(thr);
211 if (duk_is_undefined(thr, 0)) {
212 goto use_to_string;
213 }
214 DUK_ASSERT_TOP(thr, 2);
215
216 duk_to_int(thr, 0); /* for side effects */
217
218 c = (duk_small_int_t) DUK_FPCLASSIFY(d);
219 if (c == DUK_FP_NAN || c == DUK_FP_INFINITE) {
220 goto use_to_string;
221 }
222
223 prec = (duk_small_int_t) duk_to_int_check_range(thr, 0, 1, 21);
224
225 n2s_flags = DUK_N2S_FLAG_FIXED_FORMAT |
226 DUK_N2S_FLAG_NO_ZERO_PAD;
227
228 duk_numconv_stringify(thr,
229 10 /*radix*/,
230 prec /*digits*/,
231 n2s_flags /*flags*/);
232 return 1;
233
234 use_to_string:
235 /* Used when precision is undefined; also used for NaN (-> "NaN"),
236 * and +/- infinity (-> "Infinity", "-Infinity").
237 */
238
239 DUK_ASSERT_TOP(thr, 2);
240 duk_to_string(thr, -1);
241 return 1;
242 }
243
244 /*
245 * ES2015 isFinite() etc
246 */
247
248 #if defined(DUK_USE_ES6)
duk_bi_number_check_shared(duk_hthread * thr)249 DUK_INTERNAL duk_ret_t duk_bi_number_check_shared(duk_hthread *thr) {
250 duk_int_t magic;
251 duk_bool_t ret = 0;
252
253 if (duk_is_number(thr, 0)) {
254 duk_double_t d;
255
256 magic = duk_get_current_magic(thr);
257 d = duk_get_number(thr, 0);
258
259 switch (magic) {
260 case 0: /* isFinite() */
261 ret = duk_double_is_finite(d);
262 break;
263 case 1: /* isInteger() */
264 ret = duk_double_is_integer(d);
265 break;
266 case 2: /* isNaN() */
267 ret = duk_double_is_nan(d);
268 break;
269 default: /* isSafeInteger() */
270 DUK_ASSERT(magic == 3);
271 ret = duk_double_is_safe_integer(d);
272 }
273 }
274
275 duk_push_boolean(thr, ret);
276 return 1;
277 }
278 #endif /* DUK_USE_ES6 */
279
280 #endif /* DUK_USE_NUMBER_BUILTIN */
281