Bug 106495: js_strtod and js_strtointeger no longer require \0-terminated js chars. r=brendan

This commit is contained in:
igor@mir2.org 2007-08-04 12:05:16 -07:00
parent 16dfe406f9
commit b878f5679c
5 changed files with 61 additions and 48 deletions

View File

@ -97,11 +97,8 @@ num_parseFloat(JSContext *cx, uintN argc, jsval *vp)
str = js_ValueToString(cx, vp[2]);
if (!str)
return JS_FALSE;
/* XXXbe js_strtod shouldn't require NUL termination */
bp = js_UndependString(cx, str);
if (!bp)
return JS_FALSE;
if (!js_strtod(cx, bp, &ep, &d))
bp = JSSTRING_CHARS(str);
if (!js_strtod(cx, bp, bp + JSSTRING_LENGTH(str), &ep, &d))
return JS_FALSE;
if (ep == bp) {
*vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
@ -133,11 +130,8 @@ num_parseInt(JSContext *cx, uintN argc, jsval *vp)
str = js_ValueToString(cx, vp[2]);
if (!str)
return JS_FALSE;
/* XXXbe js_strtointeger shouldn't require NUL termination */
bp = js_UndependString(cx, str);
if (!bp)
return JS_FALSE;
if (!js_strtointeger(cx, bp, &ep, radix, &d))
bp = JSSTRING_CHARS(str);
if (!js_strtointeger(cx, bp, bp + JSSTRING_LENGTH(str), &ep, radix, &d))
return JS_FALSE;
if (ep == bp) {
*vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
@ -706,7 +700,7 @@ js_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp)
{
JSObject *obj;
JSString *str;
const jschar *bp, *ep;
const jschar *bp, *end, *ep;
if (JSVAL_IS_OBJECT(v)) {
obj = JSVAL_TO_OBJECT(v);
@ -723,20 +717,19 @@ js_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp)
*dp = *JSVAL_TO_DOUBLE(v);
} else if (JSVAL_IS_STRING(v)) {
str = JSVAL_TO_STRING(v);
/*
* Note that ECMA doesn't treat a string beginning with a '0' as an
* octal number here. This works because all such numbers will be
* interpreted as decimal by js_strtod and will never get passed to
* js_strtointeger (which would interpret them as octal).
*/
/* XXXbe js_strtod shouldn't require NUL termination */
bp = js_UndependString(cx, str);
if (!bp)
return JS_FALSE;
if ((!js_strtod(cx, bp, &ep, dp) ||
js_SkipWhiteSpace(ep) != bp + str->length) &&
(!js_strtointeger(cx, bp, &ep, 0, dp) ||
js_SkipWhiteSpace(ep) != bp + str->length)) {
bp = JSSTRING_CHARS(str);
end = bp + JSSTRING_LENGTH(str);
if ((!js_strtod(cx, bp, end, &ep, dp) ||
js_SkipWhiteSpace(ep, end) != end) &&
(!js_strtointeger(cx, bp, end, &ep, 0, dp) ||
js_SkipWhiteSpace(ep, end) != end)) {
goto badstr;
}
} else if (JSVAL_IS_BOOLEAN(v)) {
@ -877,15 +870,18 @@ js_DoubleToInteger(jsdouble d)
JSBool
js_strtod(JSContext *cx, const jschar *s, const jschar **ep, jsdouble *dp)
js_strtod(JSContext *cx, const jschar *s, const jschar *send,
const jschar **ep, jsdouble *dp)
{
const jschar *s1;
size_t length, i;
char cbuf[32];
size_t i;
char *cstr, *istr, *estr;
JSBool negative;
jsdouble d;
const jschar *s1 = js_SkipWhiteSpace(s);
size_t length = js_strlen(s1);
s1 = js_SkipWhiteSpace(s, send);
length = send - s1;
/* Use cbuf to avoid malloc */
if (length >= sizeof cbuf) {
@ -896,13 +892,12 @@ js_strtod(JSContext *cx, const jschar *s, const jschar **ep, jsdouble *dp)
cstr = cbuf;
}
for (i = 0; i <= length; i++) {
if (s1[i] >> 8) {
cstr[i] = 0;
for (i = 0; i != length; i++) {
if (s1[i] >> 8)
break;
}
cstr[i] = (char)s1[i];
}
cstr[i] = 0;
istr = cstr;
if ((negative = (*istr == '-')) != 0 || *istr == '+')
@ -979,41 +974,54 @@ static intN GetNextBinaryDigit(struct BinaryDigitReader *bdr)
}
JSBool
js_strtointeger(JSContext *cx, const jschar *s, const jschar **ep, jsint base, jsdouble *dp)
js_strtointeger(JSContext *cx, const jschar *s, const jschar *send,
const jschar **ep, jsint base, jsdouble *dp)
{
const jschar *s1, *start;
JSBool negative;
jsdouble value;
const jschar *start;
const jschar *s1 = js_SkipWhiteSpace(s);
if ((negative = (*s1 == '-')) != 0 || *s1 == '+')
s1 = js_SkipWhiteSpace(s, send);
if (s1 == send)
goto no_digits;
if ((negative = (*s1 == '-')) != 0 || *s1 == '+') {
s1++;
if (s1 == send)
goto no_digits;
}
if (base == 0) {
/* No base supplied, or some base that evaluated to 0. */
if (*s1 == '0') {
/* It's either hex or octal; only increment char if str isn't '0' */
if (s1[1] == 'X' || s1[1] == 'x') { /* Hex */
s1 += 2;
if (s1 + 1 != send && (s1[1] == 'X' || s1[1] == 'x')) {
base = 16;
} else { /* Octal */
s1 += 2;
if (s1 == send)
goto no_digits;
} else {
base = 8;
}
} else {
base = 10; /* Default to decimal. */
}
} else if (base == 16 && *s1 == '0' && (s1[1] == 'X' || s1[1] == 'x')) {
} else if (base == 16) {
/* If base is 16, ignore hex prefix. */
s1 += 2;
if (*s1 == '0' && s1 + 1 != send && (s1[1] == 'X' || s1[1] == 'x')) {
s1 += 2;
if (s1 == send)
goto no_digits;
}
}
/*
* Done with the preliminaries; find some prefix of the string that's
* a number in the given base.
*/
start = s1; /* Mark - if string is empty, we return NaN. */
JS_ASSERT(s1 < send);
start = s1;
value = 0.0;
for (;;) {
do {
uintN digit;
jschar c = *s1;
if ('0' <= c && c <= '9')
@ -1027,8 +1035,7 @@ js_strtointeger(JSContext *cx, const jschar *s, const jschar **ep, jsint base, j
if (digit >= (uintN)base)
break;
value = value * base + digit;
s1++;
}
} while (++s1 != send);
if (value >= 9007199254740992.0) {
if (base == 10) {
@ -1114,6 +1121,7 @@ js_strtointeger(JSContext *cx, const jschar *s, const jschar **ep, jsint base, j
/* We don't worry about inaccurate numbers for any other base. */
if (s1 == start) {
no_digits:
*dp = 0.0;
*ep = s;
} else {

View File

@ -242,7 +242,8 @@ js_DoubleToInteger(jsdouble d);
* Return false if out of memory.
*/
extern JSBool
js_strtod(JSContext *cx, const jschar *s, const jschar **ep, jsdouble *dp);
js_strtod(JSContext *cx, const jschar *s, const jschar *send,
const jschar **ep, jsdouble *dp);
/*
* Similar to strtol except that it handles integers of arbitrary size.
@ -254,7 +255,8 @@ js_strtod(JSContext *cx, const jschar *s, const jschar **ep, jsdouble *dp);
* Return false if out of memory.
*/
extern JSBool
js_strtointeger(JSContext *cx, const jschar *s, const jschar **ep, jsint radix, jsdouble *dp);
js_strtointeger(JSContext *cx, const jschar *s, const jschar *send,
const jschar **ep, jsint radix, jsdouble *dp);
JS_END_EXTERN_C

View File

@ -1119,6 +1119,7 @@ js_GetToken(JSContext *cx, JSTokenStream *ts)
/* The following 4 macros should only be used when TOKENBUF_OK() is true. */
#define TOKENBUF_BASE() (ts->tokenbuf.base)
#define TOKENBUF_END() (ts->tokenbuf.ptr)
#define TOKENBUF_CHAR(i) (ts->tokenbuf.base[i])
#define TRIM_TOKENBUF(i) (ts->tokenbuf.ptr = ts->tokenbuf.base + i)
#define NUL_TERM_TOKENBUF() (*ts->tokenbuf.ptr = 0)
@ -1443,14 +1444,16 @@ retry:
if (!TOKENBUF_OK())
goto error;
if (radix == 10) {
if (!js_strtod(cx, TOKENBUF_BASE(), &endptr, &dval)) {
if (!js_strtod(cx, TOKENBUF_BASE(), TOKENBUF_END(),
&endptr, &dval)) {
js_ReportCompileErrorNumber(cx, ts,
JSREPORT_TS | JSREPORT_ERROR,
JSMSG_OUT_OF_MEMORY);
goto error;
}
} else {
if (!js_strtointeger(cx, TOKENBUF_BASE(), &endptr, radix, &dval)) {
if (!js_strtointeger(cx, TOKENBUF_BASE(), TOKENBUF_END(),
&endptr, radix, &dval)) {
js_ReportCompileErrorNumber(cx, ts,
JSREPORT_TS | JSREPORT_ERROR,
JSMSG_OUT_OF_MEMORY);

View File

@ -2861,10 +2861,10 @@ js_strchr_limit(const jschar *s, jschar c, const jschar *limit)
}
const jschar *
js_SkipWhiteSpace(const jschar *s)
js_SkipWhiteSpace(const jschar *s, const jschar *end)
{
/* JS_ISSPACE is false on a null. */
while (JS_ISSPACE(*s))
JS_ASSERT(s <= end);
while (s != end && JS_ISSPACE(*s))
s++;
return s;
}

View File

@ -437,7 +437,7 @@ js_strchr_limit(const jschar *s, jschar c, const jschar *limit);
* Return s advanced past any Unicode white space characters.
*/
extern const jschar *
js_SkipWhiteSpace(const jschar *s);
js_SkipWhiteSpace(const jschar *s, const jschar *end);
/*
* Inflate bytes to JS chars and vice versa. Report out of memory via cx