ref: 598de57d76b9b3ff421d1d737eca0b88e0d020a3
parent: 43384d9110fc552d12d59993bcc4d07a20b6e0ce
author: Tor Andersson <tor@ccxvii.net>
date: Thu Feb 27 08:49:22 EST 2014
Fix ToNumber from strings and parseInt and parseFloat. Follow the spec more strictly, and don't rely on strtod's grammar. GNU libc strtod accepts 0x prefixes, INF, INFINITY and much more which we don't want, so pre-filter the string we pass to strtod.
--- a/jsbuiltin.c
+++ b/jsbuiltin.c
@@ -43,15 +43,43 @@
{
const char *s = js_tostring(J, 1);
double radix = js_isdefined(J, 2) ? js_tonumber(J, 2) : 10;
+ char *e;
+ double n;
+
while (jsY_iswhite(*s) || jsY_isnewline(*s)) ++s;
- js_pushnumber(J, strtol(s, NULL, radix == 0 ? 10 : radix));
+ if (radix == 0)
+ radix = 10;
+ if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
+ s += 2;
+ radix = 16;
+ }
+ n = strtol(s, &e, radix);
+ if (s == e)
+ js_pushnumber(J, NAN);
+ else
+ js_pushnumber(J, n);
}
static void jsB_parseFloat(js_State *J, unsigned int argc)
{
const char *s = js_tostring(J, 1);
+ char *e;
+ double n;
+
while (jsY_iswhite(*s) || jsY_isnewline(*s)) ++s;
- js_pushnumber(J, strtod(s, NULL));
+ if (!strncmp(s, "Infinity", 8))
+ js_pushnumber(J, INFINITY);
+ else if (!strncmp(s, "+Infinity", 9))
+ js_pushnumber(J, INFINITY);
+ else if (!strncmp(s, "-Infinity", 9))
+ js_pushnumber(J, -INFINITY);
+ else {
+ n = js_strtod(s, &e);
+ if (e == s)
+ js_pushnumber(J, NAN);
+ else
+ js_pushnumber(J, n);
+ }
}
static void jsB_isNaN(js_State *J, unsigned int argc)
--- a/jsvalue.c
+++ b/jsvalue.c
@@ -110,6 +110,29 @@
}
}
+double js_strtod(const char *s, char **ep)
+{
+ char *end;
+ double n;
+ const char *e = s;
+ while (*e == '+' || *e == '-') ++e;
+ while (*e >= '0' && *e <= '9') ++e;
+ if (*e == '.') ++e;
+ while (*e >= '0' && *e <= '9') ++e;
+ if (*e == 'e' || *e == 'E') {
+ ++e;
+ while (*e == '+' || *e == '-') ++e;
+ while (*e >= '0' && *e <= '9') ++e;
+ }
+ n = strtod(s, &end);
+ if (end == e) {
+ *ep = (char*)e;
+ return n;
+ }
+ *ep = (char*)s;
+ return 0;
+}
+
/* ToNumber() on a string */
double jsV_stringtonumber(js_State *J, const char *s)
{
@@ -118,8 +141,14 @@
while (jsY_iswhite(*s) || jsY_isnewline(*s)) ++s;
if (s[0] == '0' && s[1] == 'x' && s[2] != 0)
n = strtol(s + 2, &e, 16);
+ else if (!strncmp(s, "Infinity", 8))
+ n = INFINITY, e = (char*)s + 8;
+ else if (!strncmp(s, "+Infinity", 9))
+ n = INFINITY, e = (char*)s + 9;
+ else if (!strncmp(s, "-Infinity", 9))
+ n = -INFINITY, e = (char*)s + 9;
else
- n = strtod(s, &e);
+ n = js_strtod(s, &e);
while (jsY_iswhite(*e) || jsY_isnewline(*e)) ++e;
if (*e) return NAN;
return n;
--- a/jsvalue.h
+++ b/jsvalue.h
@@ -123,6 +123,7 @@
js_Object *jsV_toobject(js_State *J, const js_Value *v);
js_Value jsV_toprimitive(js_State *J, const js_Value *v, int preferred);
+double js_strtod(const char *s, char **ep);
double jsV_numbertointeger(double n);
int jsV_numbertoint32(double n);
unsigned int jsV_numbertouint32(double n);