diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 79f793eb2..a59dac231 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1535,6 +1535,15 @@ JS_NewExternalString(JSContext* cx, const char16_t* chars, size_t length, return s; } +JS_PUBLIC_API(JSString*) +JS_NewMaybeExternalString(JSContext* cx, const char16_t* chars, size_t length, + const JSStringFinalizer* fin, bool* isExternal) +{ + AssertHeapIsIdle(cx); + CHECK_REQUEST(cx); + return NewMaybeExternalString(cx, chars, length, fin, isExternal); +} + extern JS_PUBLIC_API(bool) JS_IsExternalString(JSString* str) { diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 7cf2dfc45..233698e34 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1820,6 +1820,17 @@ extern JS_PUBLIC_API(JSString*) JS_NewExternalString(JSContext* cx, const char16_t* chars, size_t length, const JSStringFinalizer* fin); +/** + * Create a new JSString whose chars member may refer to external memory. + * If the returned string refers to the external memory, |*isExternal| is set + * to true. Otherwise the returned string is not an external string and + * |*isExternal| is set to false. If |*isExternal| is false, |fin| won't be + * called. + */ +extern JS_PUBLIC_API(JSString*) +JS_NewMaybeExternalString(JSContext* cx, const char16_t* chars, size_t length, + const JSStringFinalizer* fin, bool* isExternal); + /** * Return whether 'str' was created with JS_NewExternalString or * JS_NewExternalStringWithClosure. diff --git a/js/src/vm/String.cpp b/js/src/vm/String.cpp index 364d6a0d7..ca743948c 100644 --- a/js/src/vm/String.cpp +++ b/js/src/vm/String.cpp @@ -1213,6 +1213,24 @@ NewStringCopyN(ExclusiveContext* cx, const Latin1Char* s, size_t n); template JSFlatString* NewStringCopyN(ExclusiveContext* cx, const Latin1Char* s, size_t n); +JSString* +NewMaybeExternalString(JSContext* cx, const char16_t* s, size_t n, const JSStringFinalizer* fin, + bool* isExternal) +{ + if (JSString* str = TryEmptyOrStaticString(cx, s, n)) { + *isExternal = false; + return str; + } + + if (JSThinInlineString::lengthFits(n) && CanStoreCharsAsLatin1(s, n)) { + *isExternal = false; + return NewInlineStringDeflated(cx, mozilla::Range(s, n)); + } + + *isExternal = true; + return JSExternalString::new_(cx, s, n, fin); +} + } /* namespace js */ #ifdef DEBUG diff --git a/js/src/vm/String.h b/js/src/vm/String.h index 8be095513..39d6fda01 100644 --- a/js/src/vm/String.h +++ b/js/src/vm/String.h @@ -1267,6 +1267,10 @@ NewStringCopyZ(js::ExclusiveContext* cx, const char* s) return NewStringCopyN(cx, s, strlen(s)); } +JSString* +NewMaybeExternalString(JSContext* cx, const char16_t* s, size_t n, const JSStringFinalizer* fin, + bool* isExternal); + JS_STATIC_ASSERT(sizeof(HashNumber) == 4); } /* namespace js */ diff --git a/js/xpconnect/src/XPCString.cpp b/js/xpconnect/src/XPCString.cpp index 511e8cb83..c5b86ee4a 100644 --- a/js/xpconnect/src/XPCString.cpp +++ b/js/xpconnect/src/XPCString.cpp @@ -80,9 +80,10 @@ XPCStringConvert::ReadableToJSVal(JSContext* cx, uint32_t length = readable.Length(); if (readable.IsLiteral()) { - JSString* str = JS_NewExternalString(cx, - static_cast(readable.BeginReading()), - length, &sLiteralFinalizer); + bool ignored; + JSString* str = JS_NewMaybeExternalString(cx, + static_cast(readable.BeginReading()), + length, &sLiteralFinalizer, &ignored); if (!str) return false; vp.setString(str); diff --git a/js/xpconnect/src/xpcpublic.h b/js/xpconnect/src/xpcpublic.h index 9d2c644e8..25bc9e0d6 100644 --- a/js/xpconnect/src/xpcpublic.h +++ b/js/xpconnect/src/xpcpublic.h @@ -237,13 +237,22 @@ public: return true; } - JSString* str = JS_NewExternalString(cx, - static_cast(buf->Data()), - length, &sDOMStringFinalizer); + bool isExternal; + JSString* str = JS_NewMaybeExternalString(cx, + static_cast(buf->Data()), + length, &sDOMStringFinalizer, &isExternal); if (!str) { return false; } rval.setString(str); + + // If JS_NewMaybeExternalString returns non-external string, finalizer + // won't be called. Do not store it to cache. + if (!isExternal) { + *sharedBuffer = false; + return true; + } + if (!cache) { cache = new ZoneStringCache(); JS_SetZoneUserData(zone, cache);