From 623555909f3e4cdd75cd24d2e6c16aea96def8b0 Mon Sep 17 00:00:00 2001 From: Gregor Wagner Date: Fri, 4 Sep 2009 16:28:30 -0700 Subject: [PATCH] Cache result of Number2String (bug 513530, r=brendan). --- js/src/jsapi.cpp | 7 ---- js/src/jsatom.cpp | 27 +++------------- js/src/jscntxt.h | 5 --- js/src/jsgc.cpp | 9 +++--- js/src/jsnum.cpp | 6 ++++ js/src/jsstr.cpp | 74 ++++++++----------------------------------- js/src/jsstr.h | 40 ++++++++--------------- js/src/jsstrinlines.h | 15 +++------ 8 files changed, 47 insertions(+), 136 deletions(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 281186628e90..83d1e1f1ec74 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -867,13 +867,6 @@ JS_DestroyRuntime(JSRuntime *rt) js_FreeRuntimeScriptState(rt); js_FinishAtomState(rt); - /* - * Free unit string storage only after all strings have been finalized, so - * that js_FinalizeString can detect unit strings and avoid calling free - * on their chars storage. - */ - js_FinishUnitStrings(rt); - /* * Finish the deflated string cache after the last GC and after * calling js_FinishAtomState, which finalizes strings. diff --git a/js/src/jsatom.cpp b/js/src/jsatom.cpp index 28f8ead7dc8e..0b7694260336 100644 --- a/js/src/jsatom.cpp +++ b/js/src/jsatom.cpp @@ -566,21 +566,6 @@ js_TraceAtomState(JSTracer *trc, JSBool allAtoms) } else { JS_DHashTableEnumerate(&state->stringAtoms, js_pinned_atom_tracer, trc); } - - if (rt->state != JSRTS_LANDING) { - /* - * Unit strings aren't in state->stringAtoms, so we mark any that have - * been created on demand. This bloats more than strictly necessary but - * we can't help that without putting unit atoms in state->stringAtoms, - * which is too expensive. - */ - for (uintN i = 0; i < UNIT_STRING_LIMIT; i++) { - if (JSString *str = rt->unitStrings[i]) { - JS_SET_TRACING_INDEX(trc, "unit_string_atom", i); - JS_CallTracer(trc, str, JSTRACE_STRING); - } - } - } } static JSDHashOperator @@ -688,10 +673,8 @@ js_AtomizeString(JSContext *cx, JSString *str, uintN flags) if (str->length() == 1) { jschar c = str->chars()[0]; - if (c < UNIT_STRING_LIMIT) { - JSString *str = JSString::getUnitString(cx, c); - return str ? (JSAtom *) STRING_TO_JSVAL(str) : NULL; - } + if (c < UNIT_STRING_LIMIT) + return (JSAtom *) STRING_TO_JSVAL(JSString::unitString(c)); } state = &cx->runtime->atomState; @@ -826,10 +809,8 @@ js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length) if (length == 1) { jschar c = *chars; - if (c < UNIT_STRING_LIMIT) { - JSString *str = JSString::getUnitString(cx, c); - return str ? (JSAtom *) STRING_TO_JSVAL(str) : NULL; - } + if (c < UNIT_STRING_LIMIT) + return (JSAtom *) STRING_TO_JSVAL(JSString::unitString(c)); } str.initFlat((jschar *)chars, length); diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index bf88ee82b18f..1ad64ce50649 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -485,12 +485,7 @@ struct JSRuntime { uint32 deflatedStringCacheBytes; #endif - /* - * Empty and unit-length strings held for use by this runtime's contexts. - * The unitStrings array and its elements are created on demand. - */ JSString *emptyString; - JSString **unitStrings; /* * Builtin functions, lazily created and held for use by the trace recorder. diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 618fcfecb4db..2de09ec53aa7 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -2638,6 +2638,8 @@ JS_CallTracer(JSTracer *trc, void *thing, uint32 kind) case JSTRACE_STRING: for (;;) { + if (JSString::isStatic((JSString *)thing)) + goto out; flagp = THING_TO_FLAGP(thing, sizeof(JSGCThing)); JS_ASSERT((*flagp & GCF_FINAL) == 0); JS_ASSERT(kind == MapGCFlagsToTraceKind(*flagp)); @@ -3233,6 +3235,7 @@ js_FinalizeStringRT(JSRuntime *rt, JSString *str, intN type, JSContext *cx) JSStringFinalizeOp finalizer; JS_RUNTIME_UNMETER(rt, liveStrings); + JS_ASSERT(!JSString::isStatic(str)); if (str->isDependent()) { /* A dependent string can not be external and must be valid. */ JS_ASSERT(type < 0); @@ -3244,11 +3247,7 @@ js_FinalizeStringRT(JSRuntime *rt, JSString *str, intN type, JSContext *cx) chars = str->flatChars(); valid = (chars != NULL); if (valid) { - if (IN_UNIT_STRING_SPACE_RT(rt, chars)) { - JS_ASSERT(rt->unitStrings[*chars] == str); - JS_ASSERT(type < 0); - rt->unitStrings[*chars] = NULL; - } else if (type < 0) { + if (type < 0) { if (cx) cx->free(chars); else diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp index 959fc7ebbd55..fff32bb913ee 100644 --- a/js/src/jsnum.cpp +++ b/js/src/jsnum.cpp @@ -860,6 +860,12 @@ NumberToStringWithBase(JSContext *cx, jsdouble d, jsint base) JSString * JS_FASTCALL js_NumberToString(JSContext *cx, jsdouble d) { + jsint i; + + if (JSDOUBLE_IS_INT(d, i)) { + if (jsuint(i) < INT_STRING_LIMIT) + return &js_IntStrings[i]; + } return NumberToStringWithBase(cx, d, 10); } diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 7ed9e8f7aabf..3dace6cd3a30 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -2391,6 +2391,8 @@ static JSFunctionSpec string_methods[] = { JS_FS_END }; +#include "jsstatic.cpp" + JSBool js_String(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { @@ -2442,7 +2444,7 @@ str_fromCharCode(JSContext *cx, uintN argc, jsval *vp) JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX); if (argc == 1 && (code = js_ValueToUint16(cx, &argv[0])) < UNIT_STRING_LIMIT) { - str = JSString::getUnitString(cx, code); + str = JSString::unitString(code); if (!str) return JS_FALSE; *vp = STRING_TO_JSVAL(str); @@ -2476,7 +2478,7 @@ String_fromCharCode(JSContext* cx, int32 i) JS_ASSERT(JS_ON_TRACE(cx)); jschar c = (jschar)i; if (c < UNIT_STRING_LIMIT) - return JSString::getUnitString(cx, c); + return JSString::unitString(c); return js_NewStringCopyN(cx, &c, 1); } #endif @@ -2528,64 +2530,6 @@ js_InitDeflatedStringCache(JSRuntime *rt) return JS_TRUE; } -JSString * -js_MakeUnitString(JSContext *cx, jschar c) -{ - jschar *cp, i; - JSRuntime *rt; - JSString **sp; - - JS_ASSERT(c < UNIT_STRING_LIMIT); - rt = cx->runtime; - if (!rt->unitStrings) { - sp = (JSString **) js_calloc(UNIT_STRING_LIMIT * sizeof(JSString *) + - UNIT_STRING_LIMIT * 2 * sizeof(jschar)); - if (!sp) { - JS_ReportOutOfMemory(cx); - return NULL; - } - cp = UNIT_STRING_SPACE(sp); - for (i = 0; i < UNIT_STRING_LIMIT; i++) { - *cp = i; - cp += 2; - } - JS_LOCK_GC(rt); - if (!rt->unitStrings) { - rt->unitStrings = sp; - JS_UNLOCK_GC(rt); - } else { - JS_UNLOCK_GC(rt); - js_free(sp); - } - } - if (!rt->unitStrings[c]) { - JSString *str; - - cp = UNIT_STRING_SPACE_RT(rt); - str = js_NewString(cx, cp + 2 * c, 1); - if (!str) - return NULL; - JS_LOCK_GC(rt); - if (!rt->unitStrings[c]) { - str->flatSetAtomized(); - rt->unitStrings[c] = str; - } -#ifdef DEBUG - else - str->initFlat(NULL, 0); /* avoid later assertion (bug 479381) */ -#endif - JS_UNLOCK_GC(rt); - } - return rt->unitStrings[c]; -} - -void -js_FinishUnitStrings(JSRuntime *rt) -{ - js_free(rt->unitStrings); - rt->unitStrings = NULL; -} - void js_FinishRuntimeStringState(JSContext *cx) { @@ -3379,6 +3323,16 @@ js_GetStringBytes(JSContext *cx, JSString *str) JSHashNumber hash; JSHashEntry *he, **hep; + if (JSString::isStatic(str)) { +#ifdef IS_LITTLE_ENDIAN + /* Unit string data is {c, 0, 0, 0} so we can just cast. */ + return (char *)str->chars(); +#else + /* Unit string data is {0, c, 0, 0} so we can point into the middle. */ + return (char *)str->chars() + 1; +#endif + } + if (cx) { rt = cx->runtime; } else { diff --git a/js/src/jsstr.h b/js/src/jsstr.h index 0172e6c66a40..47bd52deeb0c 100644 --- a/js/src/jsstr.h +++ b/js/src/jsstr.h @@ -55,35 +55,22 @@ JS_BEGIN_EXTERN_C -/* - * Maximum character code for which we will create a pinned unit string on - * demand -- see JSRuntime.unitStrings in jscntxt.h. - */ -#define UNIT_STRING_LIMIT 256U - #define JSSTRING_BIT(n) ((size_t)1 << (n)) #define JSSTRING_BITMASK(n) (JSSTRING_BIT(n) - 1) -#define UNIT_STRING_SPACE(sp) ((jschar *) ((sp) + UNIT_STRING_LIMIT)) -#define UNIT_STRING_SPACE_RT(rt) UNIT_STRING_SPACE((rt)->unitStrings) - -#define IN_UNIT_STRING_SPACE(sp,cp) \ - ((size_t)((cp) - UNIT_STRING_SPACE(sp)) < 2 * UNIT_STRING_LIMIT) -#define IN_UNIT_STRING_SPACE_RT(rt,cp) \ - IN_UNIT_STRING_SPACE((rt)->unitStrings, cp) - class TraceRecorder; +enum { + UNIT_STRING_LIMIT = 256U, + INT_STRING_LIMIT = 256U, +}; + +extern JSString js_UnitStrings[]; +extern JSString js_IntStrings[]; + extern jschar * js_GetDependentStringChars(JSString *str); -/* - * Make the independent string containing only the character code c, which must - * be less than UNIT_STRING_LIMIT, and cache it in the runtime. - */ -extern JSString * -js_MakeUnitString(JSContext *cx, jschar c); - /* * The GC-thing "string" type. * @@ -132,7 +119,6 @@ struct JSString { friend JSString * JS_FASTCALL js_ConcatStrings(JSContext *cx, JSString *left, JSString *right); -private: size_t mLength; union { jschar *mChars; @@ -377,8 +363,13 @@ public: JS_ASSERT(isDependent() && dependentIsPrefix()); mBase = bstr; } + + static inline bool isStatic(JSString *s) { + return (s >= js_UnitStrings && s < &js_UnitStrings[UNIT_STRING_LIMIT]) || + (s >= js_IntStrings && s < &js_IntStrings[INT_STRING_LIMIT]); + } - static JSString *getUnitString(JSContext *cx, jschar c); + static JSString *unitString(jschar c); static JSString *getUnitString(JSContext *cx, JSString *str, size_t index); }; @@ -559,9 +550,6 @@ js_InitRuntimeStringState(JSContext *cx); extern JSBool js_InitDeflatedStringCache(JSRuntime *rt); -extern void -js_FinishUnitStrings(JSRuntime *rt); - extern void js_FinishRuntimeStringState(JSContext *cx); diff --git a/js/src/jsstrinlines.h b/js/src/jsstrinlines.h index 6b8595536668..b687b1d3cce0 100644 --- a/js/src/jsstrinlines.h +++ b/js/src/jsstrinlines.h @@ -45,15 +45,10 @@ JS_BEGIN_EXTERN_C inline JSString * -JSString::getUnitString(JSContext *cx, jschar c) +JSString::unitString(jschar c) { JS_ASSERT(c < UNIT_STRING_LIMIT); - JSRuntime *rt = cx->runtime; - JSString **unitStrings = rt->unitStrings; - JSString *ustr; - if (unitStrings && (ustr = unitStrings[c]) != NULL) - return ustr; - return js_MakeUnitString(cx, c); + return js_UnitStrings + c; } inline JSString * @@ -61,9 +56,9 @@ JSString::getUnitString(JSContext *cx, JSString *str, size_t index) { JS_ASSERT(index < str->length()); jschar c = str->chars()[index]; - if (c >= UNIT_STRING_LIMIT) - return js_NewDependentString(cx, str, index, 1); - return getUnitString(cx, c); + if (c < UNIT_STRING_LIMIT) + return unitString(c); + return js_NewDependentString(cx, str, index, 1); } JS_END_EXTERN_C