diff --git a/js/src/jsapi-tests/Makefile.in b/js/src/jsapi-tests/Makefile.in index ebf14d5f65ac..52f3ca35574d 100644 --- a/js/src/jsapi-tests/Makefile.in +++ b/js/src/jsapi-tests/Makefile.in @@ -64,6 +64,7 @@ CPPSRCS = \ testFunctionProperties.cpp \ testGCChunkAlloc.cpp \ testGetPropertyDefault.cpp \ + testIndexToString.cpp \ testIntString.cpp \ testIntern.cpp \ testLookup.cpp \ diff --git a/js/src/jsapi-tests/testIndexToString.cpp b/js/src/jsapi-tests/testIndexToString.cpp new file mode 100644 index 000000000000..2fb4dac78a89 --- /dev/null +++ b/js/src/jsapi-tests/testIndexToString.cpp @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99: + */ + +#include "tests.h" + +#include "jsnum.h" + +#include "vm/String.h" + +BEGIN_TEST(testIndexToString) +{ + struct TestPair { + uint32 num; + const char *expected; + } tests[] = { + { 0, "0" }, + { 1, "1" }, + { 2, "2" }, + { 9, "9" }, + { 10, "10" }, + { 15, "15" }, + { 16, "16" }, + { 17, "17" }, + { 99, "99" }, + { 100, "100" }, + { 255, "255" }, + { 256, "256" }, + { 257, "257" }, + { 999, "999" }, + { 1000, "1000" }, + { 4095, "4095" }, + { 4096, "4096" }, + { 9999, "9999" }, + { 1073741823, "1073741823" }, + { 1073741824, "1073741824" }, + { 1073741825, "1073741825" }, + { 2147483647, "2147483647" }, + { 2147483648, "2147483648" }, + { 2147483649, "2147483649" }, + { 4294967294, "4294967294" }, + { 4294967295, "4294967295" }, + }; + + for (size_t i = 0, sz = JS_ARRAY_LENGTH(tests); i < sz; i++) { + JSString *str = js::IndexToString(cx, tests[i].num); + CHECK(str); + + JSBool match = JS_FALSE; + CHECK(JS_StringEqualsAscii(cx, str, tests[i].expected, &match)); + CHECK(match); + } + + return true; +} +END_TEST(testIndexToString) diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp index 185ee13db445..ea341a37399b 100644 --- a/js/src/jsnum.cpp +++ b/js/src/jsnum.cpp @@ -51,6 +51,9 @@ #include #include #include + +#include "mozilla/RangedPtr.h" + #include "jstypes.h" #include "jsstdint.h" #include "jsutil.h" @@ -80,6 +83,7 @@ #include "vm/String-inl.h" using namespace js; +using namespace mozilla; #ifndef JS_HAVE_STDINT_H /* Native support is innocent until proven guilty. */ @@ -1264,6 +1268,41 @@ NumberToString(JSContext *cx, jsdouble d) return NULL; } +JSFixedString * +IndexToString(JSContext *cx, uint32 u) +{ + if (JSAtom::hasUintStatic(u)) + return &JSAtom::uintStatic(u); + + JSCompartment *c = cx->compartment; + if (JSFixedString *str = c->dtoaCache.lookup(10, u)) + return str; + + JSShortString *str = js_NewGCShortString(cx); + if (!str) + return NULL; + + /* +1, since MAX_LENGTH does not count the null char. */ + JS_STATIC_ASSERT(JSShortString::MAX_LENGTH + 1 >= sizeof("4294967295")); + + jschar *storage = str->inlineStorageBeforeInit(); + size_t length = JSShortString::MAX_SHORT_LENGTH; + const RangedPtr end(storage + length, storage, length + 1); + RangedPtr cp = end; + *cp = '\0'; + + do { + jsuint newu = u / 10, digit = u % 10; + *--cp = '0' + digit; + u = newu; + } while (u > 0); + + str->initAtOffsetInBuffer(cp.get(), end - cp); + + c->dtoaCache.cache(10, u, str); + return str; +} + bool JS_FASTCALL NumberValueToStringBuffer(JSContext *cx, const Value &v, StringBuffer &sb) { diff --git a/js/src/jsnum.h b/js/src/jsnum.h index 59d25d6bb300..792968bf0a0e 100644 --- a/js/src/jsnum.h +++ b/js/src/jsnum.h @@ -208,6 +208,9 @@ NumberValueToStringBuffer(JSContext *cx, const Value &v, StringBuffer &sb); extern JSFixedString * NumberToString(JSContext *cx, jsdouble d); +extern JSFixedString * +IndexToString(JSContext *cx, uint32 index); + /* * Usually a small amount of static storage is enough, but sometimes we need * to dynamically allocate much more. This struct encapsulates that. diff --git a/js/src/vm/String-inl.h b/js/src/vm/String-inl.h index 749d188d954a..434b92b2a3b3 100644 --- a/js/src/vm/String-inl.h +++ b/js/src/vm/String-inl.h @@ -197,6 +197,19 @@ JSAtom::unitStatic(jschar c) return (JSStaticAtom &)unitStaticTable[c]; } +inline bool +JSAtom::hasUintStatic(uint32 u) +{ + return u < INT_STATIC_LIMIT; +} + +inline JSStaticAtom & +JSAtom::uintStatic(uint32 u) +{ + JS_ASSERT(hasUintStatic(u)); + return *reinterpret_cast(const_cast(intStaticTable[u])); +} + inline bool JSAtom::hasIntStatic(int32 i) { @@ -207,7 +220,7 @@ inline JSStaticAtom & JSAtom::intStatic(jsint i) { JS_ASSERT(hasIntStatic(i)); - return (JSStaticAtom &)*intStaticTable[i]; + return uintStatic(uint32(i)); } inline JSLinearString * diff --git a/js/src/vm/String.h b/js/src/vm/String.h index 53fd82f8a645..c47f89e38c38 100644 --- a/js/src/vm/String.h +++ b/js/src/vm/String.h @@ -660,6 +660,9 @@ class JSAtom : public JSFixedString */ static inline bool isStatic(const void *ptr); + static inline bool hasUintStatic(uint32 u); + static inline JSStaticAtom &uintStatic(uint32 u); + static inline bool hasIntStatic(int32 i); static inline JSStaticAtom &intStatic(jsint i);