Ignore:
Timestamp:
Jun 27, 2015, 3:53:12 PM (10 years ago)
Author:
Darin Adler
Message:

Make converting JSString to StringView idiomatically safe
https://bugs.webkit.org/show_bug.cgi?id=146387

Reviewed by Anders Carlsson.

  • jsc.cpp:

(functionPrint): Add explicit call to SafeView::get, needed since there
is no StringView temporary.
(functionDebug): Ditto.

  • runtime/ArrayPrototype.cpp:

(JSC::holesMustForwardToPrototype): Refactored into helper function.
(JSC::join): Refactored so that StringView is a function argument, making
the lifetime simpler.
(JSC::arrayProtoFuncJoin): Ditto.
(JSC::arrayProtoFuncReverse): Use new holesMustForwardToPrototype helper.

  • runtime/JSGlobalObjectFunctions.cpp:

(JSC::encode): Add explicit call to SafeView::get.

  • runtime/JSString.h: Moved declarations of functions to the top of the

file instead of mixing them in with the function definitions. Changed
return type of the view function to return a JSString::SafeView so that
the JSString's lifetime will last as long as the StringView does in
typical coding idioms.
(JSC::JSString::getIndex): Use unsafeView so we can index into the
view; could also have used view.get but here in this class this seems fine.
(JSC::JSRopeString::unsafeView): Renamed existing view function to this.
(JSC::JSString::unsafeView): Ditto.
(JSC::JSString::SafeView::SafeView): Contains reference to an ExecState
and a JSString. The ExecState is needed to create the StringView, and the
JSString needs to be kept alive as long as the StringView is.
(JSC::JSString::SafeView::operator StringView): Call unsafeView.
(JSC::JSString::SafeView::get): Convenience for when we want to call
StringView member functions.
(JSC::JSString::view): Added. Returns a SafeView.

  • runtime/StringPrototype.cpp:

