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