[JSC] Store bits for JSRopeString in 3 stores
[WebKit-https.git] / Source / JavaScriptCore / runtime / JSString.cpp
index a982f19..c836fa2 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  Copyright (C) 1999-2002 Harri Porten (porten@kde.org)
  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
 /*
  *  Copyright (C) 1999-2002 Harri Porten (porten@kde.org)
  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
- *  Copyright (C) 2004, 2007, 2008 Apple Inc. All rights reserved.
+ *  Copyright (C) 2004-2017 Apple Inc. All rights reserved.
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Library General Public
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Library General Public
 #include "JSGlobalObject.h"
 #include "JSGlobalObjectFunctions.h"
 #include "JSObject.h"
 #include "JSGlobalObject.h"
 #include "JSGlobalObjectFunctions.h"
 #include "JSObject.h"
-#include "Operations.h"
+#include "JSCInlines.h"
 #include "StringObject.h"
 #include "StringPrototype.h"
 #include "StringObject.h"
 #include "StringPrototype.h"
+#include "StrongInlines.h"
 
 namespace JSC {
     
 
 namespace JSC {
     
-static const unsigned substringFromRopeCutoff = 4;
+const ClassInfo JSString::s_info = { "string", nullptr, nullptr, nullptr, CREATE_METHOD_TABLE(JSString) };
 
 
-const ClassInfo JSString::s_info = { "string", 0, 0, 0, CREATE_METHOD_TABLE(JSString) };
+Structure* JSString::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto)
+{
+    return Structure::create(vm, globalObject, proto, TypeInfo(StringType, StructureFlags), info());
+}
+
+JSString* JSString::createEmptyString(VM& vm)
+{
+    JSString* newString = new (NotNull, allocateCell<JSString>(vm.heap)) JSString(vm, *StringImpl::empty());
+    newString->finishCreation(vm);
+    return newString;
+}
+
+template<>
+void JSRopeString::RopeBuilder<RecordOverflow>::expand()
+{
+    RELEASE_ASSERT(!this->hasOverflowed());
+    ASSERT(m_strings.size() == JSRopeString::s_maxInternalRopeLength);
+    static_assert(3 == JSRopeString::s_maxInternalRopeLength, "");
+    ASSERT(m_length);
+    ASSERT(asString(m_strings.at(0))->length());
+    ASSERT(asString(m_strings.at(1))->length());
+    ASSERT(asString(m_strings.at(2))->length());
+
+    JSString* string = JSRopeString::create(m_vm, asString(m_strings.at(0)), asString(m_strings.at(1)), asString(m_strings.at(2)));
+    ASSERT(string->length() == m_length);
+    m_strings.clear();
+    m_strings.append(string);
+}
+
+void JSString::destroy(JSCell* cell)
+{
+    static_cast<JSString*>(cell)->JSString::~JSString();
+}
+
+void JSString::dumpToStream(const JSCell* cell, PrintStream& out)
+{
+    VM& vm = *cell->vm();
+    const JSString* thisObject = jsCast<const JSString*>(cell);
+    out.printf("<%p, %s, [%u], ", thisObject, thisObject->className(vm), thisObject->length());
+    uintptr_t pointer = thisObject->m_fiber;
+    if (pointer & isRopeInPointer)
+        out.printf("[rope]");
+    else {
+        if (WTF::StringImpl* ourImpl = bitwise_cast<StringImpl*>(pointer)) {
+            if (ourImpl->is8Bit())
+                out.printf("[8 %p]", ourImpl->characters8());
+            else
+                out.printf("[16 %p]", ourImpl->characters16());
+        }
+    }
+    out.printf(">");
+}
 
 
-void JSString::RopeBuilder::expand()
+bool JSString::equalSlowCase(ExecState* exec, JSString* other) const
 {
 {
-    ASSERT(m_index == JSString::s_maxInternalRopeLength);
-    JSString* jsString = m_jsString;
-    m_jsString = jsStringBuilder(&m_globalData);
-    m_index = 0;
-    append(jsString);
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+    String str1 = value(exec);
+    String str2 = other->value(exec);
+    RETURN_IF_EXCEPTION(scope, false);
+    return WTF::equal(*str1.impl(), *str2.impl());
 }
 
 }
 
-JSString::~JSString()
+size_t JSString::estimatedSize(JSCell* cell, VM& vm)
 {
 {
-    ASSERT(vptr() == JSGlobalData::jsStringVPtr);
+    JSString* thisObject = asString(cell);
+    uintptr_t pointer = thisObject->m_fiber;
+    if (pointer & isRopeInPointer)
+        return Base::estimatedSize(cell, vm);
+    return Base::estimatedSize(cell, vm) + bitwise_cast<StringImpl*>(pointer)->costDuringGC();
 }
 
 void JSString::visitChildren(JSCell* cell, SlotVisitor& visitor)
 {
 }
 
 void JSString::visitChildren(JSCell* cell, SlotVisitor& visitor)
 {
-    JSString* thisObject = static_cast<JSString*>(cell);
+    JSString* thisObject = asString(cell);
     Base::visitChildren(thisObject, visitor);
     Base::visitChildren(thisObject, visitor);
-    for (size_t i = 0; i < s_maxInternalRopeLength && thisObject->m_fibers[i]; ++i)
-        visitor.append(&thisObject->m_fibers[i]);
+    
+    uintptr_t pointer = thisObject->m_fiber;
+    if (pointer & isRopeInPointer) {
+        if (pointer & JSRopeString::isSubstringInPointer) {
+            visitor.appendUnbarriered(static_cast<JSRopeString*>(thisObject)->fiber1());
+            return;
+        }
+        for (unsigned index = 0; index < JSRopeString::s_maxInternalRopeLength; ++index) {
+            JSString* fiber = nullptr;
+            switch (index) {
+            case 0:
+                fiber = bitwise_cast<JSString*>(pointer & JSRopeString::stringMask);
+                break;
+            case 1:
+                fiber = static_cast<JSRopeString*>(thisObject)->fiber1();
+                break;
+            case 2:
+                fiber = static_cast<JSRopeString*>(thisObject)->fiber2();
+                break;
+            default:
+                ASSERT_NOT_REACHED();
+                return;
+            }
+            if (!fiber)
+                break;
+            visitor.appendUnbarriered(fiber);
+        }
+        return;
+    }
+    if (StringImpl* impl = bitwise_cast<StringImpl*>(pointer))
+        visitor.reportExtraMemoryVisited(impl->costDuringGC());
 }
 
 }
 
-void JSString::resolveRope(ExecState* exec) const
+static const unsigned maxLengthForOnStackResolve = 2048;
+
+void JSRopeString::resolveRopeInternal8(LChar* buffer) const
 {
 {
-    ASSERT(isRope());
+    if (isSubstring()) {
+        StringImpl::copyCharacters(buffer, substringBase()->valueInternal().characters8() + substringOffset(), length());
+        return;
+    }
+    
+    resolveRopeInternal8NoSubstring(buffer);
+}
 
 
-    UChar* buffer;
-    if (PassRefPtr<StringImpl> newImpl = StringImpl::tryCreateUninitialized(m_length, buffer))
-        m_value = newImpl;
-    else {
-        outOfMemory(exec);
+void JSRopeString::resolveRopeInternal8NoSubstring(LChar* buffer) const
+{
+    for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i) {
+        if (fiber(i)->isRope()) {
+            resolveRopeSlowCase8(buffer);
+            return;
+        }
+    }
+
+    LChar* position = buffer;
+    for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i) {
+        const StringImpl& fiberString = *fiber(i)->valueInternal().impl();
+        unsigned length = fiberString.length();
+        StringImpl::copyCharacters(position, fiberString.characters8(), length);
+        position += length;
+    }
+    ASSERT((buffer + length()) == position);
+}
+
+void JSRopeString::resolveRopeInternal16(UChar* buffer) const
+{
+    if (isSubstring()) {
+        StringImpl::copyCharacters(
+            buffer, substringBase()->valueInternal().characters16() + substringOffset(), length());
         return;
     }
         return;
     }
+    
+    resolveRopeInternal16NoSubstring(buffer);
+}
 
 
-    for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) {
-        if (m_fibers[i]->isRope())
-            return resolveRopeSlowCase(exec, buffer);
+void JSRopeString::resolveRopeInternal16NoSubstring(UChar* buffer) const
+{
+    for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i) {
+        if (fiber(i)->isRope()) {
+            resolveRopeSlowCase(buffer);
+            return;
+        }
     }
 
     UChar* position = buffer;
     }
 
     UChar* position = buffer;
-    for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) {
-        StringImpl* string = m_fibers[i]->m_value.impl();
-        unsigned length = string->length();
-        StringImpl::copyChars(position, string->characters16(), length);
+    for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i) {
+        const StringImpl& fiberString = *fiber(i)->valueInternal().impl();
+        unsigned length = fiberString.length();
+        if (fiberString.is8Bit())
+            StringImpl::copyCharacters(position, fiberString.characters8(), length);
+        else
+            StringImpl::copyCharacters(position, fiberString.characters16(), length);
         position += length;
         position += length;
-        m_fibers[i].clear();
     }
     }
-    ASSERT((buffer + m_length) == position);
-    ASSERT(!isRope());
+    ASSERT((buffer + length()) == position);
+}
+
+AtomicString JSRopeString::resolveRopeToAtomicString(ExecState* exec) const
+{
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    if (length() > maxLengthForOnStackResolve) {
+        scope.release();
+        return resolveRopeWithFunction(exec, [&] (Ref<StringImpl>&& newImpl) {
+            return AtomicStringImpl::add(newImpl.ptr());
+        });
+    }
+
+    if (is8Bit()) {
+        LChar buffer[maxLengthForOnStackResolve];
+        resolveRopeInternal8(buffer);
+        convertToNonRope(AtomicStringImpl::add(buffer, length()));
+    } else {
+        UChar buffer[maxLengthForOnStackResolve];
+        resolveRopeInternal16(buffer);
+        convertToNonRope(AtomicStringImpl::add(buffer, length()));
+    }
+
+    // If we resolved a string that didn't previously exist, notify the heap that we've grown.
+    if (valueInternal().impl()->hasOneRef())
+        vm.heap.reportExtraMemoryAllocated(valueInternal().impl()->cost());
+    return valueInternal();
 }
 
 }
 
-// Overview: this methods converts a JSString from holding a string in rope form
-// down to a simple UString representation.  It does so by building up the string
+inline void JSRopeString::convertToNonRope(String&& string) const
+{
+    // Concurrent compiler threads can access String held by JSString. So we always emit
+    // store-store barrier here to ensure concurrent compiler threads see initialized String.
+    ASSERT(JSString::isRope());
+    WTF::storeStoreFence();
+    new (&uninitializedValueInternal()) String(WTFMove(string));
+    static_assert(sizeof(String) == sizeof(RefPtr<StringImpl>), "JSString's String initialization must be done in one pointer move.");
+    // We do not clear the trailing fibers and length information (fiber1 and fiber2) because we could be reading the length concurrently.
+    ASSERT(!JSString::isRope());
+}
+
+RefPtr<AtomicStringImpl> JSRopeString::resolveRopeToExistingAtomicString(ExecState* exec) const
+{
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    if (length() > maxLengthForOnStackResolve) {
+        RefPtr<AtomicStringImpl> existingAtomicString;
+        resolveRopeWithFunction(exec, [&] (Ref<StringImpl>&& newImpl) -> Ref<StringImpl> {
+            existingAtomicString = AtomicStringImpl::lookUp(newImpl.ptr());
+            if (existingAtomicString)
+                return makeRef(*existingAtomicString);
+            return WTFMove(newImpl);
+        });
+        RETURN_IF_EXCEPTION(scope, nullptr);
+        return existingAtomicString;
+    }
+    
+    if (is8Bit()) {
+        LChar buffer[maxLengthForOnStackResolve];
+        resolveRopeInternal8(buffer);
+        if (RefPtr<AtomicStringImpl> existingAtomicString = AtomicStringImpl::lookUp(buffer, length())) {
+            convertToNonRope(*existingAtomicString);
+            return existingAtomicString;
+        }
+    } else {
+        UChar buffer[maxLengthForOnStackResolve];
+        resolveRopeInternal16(buffer);
+        if (RefPtr<AtomicStringImpl> existingAtomicString = AtomicStringImpl::lookUp(buffer, length())) {
+            convertToNonRope(*existingAtomicString);
+            return existingAtomicString;
+        }
+    }
+
+    return nullptr;
+}
+
+template<typename Function>
+const String& JSRopeString::resolveRopeWithFunction(ExecState* nullOrExecForOOM, Function&& function) const
+{
+    ASSERT(isRope());
+    
+    VM& vm = *this->vm();
+    if (isSubstring()) {
+        ASSERT(!substringBase()->isRope());
+        auto newImpl = substringBase()->valueInternal().substringSharingImpl(substringOffset(), length());
+        convertToNonRope(function(newImpl.releaseImpl().releaseNonNull()));
+        return valueInternal();
+    }
+    
+    if (is8Bit()) {
+        LChar* buffer;
+        auto newImpl = StringImpl::tryCreateUninitialized(length(), buffer);
+        if (!newImpl) {
+            outOfMemory(nullOrExecForOOM);
+            return nullString();
+        }
+        vm.heap.reportExtraMemoryAllocated(newImpl->cost());
+
+        resolveRopeInternal8NoSubstring(buffer);
+        convertToNonRope(function(newImpl.releaseNonNull()));
+        return valueInternal();
+    }
+    
+    UChar* buffer;
+    auto newImpl = StringImpl::tryCreateUninitialized(length(), buffer);
+    if (!newImpl) {
+        outOfMemory(nullOrExecForOOM);
+        return nullString();
+    }
+    vm.heap.reportExtraMemoryAllocated(newImpl->cost());
+    
+    resolveRopeInternal16NoSubstring(buffer);
+    convertToNonRope(function(newImpl.releaseNonNull()));
+    return valueInternal();
+}
+
+const String& JSRopeString::resolveRope(ExecState* nullOrExecForOOM) const
+{
+    return resolveRopeWithFunction(nullOrExecForOOM, [] (Ref<StringImpl>&& newImpl) {
+        return WTFMove(newImpl);
+    });
+}
+
+// Overview: These functions convert a JSString from holding a string in rope form
+// down to a simple String representation. It does so by building up the string
 // backwards, since we want to avoid recursion, we expect that the tree structure
 // representing the rope is likely imbalanced with more nodes down the left side
 // (since appending to the string is likely more common) - and as such resolving
 // backwards, since we want to avoid recursion, we expect that the tree structure
 // representing the rope is likely imbalanced with more nodes down the left side
 // (since appending to the string is likely more common) - and as such resolving
@@ -96,65 +340,95 @@ void JSString::resolveRope(ExecState* exec) const
 // we would likely have to place all of the constituent StringImpls into the
 // Vector before performing any concatenation, but by working backwards we likely
 // only fill the queue with the number of substrings at any given level in a
 // we would likely have to place all of the constituent StringImpls into the
 // Vector before performing any concatenation, but by working backwards we likely
 // only fill the queue with the number of substrings at any given level in a
-// rope-of-ropes.)    
-void JSString::resolveRopeSlowCase(ExecState* exec, UChar* buffer) const
+// rope-of-ropes.)
+void JSRopeString::resolveRopeSlowCase8(LChar* buffer) const
 {
 {
-    UNUSED_PARAM(exec);
-
-    UChar* position = buffer + m_length; // We will be working backwards over the rope.
-    Vector<JSString*, 32> workQueue; // These strings are kept alive by the parent rope, so using a Vector is OK.
+    LChar* position = buffer + length(); // We will be working backwards over the rope.
+    Vector<JSString*, 32, UnsafeVectorOverflow> workQueue; // Putting strings into a Vector is only OK because there are no GC points in this method.
     
     
-    for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i)
-        workQueue.append(m_fibers[i].get());
+    for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i)
+        workQueue.append(fiber(i));
 
     while (!workQueue.isEmpty()) {
         JSString* currentFiber = workQueue.last();
         workQueue.removeLast();
 
 
     while (!workQueue.isEmpty()) {
         JSString* currentFiber = workQueue.last();
         workQueue.removeLast();
 
+        const LChar* characters;
+        
         if (currentFiber->isRope()) {
         if (currentFiber->isRope()) {
-            for (size_t i = 0; i < s_maxInternalRopeLength && currentFiber->m_fibers[i]; ++i)
-                workQueue.append(currentFiber->m_fibers[i].get());
-            continue;
-        }
-
-        StringImpl* string = static_cast<StringImpl*>(currentFiber->m_value.impl());
-        unsigned length = string->length();
+            JSRopeString* currentFiberAsRope = static_cast<JSRopeString*>(currentFiber);
+            if (!currentFiberAsRope->isSubstring()) {
+                for (size_t i = 0; i < s_maxInternalRopeLength && currentFiberAsRope->fiber(i); ++i)
+                    workQueue.append(currentFiberAsRope->fiber(i));
+                continue;
+            }
+            ASSERT(!currentFiberAsRope->substringBase()->isRope());
+            characters =
+                currentFiberAsRope->substringBase()->valueInternal().characters8() +
+                currentFiberAsRope->substringOffset();
+        } else
+            characters = currentFiber->valueInternal().characters8();
+        
+        unsigned length = currentFiber->length();
         position -= length;
         position -= length;
-        StringImpl::copyChars(position, string->characters16(), length);
+        StringImpl::copyCharacters(position, characters, length);
     }
 
     ASSERT(buffer == position);
     }
 
     ASSERT(buffer == position);
-    ASSERT(!isRope());
 }
 
 }
 
-void JSString::outOfMemory(ExecState* exec) const
+void JSRopeString::resolveRopeSlowCase(UChar* buffer) const
 {
 {
-    for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i)
-        m_fibers[i].clear();
-    ASSERT(!isRope());
-    ASSERT(m_value == UString());
-    if (exec)
-        throwOutOfMemoryError(exec);
-}
+    UChar* position = buffer + length(); // We will be working backwards over the rope.
+    Vector<JSString*, 32, UnsafeVectorOverflow> workQueue; // These strings are kept alive by the parent rope, so using a Vector is OK.
 
 
-JSValue JSString::replaceCharacter(ExecState* exec, UChar character, const UString& replacement)
-{
-    size_t matchPosition = value(exec).find(character);
-    if (matchPosition == notFound)
-        return JSValue(this);
-    return jsString(exec, m_value.substringSharingImpl(0, matchPosition), replacement, value(exec).substringSharingImpl(matchPosition + 1));
+    for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i)
+        workQueue.append(fiber(i));
+
+    while (!workQueue.isEmpty()) {
+        JSString* currentFiber = workQueue.last();
+        workQueue.removeLast();
+
+        if (currentFiber->isRope()) {
+            JSRopeString* currentFiberAsRope = static_cast<JSRopeString*>(currentFiber);
+            if (currentFiberAsRope->isSubstring()) {
+                ASSERT(!currentFiberAsRope->substringBase()->isRope());
+                StringImpl* string = static_cast<StringImpl*>(
+                    currentFiberAsRope->substringBase()->valueInternal().impl());
+                unsigned offset = currentFiberAsRope->substringOffset();
+                unsigned length = currentFiberAsRope->length();
+                position -= length;
+                if (string->is8Bit())
+                    StringImpl::copyCharacters(position, string->characters8() + offset, length);
+                else
+                    StringImpl::copyCharacters(position, string->characters16() + offset, length);
+                continue;
+            }
+            for (size_t i = 0; i < s_maxInternalRopeLength && currentFiberAsRope->fiber(i); ++i)
+                workQueue.append(currentFiberAsRope->fiber(i));
+            continue;
+        }
+
+        StringImpl* string = static_cast<StringImpl*>(currentFiber->valueInternal().impl());
+        unsigned length = string->length();
+        position -= length;
+        if (string->is8Bit())
+            StringImpl::copyCharacters(position, string->characters8(), length);
+        else
+            StringImpl::copyCharacters(position, string->characters16(), length);
+    }
+
+    ASSERT(buffer == position);
 }
 
 }
 
-JSString* JSString::getIndexSlowCase(ExecState* exec, unsigned i)
+void JSRopeString::outOfMemory(ExecState* nullOrExecForOOM) const
 {
     ASSERT(isRope());
 {
     ASSERT(isRope());
-    resolveRope(exec);
-    // Return a safe no-value result, this should never be used, since the excetion will be thrown.
-    if (exec->exception())
-        return jsString(exec, "");
-    ASSERT(!isRope());
-    ASSERT(i < m_value.length());
-    return jsSingleCharacterSubstring(exec, m_value, i);
+    if (nullOrExecForOOM) {
+        VM& vm = nullOrExecForOOM->vm();
+        auto scope = DECLARE_THROW_SCOPE(vm);
+        throwOutOfMemoryError(nullOrExecForOOM, scope);
+    }
 }
 
 JSValue JSString::toPrimitive(ExecState*, PreferredPrimitiveType) const
 }
 
 JSValue JSString::toPrimitive(ExecState*, PreferredPrimitiveType) const
@@ -164,100 +438,68 @@ JSValue JSString::toPrimitive(ExecState*, PreferredPrimitiveType) const
 
 bool JSString::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result) const
 {
 
 bool JSString::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result) const
 {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+    StringView view = unsafeView(exec);
+    RETURN_IF_EXCEPTION(scope, false);
     result = this;
     result = this;
-    number = jsToNumber(value(exec));
+    number = jsToNumber(view);
     return false;
 }
 
     return false;
 }
 
-bool JSString::toBoolean(ExecState*) const
-{
-    return m_length;
-}
-
 double JSString::toNumber(ExecState* exec) const
 {
 double JSString::toNumber(ExecState* exec) const
 {
-    return jsToNumber(value(exec));
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+    StringView view = unsafeView(exec);
+    RETURN_IF_EXCEPTION(scope, 0);
+    return jsToNumber(view);
 }
 
 }
 
-UString JSString::toString(ExecState* exec) const
+inline StringObject* StringObject::create(VM& vm, JSGlobalObject* globalObject, JSString* string)
 {
 {
-    return value(exec);
-}
-
-inline StringObject* StringObject::create(ExecState* exec, JSGlobalObject* globalObject, JSString* string)
-{
-    StringObject* object = new (allocateCell<StringObject>(*exec->heap())) StringObject(exec->globalData(), globalObject->stringObjectStructure());
-    object->finishCreation(exec->globalData(), string);
+    StringObject* object = new (NotNull, allocateCell<StringObject>(vm.heap)) StringObject(vm, globalObject->stringObjectStructure());
+    object->finishCreation(vm, string);
     return object;
 }
 
 JSObject* JSString::toObject(ExecState* exec, JSGlobalObject* globalObject) const
 {
     return object;
 }
 
 JSObject* JSString::toObject(ExecState* exec, JSGlobalObject* globalObject) const
 {
-    return StringObject::create(exec, globalObject, const_cast<JSString*>(this));
+    return StringObject::create(exec->vm(), globalObject, const_cast<JSString*>(this));
 }
 
 }
 
-JSObject* JSString::toThisObject(JSCell* cell, ExecState* exec)
+JSValue JSString::toThis(JSCell* cell, ExecState* exec, ECMAMode ecmaMode)
 {
 {
-    return StringObject::create(exec, exec->lexicalGlobalObject(), static_cast<JSString*>(cell));
-}
-
-bool JSString::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
-{
-    JSString* thisObject = static_cast<JSString*>(cell);
-    // The semantics here are really getPropertySlot, not getOwnPropertySlot.
-    // This function should only be called by JSValue::get.
-    if (thisObject->getStringPropertySlot(exec, propertyName, slot))
-        return true;
-    if (propertyName == exec->propertyNames().underscoreProto) {
-        slot.setValue(exec->lexicalGlobalObject()->stringPrototype());
-        return true;
-    }
-    slot.setBase(thisObject);
-    JSObject* object;
-    for (JSValue prototype = exec->lexicalGlobalObject()->stringPrototype(); !prototype.isNull(); prototype = object->prototype()) {
-        object = asObject(prototype);
-        if (object->methodTable()->getOwnPropertySlot(object, exec, propertyName, slot))
-            return true;
-    }
-    slot.setUndefined();
-    return true;
+    if (ecmaMode == StrictMode)
+        return cell;
+    return StringObject::create(exec->vm(), exec->lexicalGlobalObject(), asString(cell));
 }
 
 }
 
-bool JSString::getStringPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
+bool JSString::getStringPropertyDescriptor(ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor)
 {
 {
-    if (propertyName == exec->propertyNames().length) {
-        descriptor.setDescriptor(jsNumber(m_length), DontEnum | DontDelete | ReadOnly);
+    VM& vm = exec->vm();
+    if (propertyName == vm.propertyNames->length) {
+        descriptor.setDescriptor(jsNumber(length()), PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
         return true;
     }
     
         return true;
     }
     
-    bool isStrictUInt32;
-    unsigned i = propertyName.toUInt32(isStrictUInt32);
-    if (isStrictUInt32 && i < m_length) {
-        descriptor.setDescriptor(getIndex(exec, i), DontDelete | ReadOnly);
+    Optional<uint32_t> index = parseIndex(propertyName);
+    if (index && index.value() < length()) {
+        descriptor.setDescriptor(getIndex(exec, index.value()), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
         return true;
     }
     
     return false;
 }
 
         return true;
     }
     
     return false;
 }
 
-bool JSString::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
+JSString* jsStringWithCacheSlowCase(VM& vm, StringImpl& stringImpl)
 {
 {
-    if (getStringPropertyDescriptor(exec, propertyName, descriptor))
-        return true;
-    if (propertyName != exec->propertyNames().underscoreProto)
-        return false;
-    descriptor.setDescriptor(exec->lexicalGlobalObject()->stringPrototype(), DontEnum);
-    return true;
-}
+    if (JSString* string = vm.stringCache.get(&stringImpl))
+        return string;
 
 
-bool JSString::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, PropertySlot& slot)
-{
-    JSString* thisObject = static_cast<JSString*>(cell);
-    // The semantics here are really getPropertySlot, not getOwnPropertySlot.
-    // This function should only be called by JSValue::get.
-    if (thisObject->getStringPropertySlot(exec, propertyName, slot))
-        return true;
-    return JSString::getOwnPropertySlot(thisObject, exec, Identifier::from(exec, propertyName), slot);
+    JSString* string = jsString(&vm, String(stringImpl));
+    vm.lastCachedString.set(vm, string);
+    return string;
 }
 
 } // namespace JSC
 }
 
 } // namespace JSC