diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 74b56a8bd2b1..46b21460f4f8 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1499,6 +1499,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(); + 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 69be56f4be6e..6ecd50c31826 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1854,6 +1854,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 67ddee95ca01..dea67000199e 100644 --- a/js/src/vm/String.cpp +++ b/js/src/vm/String.cpp @@ -1413,6 +1413,19 @@ NewStringCopyUTF8N(JSContext* cx, const JS::UTF8Chars utf8) template JSFlatString* NewStringCopyUTF8N(JSContext* cx, const JS::UTF8Chars utf8); +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; + } + + *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 0a529fcd4a37..490c16006994 100644 --- a/js/src/vm/String.h +++ b/js/src/vm/String.h @@ -1313,6 +1313,10 @@ NewStringCopyUTF8Z(JSContext* cx, const JS::ConstUTF8CharsZ utf8) return NewStringCopyUTF8N(cx, JS::UTF8Chars(utf8.c_str(), strlen(utf8.c_str()))); } +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 42a57744ea51..006ebe120576 100644 --- a/js/xpconnect/src/XPCString.cpp +++ b/js/xpconnect/src/XPCString.cpp @@ -96,9 +96,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 5115b7ea198f..46fc230f30c7 100644 --- a/js/xpconnect/src/xpcpublic.h +++ b/js/xpconnect/src/xpcpublic.h @@ -267,13 +267,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);