Bug 892634 - Part 1: Add thread safe number conversion functions. (r=billm)

This commit is contained in:
Shu-yu Guo 2013-08-02 08:24:57 -07:00
parent 2e435e83b4
commit 811fd3643c
6 changed files with 143 additions and 105 deletions

View File

@ -4038,7 +4038,7 @@ CodeGenerator::visitEmulatesUndefinedAndBranch(LEmulatesUndefinedAndBranch *lir)
return true; return true;
} }
typedef JSString *(*ConcatStringsFn)(JSContext *, HandleString, HandleString); typedef JSString *(*ConcatStringsFn)(ThreadSafeContext *, HandleString, HandleString);
typedef ParallelResult (*ConcatStringsParFn)(ForkJoinSlice *, HandleString, HandleString, typedef ParallelResult (*ConcatStringsParFn)(ForkJoinSlice *, HandleString, HandleString,
MutableHandleString); MutableHandleString);
static const VMFunctionsModal ConcatStringsInfo = VMFunctionsModal( static const VMFunctionsModal ConcatStringsInfo = VMFunctionsModal(

View File

@ -191,7 +191,7 @@ ParallelResult
ion::ConcatStringsPar(ForkJoinSlice *slice, HandleString left, HandleString right, ion::ConcatStringsPar(ForkJoinSlice *slice, HandleString left, HandleString right,
MutableHandleString out) MutableHandleString out)
{ {
JSString *str = ConcatStringsPure(slice, left, right); JSString *str = ConcatStrings<NoGC>(slice, left, right);
if (!str) if (!str)
return TP_RETRY_SEQUENTIALLY; return TP_RETRY_SEQUENTIALLY;
out.set(str); out.set(str);

View File

@ -54,7 +54,7 @@ using mozilla::RangedPtr;
* Call js_strtod_harder to get the correct answer. * Call js_strtod_harder to get the correct answer.
*/ */
static bool static bool
ComputeAccurateDecimalInteger(ExclusiveContext *cx, ComputeAccurateDecimalInteger(ThreadSafeContext *cx,
const jschar *start, const jschar *end, double *dp) const jschar *start, const jschar *end, double *dp)
{ {
size_t length = end - start; size_t length = end - start;
@ -188,7 +188,7 @@ js::ParseDecimalNumber(const JS::TwoByteChars chars)
} }
bool bool
js::GetPrefixInteger(ExclusiveContext *cx, const jschar *start, const jschar *end, int base, js::GetPrefixInteger(ThreadSafeContext *cx, const jschar *start, const jschar *end, int base,
const jschar **endp, double *dp) const jschar **endp, double *dp)
{ {
JS_ASSERT(start <= end); JS_ASSERT(start <= end);
@ -1390,26 +1390,18 @@ js::NumberValueToStringBuffer(JSContext *cx, const Value &v, StringBuffer &sb)
return sb.appendInflated(cstr, cstrlen); return sb.appendInflated(cstr, cstrlen);
} }
bool static void
js::StringToNumber(ExclusiveContext *cx, JSString *str, double *result) CharsToNumber(ThreadSafeContext *cx, const jschar *chars, size_t length, double *result)
{ {
size_t length = str->length();
const jschar *chars = str->getChars(NULL);
if (!chars)
return false;
if (length == 1) { if (length == 1) {
jschar c = chars[0]; jschar c = chars[0];
if ('0' <= c && c <= '9') { if ('0' <= c && c <= '9')
*result = c - '0'; *result = c - '0';
return true; else if (unicode::IsSpace(c))
}
if (unicode::IsSpace(c)) {
*result = 0.0; *result = 0.0;
return true; else
}
*result = js_NaN; *result = js_NaN;
return true; return;
} }
const jschar *end = chars + length; const jschar *end = chars + length;
@ -1429,10 +1421,10 @@ js::StringToNumber(ExclusiveContext *cx, JSString *str, double *result)
SkipSpace(endptr, end) != end) SkipSpace(endptr, end) != end)
{ {
*result = js_NaN; *result = js_NaN;
return true; } else {
}
*result = d; *result = d;
return true; }
return;
} }
/* /*
@ -1444,11 +1436,41 @@ js::StringToNumber(ExclusiveContext *cx, JSString *str, double *result)
*/ */
const jschar *ep; const jschar *ep;
double d; double d;
if (!js_strtod(cx, bp, end, &ep, &d) || SkipSpace(ep, end) != end) { if (!js_strtod(cx, bp, end, &ep, &d) || SkipSpace(ep, end) != end)
*result = js_NaN; *result = js_NaN;
else
*result = d;
}
bool
js::StringToNumber(ThreadSafeContext *cx, JSString *str, double *result)
{
ScopedThreadSafeStringInspector inspector(str);
if (!inspector.ensureChars(cx))
return false;
CharsToNumber(cx, inspector.chars(), str->length(), result);
return true;
}
bool
js::NonObjectToNumberSlow(ThreadSafeContext *cx, Value v, double *out)
{
JS_ASSERT(!v.isNumber());
JS_ASSERT(!v.isObject());
if (v.isString())
return StringToNumber(cx, v.toString(), out);
if (v.isBoolean()) {
*out = v.toBoolean() ? 1.0 : 0.0;
return true; return true;
} }
*result = d; if (v.isNull()) {
*out = 0.0;
return true;
}
JS_ASSERT(v.isUndefined());
*out = js_NaN;
return true; return true;
} }
@ -1485,25 +1507,11 @@ js::ToNumberSlow(ExclusiveContext *cx, Value v, double *out)
*out = v.toNumber(); *out = v.toNumber();
return true; return true;
} }
skip_int_double:
if (v.isString())
return StringToNumber(cx, v.toString(), out);
if (v.isBoolean()) {
if (v.toBoolean()) {
*out = 1.0;
return true;
}
*out = 0.0;
return true;
}
if (v.isNull()) {
*out = 0.0;
return true;
}
if (v.isUndefined())
break;
JS_ASSERT(v.isObject()); skip_int_double:
if (!v.isObject())
return NonObjectToNumberSlow(cx, v, out);
if (!cx->isJSContext()) if (!cx->isJSContext())
return false; return false;
@ -1567,15 +1575,18 @@ js::ToUint64Slow(JSContext *cx, const HandleValue v, uint64_t *out)
return true; return true;
} }
JS_PUBLIC_API(bool) template <typename ContextType,
js::ToInt32Slow(JSContext *cx, const HandleValue v, int32_t *out) bool (*ToNumberSlowFn)(ContextType *, Value, double *),
typename ValueType>
static bool
ToInt32SlowImpl(ContextType *cx, const ValueType v, int32_t *out)
{ {
JS_ASSERT(!v.isInt32()); JS_ASSERT(!v.isInt32());
double d; double d;
if (v.isDouble()) { if (v.isDouble()) {
d = v.toDouble(); d = v.toDouble();
} else { } else {
if (!ToNumberSlow(cx, v, &d)) if (!ToNumberSlowFn(cx, v, &d))
return false; return false;
} }
*out = ToInt32(d); *out = ToInt32(d);
@ -1583,20 +1594,47 @@ js::ToInt32Slow(JSContext *cx, const HandleValue v, int32_t *out)
} }
JS_PUBLIC_API(bool) JS_PUBLIC_API(bool)
js::ToUint32Slow(JSContext *cx, const HandleValue v, uint32_t *out) js::ToInt32Slow(JSContext *cx, const HandleValue v, int32_t *out)
{
return ToInt32SlowImpl<JSContext, ToNumberSlow>(cx, v, out);
}
bool
js::NonObjectToInt32Slow(ThreadSafeContext *cx, const Value &v, int32_t *out)
{
return ToInt32SlowImpl<ThreadSafeContext, NonObjectToNumberSlow>(cx, v, out);
}
template <typename ContextType,
bool (*ToNumberSlowFn)(ContextType *, Value, double *),
typename ValueType>
static bool
ToUint32SlowImpl(ContextType *cx, const ValueType v, uint32_t *out)
{ {
JS_ASSERT(!v.isInt32()); JS_ASSERT(!v.isInt32());
double d; double d;
if (v.isDouble()) { if (v.isDouble()) {
d = v.toDouble(); d = v.toDouble();
} else { } else {
if (!ToNumberSlow(cx, v, &d)) if (!ToNumberSlowFn(cx, v, &d))
return false; return false;
} }
*out = ToUint32(d); *out = ToUint32(d);
return true; return true;
} }
JS_PUBLIC_API(bool)
js::ToUint32Slow(JSContext *cx, const HandleValue v, uint32_t *out)
{
return ToUint32SlowImpl<JSContext, ToNumberSlow>(cx, v, out);
}
bool
js::NonObjectToUint32Slow(ThreadSafeContext *cx, const Value &v, uint32_t *out)
{
return ToUint32SlowImpl<ThreadSafeContext, NonObjectToNumberSlow>(cx, v, out);
}
JS_PUBLIC_API(bool) JS_PUBLIC_API(bool)
js::ToUint16Slow(JSContext *cx, const HandleValue v, uint16_t *out) js::ToUint16Slow(JSContext *cx, const HandleValue v, uint16_t *out)
{ {
@ -1631,7 +1669,7 @@ js::ToUint16Slow(JSContext *cx, const HandleValue v, uint16_t *out)
} }
JSBool JSBool
js_strtod(ExclusiveContext *cx, const jschar *s, const jschar *send, js_strtod(ThreadSafeContext *cx, const jschar *s, const jschar *send,
const jschar **ep, double *dp) const jschar **ep, double *dp)
{ {
size_t i; size_t i;

View File

@ -128,7 +128,7 @@ ParseDecimalNumber(const JS::TwoByteChars chars);
* *dp == 0 and *endp == start upon return. * *dp == 0 and *endp == start upon return.
*/ */
extern bool extern bool
GetPrefixInteger(ExclusiveContext *cx, const jschar *start, const jschar *end, int base, GetPrefixInteger(ThreadSafeContext *cx, const jschar *start, const jschar *end, int base,
const jschar **endp, double *dp); const jschar **endp, double *dp);
/* /*
@ -140,7 +140,7 @@ extern bool
GetDecimalInteger(ExclusiveContext *cx, const jschar *start, const jschar *end, double *dp); GetDecimalInteger(ExclusiveContext *cx, const jschar *start, const jschar *end, double *dp);
extern bool extern bool
StringToNumber(ExclusiveContext *cx, JSString *str, double *result); StringToNumber(ThreadSafeContext *cx, JSString *str, double *result);
/* ES5 9.3 ToNumber, overwriting *vp with the appropriate number value. */ /* ES5 9.3 ToNumber, overwriting *vp with the appropriate number value. */
JS_ALWAYS_INLINE bool JS_ALWAYS_INLINE bool
@ -177,7 +177,7 @@ num_parseInt(JSContext *cx, unsigned argc, Value *vp);
* Return false if out of memory. * Return false if out of memory.
*/ */
extern JSBool extern JSBool
js_strtod(js::ExclusiveContext *cx, const jschar *s, const jschar *send, js_strtod(js::ThreadSafeContext *cx, const jschar *s, const jschar *send,
const jschar **ep, double *dp); const jschar **ep, double *dp);
extern JSBool extern JSBool
@ -289,6 +289,49 @@ ToNumber(ExclusiveContext *cx, const Value &v, double *out)
return ToNumberSlow(cx, v, out); return ToNumberSlow(cx, v, out);
} }
/*
* Thread safe variants of number conversion functions.
*/
bool
NonObjectToNumberSlow(ThreadSafeContext *cx, Value v, double *out);
inline bool
NonObjectToNumber(ThreadSafeContext *cx, const Value &v, double *out)
{
if (v.isNumber()) {
*out = v.toNumber();
return true;
}
return NonObjectToNumberSlow(cx, v, out);
}
bool
NonObjectToInt32Slow(ThreadSafeContext *cx, const Value &v, int32_t *out);
inline bool
NonObjectToInt32(ThreadSafeContext *cx, const Value &v, int32_t *out)
{
if (v.isInt32()) {
*out = v.toInt32();
return true;
}
return NonObjectToInt32Slow(cx, v, out);
}
bool
NonObjectToUint32Slow(ThreadSafeContext *cx, const Value &v, uint32_t *out);
JS_ALWAYS_INLINE bool
NonObjectToUint32(ThreadSafeContext *cx, const Value &v, uint32_t *out)
{
if (v.isInt32()) {
*out = uint32_t(v.toInt32());
return true;
}
return NonObjectToUint32Slow(cx, v, out);
}
} /* namespace js */ } /* namespace js */
#endif /* jsnum_h */ #endif /* jsnum_h */

