diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp index f27093684..647de2c20 100644 --- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -1176,7 +1176,7 @@ static const JSFunctionSpec object_methods[] = { JS_FN(js_toString_str, obj_toString, 0,0), JS_SELF_HOSTED_FN(js_toLocaleString_str, "Object_toLocaleString", 0,JSPROP_DEFINE_LATE), JS_FN(js_valueOf_str, obj_valueOf, 0,0), -#if (0) // JS_HAS_OBJ_WATCHPOINT // TenFourFox issue 575 +#if (0) // JS_HAS_OBJ_WATCHPOINT // TenFourFox issue 575 // flip this for confidence testing JS_FN(js_watch_str, obj_watch, 2,0), JS_FN(js_unwatch_str, obj_unwatch, 1,0), #endif diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index bf94da4af..faea25b06 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -1258,7 +1258,11 @@ CheckSetConstOp(JSOp op, ParseNode* pn) switch (op) { case JSOP_GETLOCAL: case JSOP_GETALIASEDVAR: break; case JSOP_INITLEXICAL: case JSOP_INITALIASEDLEXICAL: break; - case JSOP_SETLOCAL: break; /* return JSOP_THROWSETCONST; */ +#if (0) // flip this for confidence testing + case JSOP_SETLOCAL: return JSOP_THROWSETCONST; +#else + case JSOP_SETLOCAL: break; +#endif case JSOP_SETALIASEDVAR: return JSOP_THROWSETALIASEDCONST; default: MOZ_CRASH("unexpected set var op"); } diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index df0a2c7cd..79f793eb2 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -4735,8 +4735,6 @@ JS_NewStringCopyN(JSContext* cx, const char* s, size_t n) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); - if (!n) - return cx->names().empty; return NewStringCopyN(cx, s, n); } @@ -4745,7 +4743,7 @@ JS_NewStringCopyZ(JSContext* cx, const char* s) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); - if (!s || !*s) + if (!s) return cx->runtime()->emptyString; return NewStringCopyZ(cx, s); } diff --git a/js/src/vm/String.cpp b/js/src/vm/String.cpp index 37ad492d4..364d6a0d7 100644 --- a/js/src/vm/String.cpp +++ b/js/src/vm/String.cpp @@ -790,9 +790,11 @@ StaticStrings::init(JSContext* cx) static_assert(UNIT_STATIC_LIMIT - 1 <= JSString::MAX_LATIN1_CHAR, "Unit strings must fit in Latin1Char."); + using Latin1Range = mozilla::Range; + for (uint32_t i = 0; i < UNIT_STATIC_LIMIT; i++) { Latin1Char buffer[] = { Latin1Char(i), '\0' }; - JSFlatString* s = NewStringCopyN(cx, buffer, 1); + JSFlatString* s = NewInlineString(cx, Latin1Range(buffer, 1)); if (!s) return false; HashNumber hash = mozilla::HashString(buffer, 1); @@ -801,7 +803,7 @@ StaticStrings::init(JSContext* cx) for (uint32_t i = 0; i < NUM_SMALL_CHARS * NUM_SMALL_CHARS; i++) { Latin1Char buffer[] = { FROM_SMALL_CHAR(i >> 6), FROM_SMALL_CHAR(i & 0x3F), '\0' }; - JSFlatString* s = NewStringCopyN(cx, buffer, 2); + JSFlatString* s = NewInlineString(cx, Latin1Range(buffer, 2)); if (!s) return false; HashNumber hash = mozilla::HashString(buffer, 2); @@ -820,7 +822,7 @@ StaticStrings::init(JSContext* cx) Latin1Char('0' + ((i / 10) % 10)), Latin1Char('0' + (i % 10)), '\0' }; - JSFlatString* s = NewStringCopyN(cx, buffer, 3); + JSFlatString* s = NewInlineString(cx, Latin1Range(buffer, 3)); if (!s) return false; HashNumber hash = mozilla::HashString(buffer, 3); @@ -1029,10 +1031,31 @@ NewInlineStringDeflated(ExclusiveContext* cx, mozilla::Range cha return str; } +template +static MOZ_ALWAYS_INLINE JSFlatString* +TryEmptyOrStaticString(ExclusiveContext* cx, const CharT* chars, size_t n) +{ + // Measurements on popular websites indicate empty strings are pretty common + // and most strings with length 1 or 2 are in the StaticStrings table. For + // length 3 strings that's only about 1%, so we check n <= 2. + if (n <= 2) { + if (n == 0) + return cx->emptyString(); + + if (JSFlatString* str = cx->staticStrings().lookup(chars, n)) + return str; + } + + return nullptr; +} + template static JSFlatString* NewStringDeflated(ExclusiveContext* cx, const char16_t* s, size_t n) { + if (JSFlatString* str = TryEmptyOrStaticString(cx, s, n)) + return str; + if (JSInlineString::lengthFits(n)) return NewInlineStringDeflated(cx, mozilla::Range(s, n)); @@ -1065,14 +1088,11 @@ template JSFlatString* js::NewStringDontDeflate(ExclusiveContext* cx, CharT* chars, size_t length) { - if (length == 1) { - char16_t c = chars[0]; - if (StaticStrings::hasUnit(c)) { - // Free |chars| because we're taking possession of it, but it's no - // longer needed because we use the static string instead. - js_free(chars); - return cx->staticStrings().getUnit(c); - } + if (JSFlatString* str = TryEmptyOrStaticString(cx, chars, length)) { + // Free |chars| because we're taking possession of it, but it's no + // longer needed because we use the static string instead. + js_free(chars); + return str; } if (JSInlineString::lengthFits(length)) { @@ -1105,14 +1125,6 @@ JSFlatString* js::NewString(ExclusiveContext* cx, CharT* chars, size_t length) { if (IsSame::value && CanStoreCharsAsLatin1(chars, length)) { - if (length == 1) { - char16_t c = chars[0]; - if (StaticStrings::hasUnit(c)) { - js_free(chars); - return cx->staticStrings().getUnit(c); - } - } - JSFlatString* s = NewStringDeflated(cx, chars, length); if (!s) return nullptr; @@ -1143,6 +1155,9 @@ template JSFlatString* NewStringCopyNDontDeflate(ExclusiveContext* cx, const CharT* s, size_t n) { + if (JSFlatString* str = TryEmptyOrStaticString(cx, s, n)) + return str; + if (JSInlineString::lengthFits(n)) return NewInlineString(cx, mozilla::Range(s, n));