1 /*
2 * Copyright (c) 2003
3 * David Leonard. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of David Leonard nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #if HAVE_CONFIG_H
32 # include <config.h>
33 #endif
34
35 #include <see/mem.h>
36 #include <see/value.h>
37 #include <see/string.h>
38 #include <see/object.h>
39 #include <see/native.h>
40 #include <see/cfunction.h>
41 #include <see/error.h>
42 #include <see/interpreter.h>
43
44 #include "stringdefs.h"
45 #include "dtoa.h"
46 #include "init.h"
47 #include "nmath.h"
48 #include "array.h"
49
50 /*
51 * 15.7 The Number object.
52 */
53
54 /* structure of number instances */
55 struct number_object {
56 struct SEE_native native;
57 SEE_number_t number; /* Value */
58 };
59
60 /* Prototypes */
61 static void radix_tostring(struct SEE_string *, SEE_number_t, int);
62 static struct number_object *tonumber(struct SEE_interpreter *,
63 struct SEE_object *);
64
65 static void number_construct(struct SEE_interpreter *, struct SEE_object *,
66 struct SEE_object *, int, struct SEE_value **, struct SEE_value *);
67 static void number_call(struct SEE_interpreter *, struct SEE_object *,
68 struct SEE_object *, int, struct SEE_value **, struct SEE_value *);
69
70 static void number_proto_toString(struct SEE_interpreter *,
71 struct SEE_object *, struct SEE_object *, int, struct SEE_value **,
72 struct SEE_value *);
73 static void number_proto_toLocaleString(struct SEE_interpreter *,
74 struct SEE_object *, struct SEE_object *, int, struct SEE_value **,
75 struct SEE_value *);
76 static void number_proto_valueOf(struct SEE_interpreter *,
77 struct SEE_object *, struct SEE_object *, int, struct SEE_value **,
78 struct SEE_value *);
79 static void number_proto_toFixed(struct SEE_interpreter *,
80 struct SEE_object *, struct SEE_object *, int, struct SEE_value **,
81 struct SEE_value *);
82 static void number_proto_toExponential(struct SEE_interpreter *,
83 struct SEE_object *, struct SEE_object *, int, struct SEE_value **,
84 struct SEE_value *);
85 static void number_proto_toPrecision(struct SEE_interpreter *,
86 struct SEE_object *, struct SEE_object *, int, struct SEE_value **,
87 struct SEE_value *);
88
89 /* object class for Number constructor */
90 static struct SEE_objectclass number_const_class = {
91 "NumberConstructor", /* Class */
92 SEE_native_get, /* Get */
93 SEE_native_put, /* Put */
94 SEE_native_canput, /* CanPut */
95 SEE_native_hasproperty, /* HasProperty */
96 SEE_native_delete, /* Delete */
97 SEE_native_defaultvalue, /* DefaultValue */
98 SEE_native_enumerator, /* DefaultValue */
99 number_construct, /* Construct */
100 number_call /* Call */
101 };
102
103 /* object class for Number.prototype and number instances */
104 static struct SEE_objectclass number_inst_class = {
105 "Number", /* Class */
106 SEE_native_get, /* Get */
107 SEE_native_put, /* Put */
108 SEE_native_canput, /* CanPut */
109 SEE_native_hasproperty, /* HasProperty */
110 SEE_native_delete, /* Delete */
111 SEE_native_defaultvalue, /* DefaultValue */
112 SEE_native_enumerator /* enumerator */
113 };
114
115 void
SEE_Number_alloc(interp)116 SEE_Number_alloc(interp)
117 struct SEE_interpreter *interp;
118 {
119 interp->Number =
120 (struct SEE_object *)SEE_NEW(interp, struct SEE_native);
121 interp->Number_prototype =
122 (struct SEE_object *)SEE_NEW(interp, struct number_object);
123 }
124
125 void
SEE_Number_init(interp)126 SEE_Number_init(interp)
127 struct SEE_interpreter *interp;
128 {
129 struct SEE_object *Number; /* struct SEE_native */
130 struct SEE_object *Number_prototype; /* struct number_object */
131 struct SEE_value v;
132
133 Number = interp->Number;
134 SEE_native_init((struct SEE_native *)Number, interp,
135 &number_const_class, interp->Function_prototype);
136
137 Number_prototype = interp->Number_prototype;
138
139
140 /* 15.7.3 Number.length = 1 */
141 SEE_SET_NUMBER(&v, 1);
142 SEE_OBJECT_PUT(interp, Number, STR(length), &v, SEE_ATTR_LENGTH);
143
144 /* 15.7.3.1 Number.prototype */
145 SEE_SET_OBJECT(&v, Number_prototype);
146 SEE_OBJECT_PUT(interp, Number, STR(prototype), &v,
147 SEE_ATTR_DONTENUM | SEE_ATTR_DONTDELETE | SEE_ATTR_READONLY);
148
149 /* 15.7.3.2 Number.MAX_VALUE */
150 SEE_SET_NUMBER(&v, SEE_MaxNumber);
151 SEE_OBJECT_PUT(interp, Number, STR(MAX_VALUE), &v,
152 SEE_ATTR_DONTENUM | SEE_ATTR_DONTDELETE | SEE_ATTR_READONLY);
153
154 /* 15.7.3.3 Number.MIN_VALUE */
155 SEE_SET_NUMBER(&v, SEE_MinNumber);
156 SEE_OBJECT_PUT(interp, Number, STR(MIN_VALUE), &v,
157 SEE_ATTR_DONTENUM | SEE_ATTR_DONTDELETE | SEE_ATTR_READONLY);
158
159 /* 15.7.3.4 Number.NaN */
160 SEE_SET_NUMBER(&v, SEE_NaN);
161 SEE_OBJECT_PUT(interp, Number, STR(NaN), &v,
162 SEE_ATTR_DONTENUM | SEE_ATTR_DONTDELETE | SEE_ATTR_READONLY);
163
164 /* 15.7.3.5 Number.NEGATIVE_INFINITY */
165 SEE_SET_NUMBER(&v, -SEE_Infinity);
166 SEE_OBJECT_PUT(interp, Number, STR(NEGATIVE_INFINITY), &v,
167 SEE_ATTR_DONTENUM | SEE_ATTR_DONTDELETE | SEE_ATTR_READONLY);
168
169 /* 15.7.3.6 Number.POSITIVE_INFINITY */
170 SEE_SET_NUMBER(&v, SEE_Infinity);
171 SEE_OBJECT_PUT(interp, Number, STR(POSITIVE_INFINITY), &v,
172 SEE_ATTR_DONTENUM | SEE_ATTR_DONTDELETE | SEE_ATTR_READONLY);
173
174 SEE_native_init((struct SEE_native *)Number_prototype, interp,
175 &number_inst_class, interp->Object_prototype); /* 15.7.4 */
176 ((struct number_object *)Number_prototype)->number = 0; /* 15.7.4 */
177
178 /* 15.7.4.1 Number.prototype.constructor */
179 SEE_SET_OBJECT(&v, Number);
180 SEE_OBJECT_PUT(interp, Number_prototype, STR(constructor), &v,
181 SEE_ATTR_DEFAULT);
182
183 #define PUTFUNC(name, len) \
184 SEE_SET_OBJECT(&v, SEE_cfunction_make(interp, \
185 number_proto_##name, STR(name), len)); \
186 SEE_OBJECT_PUT(interp, Number_prototype, STR(name), &v, \
187 SEE_ATTR_DEFAULT);
188
189 PUTFUNC(toString, 1)
190 PUTFUNC(toLocaleString, 0)
191 PUTFUNC(valueOf, 0)
192 PUTFUNC(toFixed, 1)
193 PUTFUNC(toExponential, 1)
194 PUTFUNC(toPrecision, 1)
195 }
196
197 static struct number_object *
tonumber(interp,o)198 tonumber(interp, o)
199 struct SEE_interpreter *interp;
200 struct SEE_object *o;
201 {
202 if (!o || o->objectclass != &number_inst_class)
203 SEE_error_throw_string(interp, interp->TypeError,
204 STR(not_number));
205 return (struct number_object *)o;
206 }
207
208 /* 15.7.2.1 */
209 static void
number_construct(interp,self,thisobj,argc,argv,res)210 number_construct(interp, self, thisobj, argc, argv, res)
211 struct SEE_interpreter *interp;
212 struct SEE_object *self, *thisobj;
213 int argc;
214 struct SEE_value **argv;
215 struct SEE_value *res;
216 {
217 struct number_object *no;
218 struct SEE_value v;
219
220 if (argc == 0)
221 SEE_SET_NUMBER(&v, 0);
222 else
223 SEE_ToNumber(interp, argv[0], &v);
224
225 no = SEE_NEW(interp, struct number_object);
226 SEE_native_init(&no->native, interp, &number_inst_class,
227 interp->Number_prototype);
228 no->number = v.u.number;
229
230 SEE_SET_OBJECT(res, (struct SEE_object *)no);
231 }
232
233 /* 15.7.1.1 */
234 static void
number_call(interp,self,thisobj,argc,argv,res)235 number_call(interp, self, thisobj, argc, argv, res)
236 struct SEE_interpreter *interp;
237 struct SEE_object *self, *thisobj;
238 int argc;
239 struct SEE_value **argv;
240 struct SEE_value *res;
241 {
242 if (argc < 1)
243 SEE_SET_NUMBER(res, 0);
244 else if (SEE_COMPAT_JS(interp, ==, JS12) &&
245 SEE_VALUE_GET_TYPE(argv[0]) == SEE_OBJECT &&
246 SEE_is_Array(argv[0]->u.object))
247 SEE_SET_NUMBER(res, SEE_Array_length(interp,
248 argv[0]->u.object));
249 else
250 SEE_ToNumber(interp, argv[0], res);
251 }
252
253 static void
radix_tostring(s,n,radix)254 radix_tostring(s, n, radix)
255 struct SEE_string *s;
256 SEE_number_t n;
257 int radix;
258 {
259 int d;
260
261 if (n >= radix) {
262 radix_tostring(s, n / radix, radix);
263 n = NUMBER_fmod(n, (SEE_number_t)radix);
264 }
265 d = NUMBER_floor(n);
266 if (d < 10) SEE_string_addch(s, '0' + d);
267 else SEE_string_addch(s, 'a' + d - 10);
268 }
269
270
271 /* 15.7.4.2 Number.prototype.toString() */
272 static void
number_proto_toString(interp,self,thisobj,argc,argv,res)273 number_proto_toString(interp, self, thisobj, argc, argv, res)
274 struct SEE_interpreter *interp;
275 struct SEE_object *self, *thisobj;
276 int argc;
277 struct SEE_value **argv, *res;
278 {
279 struct number_object *no;
280 SEE_int32_t radix;
281 struct SEE_value v;
282
283 no = tonumber(interp, thisobj);
284
285 if (argc == 0 || SEE_VALUE_GET_TYPE(argv[0]) == SEE_UNDEFINED)
286 radix = 10;
287 else
288 radix = SEE_ToInt32(interp, argv[0]);
289
290 if (radix == 10) {
291 SEE_SET_NUMBER(&v, no->number);
292 SEE_ToString(interp, &v, res);
293 } else if (radix >= 2 && radix <= 36) {
294 /*
295 * The specs say to do something "implementation dependent",
296 * but here I try and convert the number into the desired
297 * representation. Numbers bigger than 10^20 or smaller than
298 * 10^-6 are printed in p-notation (with the exponent being
299 * expressed in base 10)
300 */
301 struct SEE_string *s;
302 SEE_number_t n = no->number, ni, nf;
303 int expon;
304
305 if (SEE_ISNAN(n)) {
306 SEE_SET_STRING(res, STR(NaN));
307 return;
308 }
309 if (n == 0) {
310 SEE_SET_STRING(res, STR(zero_digit)); /* "0" */
311 return;
312 }
313 s = SEE_string_new(interp, 0);
314 if (n < 0) {
315 SEE_string_addch(s, '-');
316 n = -n;
317 }
318 if (!SEE_ISFINITE(n)) {
319 SEE_string_append(s, STR(Infinity));
320 SEE_SET_STRING(res, s);
321 return;
322 }
323 if (n > 1e20 || n < 1e-6) {
324 expon = NUMBER_floor(NUMBER_log(n) /
325 NUMBER_log((SEE_number_t)radix));
326 n /= NUMBER_pow((SEE_number_t)radix,
327 (SEE_number_t)expon);
328 if (n == 0) {
329 /* e.g.: Number(MAX_VALUE).toString(16) */
330 SEE_string_append(s, STR(Infinity));
331 SEE_SET_STRING(res, s);
332 return;
333 }
334 if (!SEE_ISFINITE(n)) {
335 /* e.g.: Number(MIN_VALUE).toString(16) */
336 SEE_SET_STRING(res, STR(zero_digit));
337 return;
338 }
339 } else
340 expon = 0;
341 ni = NUMBER_floor(n);
342 nf = n - ni;
343 radix_tostring(s, ni, radix);
344 if (nf > 0) {
345 int i;
346 #define MAXPREC 20
347 SEE_string_addch(s, '.');
348 for (i = 0; i < MAXPREC && nf; i++) {
349 SEE_number_t d;
350 nf *= radix;
351 if (i == MAXPREC - 1) {
352 d = NUMBER_floor(nf + 0.5);
353 } else {
354 d = NUMBER_floor(nf);
355 nf -= d;
356 }
357 if (d < 10) SEE_string_addch(s, '0' + (int)d);
358 else SEE_string_addch(s, 'a' + (int)d - 10);
359 };
360 }
361 if (expon) {
362 SEE_string_addch(s, 'p');
363 if (expon < 0) {
364 expon = -expon;
365 SEE_string_addch(s, '-');
366 }
367 radix_tostring(s, (SEE_number_t)expon, 10);
368 }
369
370 SEE_SET_STRING(res, s);
371 } else
372 SEE_error_throw_string(interp, interp->RangeError,
373 STR(bad_radix));
374 }
375
376 /* 15.7.4.3 Number.prototype.toLocaleString() */
377 static void
number_proto_toLocaleString(interp,self,thisobj,argc,argv,res)378 number_proto_toLocaleString(interp, self, thisobj, argc, argv, res)
379 struct SEE_interpreter *interp;
380 struct SEE_object *self, *thisobj;
381 int argc;
382 struct SEE_value **argv, *res;
383 {
384 /* struct number_object *no;
385 no = tonumber(interp, thisobj); */
386
387 /* XXX - really should use localeconv() */
388 number_proto_toString(interp, self, thisobj, 0, NULL, res);
389 }
390
391 /* 15.7.4.4 Number.prototype.valueOf() */
392 static void
number_proto_valueOf(interp,self,thisobj,argc,argv,res)393 number_proto_valueOf(interp, self, thisobj, argc, argv, res)
394 struct SEE_interpreter *interp;
395 struct SEE_object *self, *thisobj;
396 int argc;
397 struct SEE_value **argv, *res;
398 {
399 struct number_object *no;
400
401 no = tonumber(interp, thisobj);
402 SEE_SET_NUMBER(res, no->number);
403 }
404
405 /* 15.7.4.5 Number.prototype.toFixed() */
406 static void
number_proto_toFixed(interp,self,thisobj,argc,argv,res)407 number_proto_toFixed(interp, self, thisobj, argc, argv, res)
408 struct SEE_interpreter *interp;
409 struct SEE_object *self, *thisobj;
410 int argc;
411 struct SEE_value **argv, *res;
412 {
413 struct number_object *no;
414 struct SEE_value v;
415 struct SEE_string *m;
416 SEE_number_t x;
417 char *ms, *endstr;
418 int f, sign, n, i, k;
419
420 if (argc > 0 && SEE_VALUE_GET_TYPE(argv[0]) != SEE_UNDEFINED) {
421 SEE_ToInteger(interp, argv[0], &v);
422 if (v.u.number < 0 || v.u.number > 20 || SEE_NUMBER_ISNAN(&v))
423 SEE_error_throw(interp, interp->RangeError, "%f", v.u.number);
424 f = v.u.number;
425 } else
426 f = 0;
427
428 no = tonumber(interp, thisobj);
429 x = no->number;
430 if (!SEE_ISFINITE(x) || x <= -1e21 || x >= 1e21) {
431 SEE_SET_NUMBER(&v, x);
432 SEE_ToString(interp, &v, res);
433 return;
434 }
435
436 ms = SEE_dtoa(x, DTOA_MODE_FCVT, f, &n, &sign, &endstr);
437 k = endstr - ms;
438
439 m = SEE_string_new(interp, 0);
440 if (x < 0)
441 SEE_string_addch(m, '-');
442 if (n <= 0)
443 SEE_string_addch(m, '0');
444 if (n < 0) {
445 SEE_string_addch(m, '.');
446 for (i = 0; i < -n; i++)
447 SEE_string_addch(m, '0');
448 }
449 for (i = 0; i < k; i++) {
450 if (i == n)
451 SEE_string_addch(m, '.');
452 SEE_string_addch(m, ms[i]);
453 }
454 for (i = k; i < f + n; i++) {
455 if (i == n)
456 SEE_string_addch(m, '.');
457 SEE_string_addch(m, '0');
458 }
459 SEE_freedtoa(ms);
460 SEE_SET_STRING(res, m);
461 }
462
463 /* 15.7.4.6 Number.prototype.toExponential() */
464 static void
number_proto_toExponential(interp,self,thisobj,argc,argv,res)465 number_proto_toExponential(interp, self, thisobj, argc, argv, res)
466 struct SEE_interpreter *interp;
467 struct SEE_object *self, *thisobj;
468 int argc;
469 struct SEE_value **argv, *res;
470 {
471 struct number_object *no;
472 struct SEE_value v;
473 struct SEE_string *s;
474 SEE_number_t x;
475 char *ms, *endstr;
476 int e, f, n, i, k, sign;
477
478 if (argc > 0 && SEE_VALUE_GET_TYPE(argv[0]) != SEE_UNDEFINED) {
479 SEE_ToInteger(interp, argv[0], &v);
480 if (v.u.number < 0 || v.u.number > 20 || SEE_NUMBER_ISNAN(&v))
481 SEE_error_throw(interp, interp->RangeError, "%f", v.u.number);
482 f = v.u.number;
483 } else
484 f = 0;
485
486 no = tonumber(interp, thisobj);
487 x = no->number;
488 SEE_SET_NUMBER(&v, x);
489 if (!SEE_NUMBER_ISFINITE(&v)) {
490 SEE_ToString(interp, &v, res);
491 return;
492 }
493
494 if (f)
495 ms = SEE_dtoa(x, DTOA_MODE_ECVT, f, &n, &sign, &endstr);
496 else
497 ms = SEE_dtoa(x, DTOA_MODE_SHORT_SW, 31, &n, &sign, &endstr);
498 k = endstr - ms;
499 if (x)
500 e = n - 1;
501 else
502 e = 0;
503
504 s = SEE_string_new(interp, 0);
505 if (x < 0)
506 SEE_string_addch(s, '-');
507 if (k == 0)
508 SEE_string_addch(s, '0');
509 else
510 SEE_string_addch(s, ms[0]);
511 if (k > 1 || f) {
512 SEE_string_addch(s, '.');
513 for (i = 1; i < k; i++)
514 SEE_string_addch(s, ms[i]);
515 for (; i < f + 1; i++)
516 SEE_string_addch(s, '0');
517 }
518 SEE_string_addch(s, 'e');
519 if (e >= 0)
520 SEE_string_addch(s, '+');
521 SEE_string_append_int(s, e);
522 SEE_freedtoa(ms);
523 SEE_SET_STRING(res, s);
524 }
525
526 /* 15.7.4.7 Number.prototype.toPrecision() */
527 static void
number_proto_toPrecision(interp,self,thisobj,argc,argv,res)528 number_proto_toPrecision(interp, self, thisobj, argc, argv, res)
529 struct SEE_interpreter *interp;
530 struct SEE_object *self, *thisobj;
531 int argc;
532 struct SEE_value **argv, *res;
533 {
534 struct number_object *no;
535 struct SEE_value v;
536 struct SEE_string *s;
537 SEE_number_t x;
538 char *ms, *endstr;
539 int p, n, k, e, i, sign;
540
541 no = tonumber(interp, thisobj);
542 x = no->number;
543
544 SEE_SET_NUMBER(&v, x);
545 if (argc < 1 ||
546 SEE_VALUE_GET_TYPE(argv[0]) == SEE_UNDEFINED ||
547 !SEE_NUMBER_ISFINITE(&v))
548 {
549 SEE_ToString(interp, &v, res);
550 return;
551 }
552
553 SEE_ToInteger(interp, argv[0], &v);
554 if (v.u.number < 1 || v.u.number > 21 || SEE_NUMBER_ISNAN(&v))
555 SEE_error_throw(interp, interp->RangeError, "%f", v.u.number);
556 p = v.u.number;
557
558 s = SEE_string_new(interp, 0);
559 if (x < 0)
560 SEE_string_addch(s, '-');
561 ms = SEE_dtoa(x, DTOA_MODE_ECVT, p, &n, &sign, &endstr);
562 k = endstr - ms;
563 if (x)
564 e = n - 1;
565 else {
566 e = 0;
567 goto fixed;
568 }
569 if (e < -6 || e >= p) {
570 if (k == 0)
571 SEE_string_addch(s, '0');
572 else
573 SEE_string_addch(s, ms[0]);
574 if (p > 1) {
575 SEE_string_addch(s, '.');
576 for (i = 1; i < k; i++)
577 SEE_string_addch(s, ms[i]);
578 for (; i < p - 1; i++)
579 SEE_string_addch(s, '0');
580 }
581 SEE_string_addch(s, 'e');
582 if (e >= 0)
583 SEE_string_addch(s, '+');
584 SEE_string_append_int(s, e);
585 } else {
586 fixed:
587 if (n <= 0)
588 SEE_string_addch(s, '0');
589 if (n < 0) {
590 SEE_string_addch(s, '.');
591 for (i = 0; i < -n; i++)
592 SEE_string_addch(s, '0');
593 }
594 for (i = 0; i < k; i++) {
595 if (i == n)
596 SEE_string_addch(s, '.');
597 SEE_string_addch(s, ms[i]);
598 }
599 for (i = k; i < p; i++) {
600 if (i == n)
601 SEE_string_addch(s, '.');
602 SEE_string_addch(s, '0');
603 }
604 }
605 SEE_freedtoa(ms);
606 SEE_SET_STRING(res, s);
607 }
608