(JSC::stringProtoFuncIndexOf): Add explicit call to SafeView::get.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/runtime/JSString.h

    r185899 r186037  
    4848JSString* jsSubstring(VM*, const String&, unsigned offset, unsigned length);
    4949JSString* jsSubstring(ExecState*, const String&, unsigned offset, unsigned length);
     50JSString* jsSubstring8(VM*, const String&, unsigned offset, unsigned length);
     51JSString* jsSubstring8(ExecState*, const String&, unsigned offset, unsigned length);
    5052
    5153// Non-trivial strings are two or more characters long.
     
    6264
    6365JSRopeString* jsStringBuilder(VM*);
     66
     67bool isJSString(JSValue);
     68JSString* asString(JSValue);
    6469
    6570struct StringViewWithUnderlyingString {
     
    149154    AtomicString toAtomicString(ExecState*) const;
    150155    RefPtr<AtomicStringImpl> toExistingAtomicString(ExecState*) const;
    151     StringView view(ExecState*) const;
     156
     157    class SafeView;
     158    SafeView view(ExecState*) const;
    152159    StringViewWithUnderlyingString viewWithUnderlyingString(ExecState&) const;
     160
    153161    const String& value(ExecState*) const;
    154162    const String& tryGetValue() const;
     
    221229
    222230    String& string() { ASSERT(!isRope()); return m_value; }
     231    StringView unsafeView(ExecState&) const;
    223232
    224233    friend JSValue jsString(ExecState*, JSString*, JSString*);
     
    390399    void resolveRopeInternal16NoSubstring(UChar*) const;
    391400    void clearFibers() const;
    392     StringView view(ExecState*) const;
     401    StringView unsafeView(ExecState&) const;
    393402    StringViewWithUnderlyingString viewWithUnderlyingString(ExecState&) const;
    394403
     
    436445};
    437446
     447class JSString::SafeView {
     448public:
     449    SafeView();
     450    explicit SafeView(ExecState&, const JSString&);
     451    operator StringView() const;
     452    StringView get() const;
     453
     454private:
     455    ExecState* m_state { nullptr };
     456
     457    // The following pointer is marked "volatile" to make the compiler leave it on the stack
     458    // or in a register as long as this object is alive, even after the last use of the pointer.
     459    // That's needed to prevent garbage collecting the string and possibly deleting the block
     460    // with the characters in it, and then using the StringView after that.
     461    const JSString* volatile m_string { nullptr };
     462};
     463
     464JS_EXPORT_PRIVATE JSString* jsStringWithCacheSlowCase(VM&, StringImpl&);
    438465
    439466inline const StringImpl* JSString::tryGetValueImpl() const
     
    441468    return m_value.impl();
    442469}
    443 
    444 JSString* asString(JSValue);
    445470
    446471inline JSString* asString(JSValue value)
     
    512537{
    513538    ASSERT(canGetIndex(i));
    514     return jsSingleCharacterString(exec, view(exec)[i]);
     539    return jsSingleCharacterString(exec, unsafeView(*exec)[i]);
    515540}
    516541
     
    598623inline JSString* jsOwnedString(ExecState* exec, const String& s) { return jsOwnedString(&exec->vm(), s); }
    599624
    600 JS_EXPORT_PRIVATE JSString* jsStringWithCacheSlowCase(VM&, StringImpl&);
    601 
    602625ALWAYS_INLINE JSString* jsStringWithCache(ExecState* exec, const String& s)
    603626{
     
    652675}
    653676
    654 inline bool isJSString(JSValue v) { return v.isCell() && v.asCell()->type() == StringType; }
     677inline bool isJSString(JSValue v)
     678{
     679    return v.isCell() && v.asCell()->type() == StringType;
     680}
     681
     682ALWAYS_INLINE StringView JSRopeString::unsafeView(ExecState& state) const
     683{
     684    if (isSubstring()) {
     685        if (is8Bit())
     686            return StringView(substringBase()->m_value.characters8() + substringOffset(), m_length);
     687        return StringView(substringBase()->m_value.characters16() + substringOffset(), m_length);
     688    }
     689    resolveRope(&state);
     690    return m_value;
     691}
     692
     693ALWAYS_INLINE StringViewWithUnderlyingString JSRopeString::viewWithUnderlyingString(ExecState& state) const
     694{
     695    if (isSubstring()) {
     696        auto& base = substringBase()->m_value;
     697        if (is8Bit())
     698            return { { base.characters8() + substringOffset(), m_length }, base };
     699        return { { base.characters16() + substringOffset(), m_length }, base };
     700    }
     701    resolveRope(&state);
     702    return { m_value, m_value };
     703}
     704
     705ALWAYS_INLINE StringView JSString::unsafeView(ExecState& state) const
     706{
     707    if (isRope())
     708        return static_cast<const JSRopeString*>(this)->unsafeView(state);
     709    return m_value;
     710}
     711
     712ALWAYS_INLINE StringViewWithUnderlyingString JSString::viewWithUnderlyingString(ExecState& state) const
     713{
     714    if (isRope())
     715        return static_cast<const JSRopeString&>(*this).viewWithUnderlyingString(state);
     716    return { m_value, m_value };
     717}
     718
     719inline bool JSString::isSubstring() const
     720{
     721    return isRope() && static_cast<const JSRopeString*>(this)->isSubstring();
     722}
     723
     724inline JSString::SafeView::SafeView()
     725{
     726}
     727
     728inline JSString::SafeView::SafeView(ExecState& state, const JSString& string)
     729    : m_state(&state)
     730    , m_string(&string)
     731{
     732}
     733
     734inline JSString::SafeView::operator StringView() const
     735{
     736    return m_string->unsafeView(*m_state);
     737}
     738
     739inline StringView JSString::SafeView::get() const
     740{
     741    return *this;
     742}
     743
     744ALWAYS_INLINE JSString::SafeView JSString::view(ExecState* exec) const
     745{
     746    return SafeView(*exec, *this);
     747}
    655748
    656749// --- JSValue inlines ----------------------------
     
    681774}
    682775
    683 ALWAYS_INLINE StringView JSRopeString::view(ExecState* exec) const
    684 {
    685     if (isSubstring()) {
    686         if (is8Bit())
    687             return StringView(substringBase()->m_value.characters8() + substringOffset(), m_length);
    688         return StringView(substringBase()->m_value.characters16() + substringOffset(), m_length);
    689     }
    690     resolveRope(exec);
    691     return StringView(m_value);
    692 }
    693 
    694 ALWAYS_INLINE StringViewWithUnderlyingString JSRopeString::viewWithUnderlyingString(ExecState& state) const
    695 {
    696     if (isSubstring()) {
    697         auto& base = substringBase()->m_value;
    698         if (is8Bit())
    699             return { { base.characters8() + substringOffset(), m_length }, base };
    700         return { { base.characters16() + substringOffset(), m_length }, base };
    701     }
    702     resolveRope(&state);
    703     return { m_value, m_value };
    704 }
    705 
    706 ALWAYS_INLINE StringView JSString::view(ExecState* exec) const
    707 {
    708     if (isRope())
    709         return static_cast<const JSRopeString*>(this)->view(exec);
    710     return StringView(m_value);
    711 }
    712 
    713 ALWAYS_INLINE StringViewWithUnderlyingString JSString::viewWithUnderlyingString(ExecState& state) const
    714 {
    715     if (isRope())
    716         return static_cast<const JSRopeString&>(*this).viewWithUnderlyingString(state);
    717     return { m_value, m_value };
    718 }
    719 
    720 inline bool JSString::isSubstring() const
    721 {
    722     return isRope() && static_cast<const JSRopeString*>(this)->isSubstring();
    723 }
    724 
    725776} // namespace JSC
    726777
Note: See TracChangeset for help on using the changeset viewer.