View File

@ -44,13 +44,10 @@ class RopeBuilder;
template <AllowGC allowGC> template <AllowGC allowGC>
extern JSString * extern JSString *
ConcatStrings(JSContext *cx, ConcatStrings(ThreadSafeContext *cx,
typename MaybeRooted<JSString*, allowGC>::HandleType left, typename MaybeRooted<JSString*, allowGC>::HandleType left,
typename MaybeRooted<JSString*, allowGC>::HandleType right); typename MaybeRooted<JSString*, allowGC>::HandleType right);
extern JSString *
ConcatStringsPure(ThreadSafeContext *cx, JSString *left, JSString *right);
// Return s advanced past any Unicode white space characters. // Return s advanced past any Unicode white space characters.
static inline const jschar * static inline const jschar *
SkipSpace(const jschar *s, const jschar *end) SkipSpace(const jschar *s, const jschar *end)

View File

@ -401,7 +401,7 @@ JSRope::flatten(JSContext *maybecx)
template <AllowGC allowGC> template <AllowGC allowGC>
JSString * JSString *
js::ConcatStrings(JSContext *cx, js::ConcatStrings(ThreadSafeContext *cx,
typename MaybeRooted<JSString*, allowGC>::HandleType left, typename MaybeRooted<JSString*, allowGC>::HandleType left,
typename MaybeRooted<JSString*, allowGC>::HandleType right) typename MaybeRooted<JSString*, allowGC>::HandleType right)
{ {
@ -424,16 +424,16 @@ js::ConcatStrings(JSContext *cx,
JSShortString *str = js_NewGCShortString<allowGC>(cx); JSShortString *str = js_NewGCShortString<allowGC>(cx);
if (!str) if (!str)
return NULL; return NULL;
const jschar *leftChars = left->getChars(cx);
if (!leftChars) ScopedThreadSafeStringInspector leftInspector(left);
return NULL; ScopedThreadSafeStringInspector rightInspector(right);
const jschar *rightChars = right->getChars(cx); if (!leftInspector.ensureChars(cx) || !rightInspector.ensureChars(cx))
if (!rightChars)
return NULL; return NULL;
jschar *buf = str->init(wholeLength); jschar *buf = str->init(wholeLength);
PodCopy(buf, leftChars, leftLen); PodCopy(buf, leftInspector.chars(), leftLen);
PodCopy(buf + leftLen, rightChars, rightLen); PodCopy(buf + leftLen, rightInspector.chars(), rightLen);
buf[wholeLength] = 0; buf[wholeLength] = 0;
return str; return str;
} }
@ -442,50 +442,10 @@ js::ConcatStrings(JSContext *cx,
} }
template JSString * template JSString *
js::ConcatStrings<CanGC>(JSContext *cx, HandleString left, HandleString right); js::ConcatStrings<CanGC>(ThreadSafeContext *cx, HandleString left, HandleString right);
template JSString * template JSString *
js::ConcatStrings<NoGC>(JSContext *cx, JSString *left, JSString *right); js::ConcatStrings<NoGC>(ThreadSafeContext *cx, JSString *left, JSString *right);
JSString *
js::ConcatStringsPure(ThreadSafeContext *cx, JSString *left, JSString *right)
{
JS_ASSERT_IF(!left->isAtom(), cx->isInsideCurrentZone(left));
JS_ASSERT_IF(!right->isAtom(), cx->isInsideCurrentZone(right));
size_t leftLen = left->length();
if (leftLen == 0)
return right;
size_t rightLen = right->length();
if (rightLen == 0)
return left;
size_t wholeLength = leftLen + rightLen;
if (!JSString::validateLength(NULL, wholeLength))
return NULL;
if (JSShortString::lengthFits(wholeLength)) {
JSShortString *str = js_NewGCShortString<NoGC>(cx);
if (!str)
return NULL;
jschar *buf = str->init(wholeLength);
ScopedThreadSafeStringInspector leftInspector(left);
ScopedThreadSafeStringInspector rightInspector(right);
if (!leftInspector.ensureChars(cx) || !rightInspector.ensureChars(cx))
return NULL;
PodCopy(buf, leftInspector.chars(), leftLen);
PodCopy(buf + leftLen, rightInspector.chars(), rightLen);
buf[wholeLength] = 0;
return str;
}
return JSRope::new_<NoGC>(cx, left, right, wholeLength);
}
bool bool
JSDependentString::getCharsZNonDestructive(ThreadSafeContext *cx, ScopedJSFreePtr<jschar> &out) const JSDependentString::getCharsZNonDestructive(ThreadSafeContext *cx, ScopedJSFreePtr<jschar> &out) const