PropertyAttribute needs a CustomValue bit.
[WebKit-https.git] / Source / JavaScriptCore / runtime / JSObject.cpp
index 2525913..b7fa795 100644 (file)
@@ -37,9 +37,9 @@
 #include "JSCustomGetterSetterFunction.h"
 #include "JSFunction.h"
 #include "JSGlobalObject.h"
+#include "JSImmutableButterfly.h"
 #include "Lookup.h"
 #include "NativeErrorConstructor.h"
-#include "Nodes.h"
 #include "ObjectPrototype.h"
 #include "PropertyDescriptor.h"
 #include "PropertyNameArray.h"
@@ -60,14 +60,14 @@ static unsigned lastArraySize = 0;
 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSObject);
 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSFinalObject);
 
-const char* const NonExtensibleObjectPropertyDefineError = "Attempting to define property on object that is not extensible.";
-const char* const ReadonlyPropertyWriteError = "Attempted to assign to readonly property.";
-const char* const ReadonlyPropertyChangeError = "Attempting to change value of a readonly property.";
-const char* const UnableToDeletePropertyError = "Unable to delete property.";
-const char* const UnconfigurablePropertyChangeAccessMechanismError = "Attempting to change access mechanism for an unconfigurable property.";
-const char* const UnconfigurablePropertyChangeConfigurabilityError = "Attempting to change configurable attribute of unconfigurable property.";
-const char* const UnconfigurablePropertyChangeEnumerabilityError = "Attempting to change enumerable attribute of unconfigurable property.";
-const char* const UnconfigurablePropertyChangeWritabilityError = "Attempting to change writable attribute of unconfigurable property.";
+const ASCIILiteral NonExtensibleObjectPropertyDefineError { "Attempting to define property on object that is not extensible."_s };
+const ASCIILiteral ReadonlyPropertyWriteError { "Attempted to assign to readonly property."_s };
+const ASCIILiteral ReadonlyPropertyChangeError { "Attempting to change value of a readonly property."_s };
+const ASCIILiteral UnableToDeletePropertyError { "Unable to delete property."_s };
+const ASCIILiteral UnconfigurablePropertyChangeAccessMechanismError { "Attempting to change access mechanism for an unconfigurable property."_s };
+const ASCIILiteral UnconfigurablePropertyChangeConfigurabilityError { "Attempting to change configurable attribute of unconfigurable property."_s };
+const ASCIILiteral UnconfigurablePropertyChangeEnumerabilityError { "Attempting to change enumerable attribute of unconfigurable property."_s };
+const ASCIILiteral UnconfigurablePropertyChangeWritabilityError { "Attempting to change writable attribute of unconfigurable property."_s };
 
 const ClassInfo JSObject::s_info = { "Object", nullptr, nullptr, nullptr, CREATE_METHOD_TABLE(JSObject) };
 
@@ -97,7 +97,12 @@ ALWAYS_INLINE void JSObject::markAuxiliaryAndVisitOutOfLineProperties(SlotVisito
     
     if (!butterfly)
         return;
-    
+
+    if (isCopyOnWrite(structure->indexingMode())) {
+        visitor.append(bitwise_cast<WriteBarrier<JSCell>>(JSImmutableButterfly::fromButterfly(butterfly)));
+        return;
+    }
+
     bool hasIndexingHeader = structure->hasIndexingHeader(this);
     size_t preCapacity;
     if (hasIndexingHeader)
@@ -132,16 +137,11 @@ ALWAYS_INLINE Structure* JSObject::visitButterflyImpl(SlotVisitor& visitor)
     Butterfly* butterfly;
     Structure* structure;
     PropertyOffset lastOffset;
-    
-    if (visitor.mutatorIsStopped()) {
-        butterfly = this->butterfly();
-        structure = this->structure(vm);
-        lastOffset = structure->lastOffset();
-        
-        markAuxiliaryAndVisitOutOfLineProperties(visitor, butterfly, structure, lastOffset);
-        
-        switch (structure->indexingType()) {
-        case ALL_CONTIGUOUS_INDEXING_TYPES:
+
+    auto visitElements = [&] (IndexingType indexingMode) {
+        switch (indexingMode) {
+        // We don't need to visit the elements for CopyOnWrite butterflies since they we marked the JSImmutableButterfly acting as out butterfly.
+        case ALL_WRITABLE_CONTIGUOUS_INDEXING_TYPES:
             visitor.appendValuesHidden(butterfly->contiguous().data(), butterfly->publicLength());
             break;
         case ALL_ARRAY_STORAGE_INDEXING_TYPES:
@@ -152,6 +152,16 @@ ALWAYS_INLINE Structure* JSObject::visitButterflyImpl(SlotVisitor& visitor)
         default:
             break;
         }
+    };
+
+    if (visitor.mutatorIsStopped()) {
+        butterfly = this->butterfly();
+        structure = this->structure(vm);
+        lastOffset = structure->lastOffset();
+        
+        markAuxiliaryAndVisitOutOfLineProperties(visitor, butterfly, structure, lastOffset);
+        visitElements(structure->indexingMode());
+
         return structure;
     }
     
@@ -381,22 +391,23 @@ ALWAYS_INLINE Structure* JSObject::visitButterflyImpl(SlotVisitor& visitor)
         return nullptr;
     structure = vm.getStructure(structureID);
     lastOffset = structure->lastOffset();
-    IndexingType indexingType = structure->indexingType();
-    Dependency indexingTypeDependency = Dependency::fence(indexingType);
+    IndexingType indexingMode = structure->indexingMode();
+    Dependency indexingModeDependency = Dependency::fence(indexingMode);
     Locker<JSCellLock> locker(NoLockingNecessary);
-    switch (indexingType) {
-    case ALL_CONTIGUOUS_INDEXING_TYPES:
+    switch (indexingMode) {
     case ALL_ARRAY_STORAGE_INDEXING_TYPES:
         // We need to hold this lock to protect against changes to the innards of the butterfly
-        // that can happen when the butterfly is used for array storage. We conservatively
-        // assume that a contiguous butterfly may transform into an array storage one, though
-        // this is probably more conservative than necessary.
+        // that can happen when the butterfly is used for array storage.
+        // We do not need to hold this lock for contiguous butterflies. We do not reuse the existing
+        // butterfly with contiguous shape for new array storage butterfly. When converting the butterfly
+        // with contiguous shape to array storage, we always allocate a new one. Holding this lock for contiguous
+        // butterflies is unnecessary since contiguous shaped butterfly never becomes broken state.
         locker = holdLock(cellLock());
         break;
     default:
         break;
     }
-    butterfly = indexingTypeDependency.consume(this)->butterfly();
+    butterfly = indexingModeDependency.consume(this)->butterfly();
     Dependency butterflyDependency = Dependency::fence(butterfly);
     if (!butterfly)
         return structure;
@@ -406,30 +417,17 @@ ALWAYS_INLINE Structure* JSObject::visitButterflyImpl(SlotVisitor& visitor)
         return nullptr;
     
     markAuxiliaryAndVisitOutOfLineProperties(visitor, butterfly, structure, lastOffset);
-    
-    ASSERT(indexingType == structure->indexingType());
-    
-    switch (indexingType) {
-    case ALL_CONTIGUOUS_INDEXING_TYPES:
-        visitor.appendValuesHidden(butterfly->contiguous().data(), butterfly->publicLength());
-        break;
-    case ALL_ARRAY_STORAGE_INDEXING_TYPES:
-        visitor.appendValuesHidden(butterfly->arrayStorage()->m_vector, butterfly->arrayStorage()->vectorLength());
-        if (butterfly->arrayStorage()->m_sparseMap)
-            visitor.append(butterfly->arrayStorage()->m_sparseMap);
-        break;
-    default:
-        break;
-    }
+    ASSERT(indexingMode == structure->indexingMode());
+    visitElements(indexingMode);
     
     return structure;
 }
 
-size_t JSObject::estimatedSize(JSCell* cell)
+size_t JSObject::estimatedSize(JSCell* cell, VM& vm)
 {
     JSObject* thisObject = jsCast<JSObject*>(cell);
-    size_t butterflyOutOfLineSize = thisObject->m_butterfly ? thisObject->structure()->outOfLineSize() : 0;
-    return Base::estimatedSize(cell) + butterflyOutOfLineSize;
+    size_t butterflyOutOfLineSize = thisObject->m_butterfly ? thisObject->structure(vm)->outOfLineSize() : 0;
+    return Base::estimatedSize(cell, vm) + butterflyOutOfLineSize;
 }
 
 void JSObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
@@ -509,18 +507,19 @@ void JSFinalObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
 #endif
 }
 
-String JSObject::className(const JSObject* object)
+String JSObject::className(const JSObject* object, VM& vm)
 {
-    const ClassInfo* info = object->classInfo(*object->vm());
+    const ClassInfo* info = object->classInfo(vm);
     ASSERT(info);
     return info->className;
 }
 
-String JSObject::toStringName(const JSObject* object, ExecState*)
+String JSObject::toStringName(const JSObject* object, ExecState* exec)
 {
-    const ClassInfo* info = object->classInfo(*object->vm());
+    VM& vm = exec->vm();
+    const ClassInfo* info = object->classInfo(vm);
     ASSERT(info);
-    return info->methodTable.className(object);
+    return info->methodTable.className(object, vm);
 }
 
 String JSObject::calculatedClassName(JSObject* object)
@@ -554,7 +553,7 @@ String JSObject::calculatedClassName(JSObject* object)
         scope.clearException();
 
     if (prototypeFunctionName.isNull() || prototypeFunctionName == "Object") {
-        String tableClassName = object->methodTable(vm)->className(object);
+        String tableClassName = object->methodTable(vm)->className(object, vm);
         if (!tableClassName.isNull() && tableClassName != "Object")
             return tableClassName;
 
@@ -563,7 +562,7 @@ String JSObject::calculatedClassName(JSObject* object)
             return classInfoName;
 
         if (prototypeFunctionName.isNull())
-            return ASCIILiteral("Object");
+            return "Object"_s;
     }
 
     return prototypeFunctionName;
@@ -658,8 +657,7 @@ bool ordinarySetSlow(ExecState* exec, JSObject* object, PropertyName propertyNam
         if (current->type() == ProxyObjectType && propertyName != vm.propertyNames->underscoreProto) {
             ProxyObject* proxy = jsCast<ProxyObject*>(current);
             PutPropertySlot slot(receiver, shouldThrow);
-            scope.release();
-            return proxy->ProxyObject::put(proxy, exec, propertyName, value, slot);
+            RELEASE_AND_RETURN(scope, proxy->ProxyObject::put(proxy, exec, propertyName, value, slot));
         }
 
         // 9.1.9.1-2 Let ownDesc be ? O.[[GetOwnProperty]](P).
@@ -687,11 +685,11 @@ bool ordinarySetSlow(ExecState* exec, JSObject* object, PropertyName propertyNam
     if (ownDescriptor.isDataDescriptor()) {
         // 9.1.9.1-4-a If ownDesc.[[Writable]] is false, return false.
         if (!ownDescriptor.writable())
-            return typeError(exec, scope, shouldThrow, ASCIILiteral(ReadonlyPropertyWriteError));
+            return typeError(exec, scope, shouldThrow, ReadonlyPropertyWriteError);
 
         // 9.1.9.1-4-b If Type(Receiver) is not Object, return false.
         if (!receiver.isObject())
-            return typeError(exec, scope, shouldThrow, ASCIILiteral(ReadonlyPropertyWriteError));
+            return typeError(exec, scope, shouldThrow, ReadonlyPropertyWriteError);
 
         // In OrdinarySet, the receiver may not be the same to the object.
         // So, we perform [[GetOwnProperty]] onto the receiver while we already perform [[GetOwnProperty]] onto the object.
@@ -706,25 +704,23 @@ bool ordinarySetSlow(ExecState* exec, JSObject* object, PropertyName propertyNam
         if (existingDescriptorFound) {
             // 9.1.9.1-4-d-i If IsAccessorDescriptor(existingDescriptor) is true, return false.
             if (existingDescriptor.isAccessorDescriptor())
-                return typeError(exec, scope, shouldThrow, ASCIILiteral(ReadonlyPropertyWriteError));
+                return typeError(exec, scope, shouldThrow, ReadonlyPropertyWriteError);
 
             // 9.1.9.1-4-d-ii If existingDescriptor.[[Writable]] is false, return false.
             if (!existingDescriptor.writable())
-                return typeError(exec, scope, shouldThrow, ASCIILiteral(ReadonlyPropertyWriteError));
+                return typeError(exec, scope, shouldThrow, ReadonlyPropertyWriteError);
 
             // 9.1.9.1-4-d-iii Let valueDesc be the PropertyDescriptor{[[Value]]: V}.
             PropertyDescriptor valueDescriptor;
             valueDescriptor.setValue(value);
 
             // 9.1.9.1-4-d-iv Return ? Receiver.[[DefineOwnProperty]](P, valueDesc).
-            scope.release();
-            return receiverObject->methodTable(vm)->defineOwnProperty(receiverObject, exec, propertyName, valueDescriptor, shouldThrow);
+            RELEASE_AND_RETURN(scope, receiverObject->methodTable(vm)->defineOwnProperty(receiverObject, exec, propertyName, valueDescriptor, shouldThrow));
         }
 
         // 9.1.9.1-4-e Else Receiver does not currently have a property P,
         // 9.1.9.1-4-e-i Return ? CreateDataProperty(Receiver, P, V).
-        scope.release();
-        return receiverObject->methodTable(vm)->defineOwnProperty(receiverObject, exec, propertyName, PropertyDescriptor(value, static_cast<unsigned>(PropertyAttribute::None)), shouldThrow);
+        RELEASE_AND_RETURN(scope, receiverObject->methodTable(vm)->defineOwnProperty(receiverObject, exec, propertyName, PropertyDescriptor(value, static_cast<unsigned>(PropertyAttribute::None)), shouldThrow));
     }
 
     // 9.1.9.1-5 Assert: IsAccessorDescriptor(ownDesc) is true.
@@ -734,7 +730,7 @@ bool ordinarySetSlow(ExecState* exec, JSObject* object, PropertyName propertyNam
     // 9.1.9.1-7 If setter is undefined, return false.
     JSValue setter = ownDescriptor.setter();
     if (!setter.isObject())
-        return typeError(exec, scope, shouldThrow, ASCIILiteral(ReadonlyPropertyWriteError));
+        return typeError(exec, scope, shouldThrow, ReadonlyPropertyWriteError);
 
     // 9.1.9.1-8 Perform ? Call(setter, Receiver, << V >>).
     JSObject* setterObject = asObject(setter);
@@ -771,13 +767,13 @@ bool JSObject::putInlineSlow(ExecState* exec, PropertyName propertyName, JSValue
         if (isValidOffset(offset)) {
             if (attributes & PropertyAttribute::ReadOnly) {
                 ASSERT(this->prototypeChainMayInterceptStoreTo(vm, propertyName) || obj == this);
-                return typeError(exec, scope, slot.isStrictMode(), ASCIILiteral(ReadonlyPropertyWriteError));
+                return typeError(exec, scope, slot.isStrictMode(), ReadonlyPropertyWriteError);
             }
 
             JSValue gs = obj->getDirect(offset);
             if (gs.isGetterSetter()) {
                 // We need to make sure that we decide to cache this property before we potentially execute aribitrary JS.
-                if (!structure()->isDictionary())
+                if (!structure(vm)->isDictionary())
                     slot.setCacheableSetter(obj, offset);
 
                 bool result = callSetter(exec, slot.thisValue(), gs, value, slot.isStrictMode() ? StrictMode : NotStrictMode);
@@ -801,12 +797,10 @@ bool JSObject::putInlineSlow(ExecState* exec, PropertyName propertyName, JSValue
             // prototypes it should be replaced, so break here.
             break;
         }
-        if (!obj->staticPropertiesReified()) {
+        if (!obj->staticPropertiesReified(vm)) {
             if (obj->classInfo(vm)->hasStaticSetterOrReadonlyProperties()) {
-                if (auto entry = obj->findPropertyHashEntry(vm, propertyName)) {
-                    scope.release();
-                    return putEntry(exec, entry->table->classForThis, entry->value, obj, this, propertyName, value, slot);
-                }
+                if (auto entry = obj->findPropertyHashEntry(vm, propertyName))
+                    RELEASE_AND_RETURN(scope, putEntry(exec, entry->table->classForThis, entry->value, obj, this, propertyName, value, slot));
             }
         }
         if (obj->type() == ProxyObjectType && propertyName != vm.propertyNames->underscoreProto) {
@@ -814,8 +808,7 @@ bool JSObject::putInlineSlow(ExecState* exec, PropertyName propertyName, JSValue
             // We need to do more because this is observable behavior.
             // https://bugs.webkit.org/show_bug.cgi?id=155012
             ProxyObject* proxy = jsCast<ProxyObject*>(obj);
-            scope.release();
-            return proxy->ProxyObject::put(proxy, exec, propertyName, value, slot);
+            RELEASE_AND_RETURN(scope, proxy->ProxyObject::put(proxy, exec, propertyName, value, slot));
         }
         JSValue prototype = obj->getPrototype(vm, exec);
         RETURN_IF_EXCEPTION(scope, false);
@@ -825,7 +818,7 @@ bool JSObject::putInlineSlow(ExecState* exec, PropertyName propertyName, JSValue
     }
 
     if (!putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot))
-        return typeError(exec, scope, slot.isStrictMode(), ASCIILiteral(ReadonlyPropertyWriteError));
+        return typeError(exec, scope, slot.isStrictMode(), ReadonlyPropertyWriteError);
     return true;
 }
 
@@ -833,12 +826,14 @@ bool JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName,
 {
     VM& vm = exec->vm();
     JSObject* thisObject = jsCast<JSObject*>(cell);
-    
+
     if (propertyName > MAX_ARRAY_INDEX) {
         PutPropertySlot slot(cell, shouldThrow);
-        return thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, propertyName), value, slot);
+        return thisObject->methodTable(vm)->put(thisObject, exec, Identifier::from(exec, propertyName), value, slot);
     }
-    
+
+    thisObject->ensureWritable(vm);
+
     switch (thisObject->indexingType()) {
     case ALL_BLANK_INDEXING_TYPES:
         break;
@@ -873,6 +868,7 @@ bool JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName,
             // Reloop.
             return putByIndex(cell, exec, propertyName, value, shouldThrow);
         }
+
         double valueAsDouble = value.asNumber();
         if (valueAsDouble != valueAsDouble) {
             thisObject->convertDoubleToContiguous(vm);
@@ -964,7 +960,7 @@ ArrayStorage* JSObject::enterDictionaryIndexingModeWhenArrayStorageAlreadyExists
         // This will always be a new entry in the map, so no need to check we can write,
         // and attributes are default so no need to set them.
         if (value)
-            map->add(this, i).iterator->value.set(vm, map, value);
+            map->add(this, i).iterator->value.forceSet(vm, map, value, 0);
     }
 
     DeferGC deferGC(vm.heap);
@@ -1004,7 +1000,7 @@ void JSObject::enterDictionaryIndexingMode(VM& vm)
 
 void JSObject::notifyPresenceOfIndexedAccessors(VM& vm)
 {
-    if (mayInterceptIndexedAccesses())
+    if (mayInterceptIndexedAccesses(vm))
         return;
     
     setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AddIndexedAccessors));
@@ -1020,8 +1016,8 @@ Butterfly* JSObject::createInitialIndexedStorage(VM& vm, unsigned length)
     ASSERT(length <= MAX_STORAGE_VECTOR_LENGTH);
     IndexingType oldType = indexingType();
     ASSERT_UNUSED(oldType, !hasIndexedProperties(oldType));
-    ASSERT(!needsSlowPutIndexing());
-    ASSERT(!indexingShouldBeSparse());
+    ASSERT(!needsSlowPutIndexing(vm));
+    ASSERT(!indexingShouldBeSparse(vm));
     Structure* structure = this->structure(vm);
     unsigned propertyCapacity = structure->outOfLineCapacity();
     unsigned vectorLength = Butterfly::optimalContiguousVectorLength(propertyCapacity, length);
@@ -1040,7 +1036,7 @@ Butterfly* JSObject::createInitialUndecided(VM& vm, unsigned length)
     StructureID oldStructureID = this->structureID();
     Structure* oldStructure = vm.getStructure(oldStructureID);
     Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, NonPropertyTransition::AllocateUndecided);
-    nukeStructureAndSetButterfly(vm, oldStructureID, newButterfly, newStructure->indexingType());
+    nukeStructureAndSetButterfly(vm, oldStructureID, newButterfly);
     setStructure(vm, newStructure);
     return newButterfly;
 }
@@ -1049,13 +1045,12 @@ ContiguousJSValues JSObject::createInitialInt32(VM& vm, unsigned length)
 {
     DeferGC deferGC(vm.heap);
     Butterfly* newButterfly = createInitialIndexedStorage(vm, length);
-    uint32_t mask = newButterfly->computeIndexingMask();
     for (unsigned i = newButterfly->vectorLength(); i--;)
-        newButterfly->contiguous().at(mask, i).setWithoutWriteBarrier(JSValue());
+        newButterfly->contiguous().at(this, i).setWithoutWriteBarrier(JSValue());
     StructureID oldStructureID = this->structureID();
     Structure* oldStructure = vm.getStructure(oldStructureID);
     Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, NonPropertyTransition::AllocateInt32);
-    nukeStructureAndSetButterfly(vm, oldStructureID, newButterfly, newStructure->indexingType());
+    nukeStructureAndSetButterfly(vm, oldStructureID, newButterfly);
     setStructure(vm, newStructure);
     return newButterfly->contiguousInt32();
 }
@@ -1064,13 +1059,12 @@ ContiguousDoubles JSObject::createInitialDouble(VM& vm, unsigned length)
 {
     DeferGC deferGC(vm.heap);
     Butterfly* newButterfly = createInitialIndexedStorage(vm, length);
-    uint32_t mask = newButterfly->computeIndexingMask();
     for (unsigned i = newButterfly->vectorLength(); i--;)
-        newButterfly->contiguousDouble().at(mask, i) = PNaN;
+        newButterfly->contiguousDouble().at(this, i) = PNaN;
     StructureID oldStructureID = this->structureID();
     Structure* oldStructure = vm.getStructure(oldStructureID);
     Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, NonPropertyTransition::AllocateDouble);
-    nukeStructureAndSetButterfly(vm, oldStructureID, newButterfly, newStructure->indexingType());
+    nukeStructureAndSetButterfly(vm, oldStructureID, newButterfly);
     setStructure(vm, newStructure);
     return newButterfly->contiguousDouble();
 }
@@ -1079,18 +1073,17 @@ ContiguousJSValues JSObject::createInitialContiguous(VM& vm, unsigned length)
 {
     DeferGC deferGC(vm.heap);
     Butterfly* newButterfly = createInitialIndexedStorage(vm, length);
-    uint32_t mask = newButterfly->computeIndexingMask();
     for (unsigned i = newButterfly->vectorLength(); i--;)
-        newButterfly->contiguous().at(mask, i).setWithoutWriteBarrier(JSValue());
+        newButterfly->contiguous().at(this, i).setWithoutWriteBarrier(JSValue());
     StructureID oldStructureID = this->structureID();
     Structure* oldStructure = vm.getStructure(oldStructureID);
     Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, NonPropertyTransition::AllocateContiguous);
-    nukeStructureAndSetButterfly(vm, oldStructureID, newButterfly, newStructure->indexingType());
+    nukeStructureAndSetButterfly(vm, oldStructureID, newButterfly);
     setStructure(vm, newStructure);
     return newButterfly->contiguous();
 }
 
-Butterfly* JSObject::createArrayStorageButterfly(VM& vm, JSCell* intendedOwner, Structure* structure, unsigned length, unsigned vectorLength, Butterfly* oldButterfly)
+Butterfly* JSObject::createArrayStorageButterfly(VM& vm, JSObject* intendedOwner, Structure* structure, unsigned length, unsigned vectorLength, Butterfly* oldButterfly)
 {
     Butterfly* newButterfly = Butterfly::createOrGrowArrayRight(
         oldButterfly, vm, intendedOwner, structure, structure->outOfLineCapacity(), false, 0,
@@ -1119,8 +1112,8 @@ ArrayStorage* JSObject::createArrayStorage(VM& vm, unsigned length, unsigned vec
 
     Butterfly* newButterfly = createArrayStorageButterfly(vm, this, oldStructure, length, vectorLength, butterfly());
     ArrayStorage* result = newButterfly->arrayStorage();
-    Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, suggestedArrayStorageTransition());
-    nukeStructureAndSetButterfly(vm, oldStructureID, newButterfly, newStructure->indexingType());
+    Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, suggestedArrayStorageTransition(vm));
+    nukeStructureAndSetButterfly(vm, oldStructureID, newButterfly);
     setStructure(vm, newStructure);
     return result;
 }
@@ -1173,16 +1166,14 @@ ArrayStorage* JSObject::constructConvertedArrayStorageWithoutCopyingElements(VM&
     Structure* structure = this->structure(vm);
     unsigned publicLength = m_butterfly->publicLength();
     unsigned propertyCapacity = structure->outOfLineCapacity();
-    unsigned propertySize = structure->outOfLineSize();
-    
-    Butterfly* newButterfly = Butterfly::createUninitialized(
-        vm, this, 0, propertyCapacity, true, ArrayStorage::sizeFor(neededLength));
+
+    Butterfly* newButterfly = Butterfly::createUninitialized(vm, this, 0, propertyCapacity, true, ArrayStorage::sizeFor(neededLength));
     
     memcpy(
-        newButterfly->propertyStorage() - propertySize,
-        m_butterfly->propertyStorage() - propertySize,
-        propertySize * sizeof(EncodedJSValue));
-    
+        newButterfly->base(0, propertyCapacity),
+        m_butterfly->base(0, propertyCapacity),
+        propertyCapacity * sizeof(EncodedJSValue));
+
     ArrayStorage* newStorage = newButterfly->arrayStorage();
     newStorage->setVectorLength(neededLength);
     newStorage->setLength(publicLength);
@@ -1207,23 +1198,24 @@ ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm, NonPropertyTransi
     StructureID oldStructureID = this->structureID();
     Structure* oldStructure = vm.getStructure(oldStructureID);
     Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, transition);
-    nukeStructureAndSetButterfly(vm, oldStructureID, storage->butterfly(), newStructure->indexingType());
+    nukeStructureAndSetButterfly(vm, oldStructureID, storage->butterfly());
     setStructure(vm, newStructure);
     return storage;
 }
 
 ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm)
 {
-    return convertUndecidedToArrayStorage(vm, suggestedArrayStorageTransition());
+    return convertUndecidedToArrayStorage(vm, suggestedArrayStorageTransition(vm));
 }
 
 ContiguousDoubles JSObject::convertInt32ToDouble(VM& vm)
 {
     ASSERT(hasInt32(indexingType()));
+    ASSERT(!isCopyOnWrite(indexingMode()));
 
     Butterfly* butterfly = m_butterfly.get();
     for (unsigned i = butterfly->vectorLength(); i--;) {
-        WriteBarrier<Unknown>* current = &butterfly->contiguous().at(this, i);
+        WriteBarrier<Unknown>* current = &butterfly->contiguous().atUnsafe(i);
         double* currentAsDouble = bitwise_cast<double*>(current);
         JSValue v = current->get();
         // NOTE: Since this may be used during initialization, v could be garbage. If it's garbage,
@@ -1265,23 +1257,24 @@ ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm, NonPropertyTransition
     StructureID oldStructureID = this->structureID();
     Structure* oldStructure = vm.getStructure(oldStructureID);
     Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, transition);
-    nukeStructureAndSetButterfly(vm, oldStructureID, newStorage->butterfly(), newStructure->indexingType());
+    nukeStructureAndSetButterfly(vm, oldStructureID, newStorage->butterfly());
     setStructure(vm, newStructure);
     return newStorage;
 }
 
 ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm)
 {
-    return convertInt32ToArrayStorage(vm, suggestedArrayStorageTransition());
+    return convertInt32ToArrayStorage(vm, suggestedArrayStorageTransition(vm));
 }
 
 ContiguousJSValues JSObject::convertDoubleToContiguous(VM& vm)
 {
     ASSERT(hasDouble(indexingType()));
+    ASSERT(!isCopyOnWrite(indexingMode()));
 
     Butterfly* butterfly = m_butterfly.get();
     for (unsigned i = butterfly->vectorLength(); i--;) {
-        double* current = &butterfly->contiguousDouble().at(this, i);
+        double* current = &butterfly->contiguousDouble().atUnsafe(i);
         WriteBarrier<Unknown>* currentAsValue = bitwise_cast<WriteBarrier<Unknown>*>(current);
         double value = *current;
         if (value != value) {
@@ -1318,14 +1311,14 @@ ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm, NonPropertyTransitio
     StructureID oldStructureID = this->structureID();
     Structure* oldStructure = vm.getStructure(oldStructureID);
     Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, transition);
-    nukeStructureAndSetButterfly(vm, oldStructureID, newStorage->butterfly(), newStructure->indexingType());
+    nukeStructureAndSetButterfly(vm, oldStructureID, newStorage->butterfly());
     setStructure(vm, newStructure);
     return newStorage;
 }
 
 ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm)
 {
-    return convertDoubleToArrayStorage(vm, suggestedArrayStorageTransition());
+    return convertDoubleToArrayStorage(vm, suggestedArrayStorageTransition(vm));
 }
 
 ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm, NonPropertyTransition transition)
@@ -1342,41 +1335,73 @@ ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm, NonPropertyTrans
         if (v)
             newStorage->m_numValuesInVector++;
     }
-    
-    Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition);
 
-    // This has a crazy race with the garbage collector. When changing the butterfly and structure,
-    // the mutator always sets the structure last. The collector will always read the structure
-    // first. We probably have to follow that convention here as well. This means that the collector
-    // will sometimes see the new butterfly (the one with ArrayStorage) with the only structure (the
-    // one that says that the butterfly is Contiguous). When scanning Contiguous, the collector will
-    // mark word at addresses greater than or equal to the butterfly pointer, up to the publicLength
-    // in the butterfly. But an ArrayStorage has some non-pointer header data at low positive
-    // offsets from the butterfly - so when this race happens, the collector will surely crash
-    // because it will fail to decode two consecutive int32s as if it was a JSValue.
+    // While we modify the butterfly of Contiguous Array, we do not take any cellLock here. This is because
+    // (1) the old butterfly is not changed and (2) new butterfly is not changed after it is exposed to
+    // the collector.
+    // The mutator performs the following operations are sequentially executed by using storeStoreFence.
     //
-    // Fortunately, we have the JSCell lock for this purpose!
-    
-    if (vm.heap.mutatorShouldBeFenced()) {
-        auto locker = holdLock(cellLock());
-        m_butterflyIndexingMask = newStorage->butterfly()->computeIndexingMask();
-        setStructureIDDirectly(nuke(structureID()));
-        WTF::storeStoreFence();
-        m_butterfly.set(vm, this, newStorage->butterfly());
+    //     CreateNewButterfly NukeStructure ChangeButterfly PutNewStructure
+    //
+    // Meanwhile the collector performs the following steps sequentially:
+    //
+    //     ReadStructureEarly ReadButterfly ReadStructureLate
+    //
+    // We list up all the patterns by writing a tiny script, and ensure all the cases are categorized into BEFORE, AFTER, and IGNORE.
+    //
+    // CreateNewButterfly NukeStructure ChangeButterfly PutNewStructure ReadStructureEarly ReadButterfly ReadStructureLate: AFTER, trivially
+    // CreateNewButterfly NukeStructure ChangeButterfly ReadStructureEarly PutNewStructure ReadButterfly ReadStructureLate: IGNORE, because nuked structure read early
+    // CreateNewButterfly NukeStructure ChangeButterfly ReadStructureEarly ReadButterfly PutNewStructure ReadStructureLate: IGNORE, because nuked structure read early
+    // CreateNewButterfly NukeStructure ChangeButterfly ReadStructureEarly ReadButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read early
+    // CreateNewButterfly NukeStructure ReadStructureEarly ChangeButterfly PutNewStructure ReadButterfly ReadStructureLate: IGNORE, because nuked structure read early
+    // CreateNewButterfly NukeStructure ReadStructureEarly ChangeButterfly ReadButterfly PutNewStructure ReadStructureLate: IGNORE, because nuked structure read early
+    // CreateNewButterfly NukeStructure ReadStructureEarly ChangeButterfly ReadButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read early
+    // CreateNewButterfly NukeStructure ReadStructureEarly ReadButterfly ChangeButterfly PutNewStructure ReadStructureLate: IGNORE, because nuked structure read early
+    // CreateNewButterfly NukeStructure ReadStructureEarly ReadButterfly ChangeButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read early
+    // CreateNewButterfly NukeStructure ReadStructureEarly ReadButterfly ReadStructureLate ChangeButterfly PutNewStructure: IGNORE, because nuked structure read early
+    // CreateNewButterfly ReadStructureEarly NukeStructure ChangeButterfly PutNewStructure ReadButterfly ReadStructureLate: IGNORE, because early and late structures don't match
+    // CreateNewButterfly ReadStructureEarly NukeStructure ChangeButterfly ReadButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
+    // CreateNewButterfly ReadStructureEarly NukeStructure ChangeButterfly ReadButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
+    // CreateNewButterfly ReadStructureEarly NukeStructure ReadButterfly ChangeButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
+    // CreateNewButterfly ReadStructureEarly NukeStructure ReadButterfly ChangeButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
+    // CreateNewButterfly ReadStructureEarly NukeStructure ReadButterfly ReadStructureLate ChangeButterfly PutNewStructure: IGNORE, because nuked structure read late
+    // CreateNewButterfly ReadStructureEarly ReadButterfly NukeStructure ChangeButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
+    // CreateNewButterfly ReadStructureEarly ReadButterfly NukeStructure ChangeButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
+    // CreateNewButterfly ReadStructureEarly ReadButterfly NukeStructure ReadStructureLate ChangeButterfly PutNewStructure: IGNORE, because nuked structure read late
+    // CreateNewButterfly ReadStructureEarly ReadButterfly ReadStructureLate NukeStructure ChangeButterfly PutNewStructure: BEFORE, trivially.
+    // ReadStructureEarly CreateNewButterfly NukeStructure ChangeButterfly PutNewStructure ReadButterfly ReadStructureLate: IGNORE, because early and late structures don't match
+    // ReadStructureEarly CreateNewButterfly NukeStructure ChangeButterfly ReadButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
+    // ReadStructureEarly CreateNewButterfly NukeStructure ChangeButterfly ReadButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
+    // ReadStructureEarly CreateNewButterfly NukeStructure ReadButterfly ChangeButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
+    // ReadStructureEarly CreateNewButterfly NukeStructure ReadButterfly ChangeButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
+    // ReadStructureEarly CreateNewButterfly NukeStructure ReadButterfly ReadStructureLate ChangeButterfly PutNewStructure: IGNORE, because nuked structure read late
+    // ReadStructureEarly CreateNewButterfly ReadButterfly NukeStructure ChangeButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
+    // ReadStructureEarly CreateNewButterfly ReadButterfly NukeStructure ChangeButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
+    // ReadStructureEarly CreateNewButterfly ReadButterfly NukeStructure ReadStructureLate ChangeButterfly PutNewStructure: IGNORE, because nuked structure read late
+    // ReadStructureEarly CreateNewButterfly ReadButterfly ReadStructureLate NukeStructure ChangeButterfly PutNewStructure: BEFORE, CreateNewButterfly is not visible to collector.
+    // ReadStructureEarly ReadButterfly CreateNewButterfly NukeStructure ChangeButterfly PutNewStructure ReadStructureLate: IGNORE, because early and late structures don't match
+    // ReadStructureEarly ReadButterfly CreateNewButterfly NukeStructure ChangeButterfly ReadStructureLate PutNewStructure: IGNORE, because nuked structure read late
+    // ReadStructureEarly ReadButterfly CreateNewButterfly NukeStructure ReadStructureLate ChangeButterfly PutNewStructure: IGNORE, because nuked structure read late
+    // ReadStructureEarly ReadButterfly CreateNewButterfly ReadStructureLate NukeStructure ChangeButterfly PutNewStructure: BEFORE, CreateNewButterfly is not visible to collector.
+    // ReadStructureEarly ReadButterfly ReadStructureLate CreateNewButterfly NukeStructure ChangeButterfly PutNewStructure: BEFORE, trivially.
+
+    ASSERT(newStorage->butterfly() != butterfly);
+    StructureID oldStructureID = this->structureID();
+    Structure* oldStructure = vm.getStructure(oldStructureID);
+    Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, transition);
+
+    // Ensure new Butterfly initialization is correctly done before exposing it to the concurrent threads.
+    if (isX86() || vm.heap.mutatorShouldBeFenced())
         WTF::storeStoreFence();
-        setStructure(vm, newStructure);
-    } else {
-        m_butterflyIndexingMask = newStorage->butterfly()->computeIndexingMask();
-        m_butterfly.set(vm, this, newStorage->butterfly());
-        setStructure(vm, newStructure);
-    }
+    nukeStructureAndSetButterfly(vm, oldStructureID, newStorage->butterfly());
+    setStructure(vm, newStructure);
     
     return newStorage;
 }
 
 ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm)
 {
-    return convertContiguousToArrayStorage(vm, suggestedArrayStorageTransition());
+    return convertContiguousToArrayStorage(vm, suggestedArrayStorageTransition(vm));
 }
 
 void JSObject::convertUndecidedForValue(VM& vm, JSValue value)
@@ -1422,10 +1447,43 @@ void JSObject::convertInt32ForValue(VM& vm, JSValue value)
         convertInt32ToDouble(vm);
         return;
     }
-    
+
     convertInt32ToContiguous(vm);
 }
 
+void JSObject::convertFromCopyOnWrite(VM& vm)
+{
+    ASSERT(isCopyOnWrite(indexingMode()));
+    ASSERT(structure(vm)->indexingMode() == indexingMode());
+
+    const bool hasIndexingHeader = true;
+    Butterfly* oldButterfly = butterfly();
+    size_t propertyCapacity = 0;
+    unsigned newVectorLength = Butterfly::optimalContiguousVectorLength(propertyCapacity, std::min(oldButterfly->vectorLength() * 2, MAX_STORAGE_VECTOR_LENGTH));
+    Butterfly* newButterfly = Butterfly::createUninitialized(vm, this, 0, propertyCapacity, hasIndexingHeader, newVectorLength * sizeof(JSValue));
+
+    memcpy(newButterfly->propertyStorage(), oldButterfly->propertyStorage(), oldButterfly->vectorLength() * sizeof(JSValue) + sizeof(IndexingHeader));
+
+    WTF::storeStoreFence();
+    NonPropertyTransition transition = ([&] () {
+        switch (indexingType()) {
+        case ArrayWithInt32:
+            return NonPropertyTransition::AllocateInt32;
+        case ArrayWithDouble:
+            return NonPropertyTransition::AllocateDouble;
+        case ArrayWithContiguous:
+            return NonPropertyTransition::AllocateContiguous;
+        default:
+            RELEASE_ASSERT_NOT_REACHED();
+            return NonPropertyTransition::AllocateContiguous;
+        }
+    })();
+    StructureID oldStructureID = structureID();
+    Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition);
+    nukeStructureAndSetButterfly(vm, oldStructureID, newButterfly);
+    setStructure(vm, newStructure);
+}
+
 void JSObject::setIndexQuicklyToUndecided(VM& vm, unsigned index, JSValue value)
 {
     ASSERT(index < m_butterfly->publicLength());
@@ -1448,16 +1506,25 @@ void JSObject::convertDoubleToContiguousWhilePerformingSetIndex(VM& vm, unsigned
     setIndexQuickly(vm, index, value);
 }
 
-ContiguousJSValues JSObject::ensureInt32Slow(VM& vm)
+ContiguousJSValues JSObject::tryMakeWritableInt32Slow(VM& vm)
 {
     ASSERT(inherits(vm, info()));
-    
+
+    if (isCopyOnWrite(indexingMode())) {
+        if (leastUpperBoundOfIndexingTypes(indexingType() & IndexingShapeMask, Int32Shape) == Int32Shape) {
+            ASSERT(hasInt32(indexingMode()));
+            convertFromCopyOnWrite(vm);
+            return butterfly()->contiguousInt32();
+        }
+        return ContiguousJSValues();
+    }
+
     if (structure(vm)->hijacksIndexingHeader())
         return ContiguousJSValues();
     
     switch (indexingType()) {
     case ALL_BLANK_INDEXING_TYPES:
-        if (UNLIKELY(indexingShouldBeSparse() || needsSlowPutIndexing()))
+        if (UNLIKELY(indexingShouldBeSparse(vm) || needsSlowPutIndexing(vm)))
             return ContiguousJSValues();
         return createInitialInt32(vm, 0);
         
@@ -1475,16 +1542,26 @@ ContiguousJSValues JSObject::ensureInt32Slow(VM& vm)
     }
 }
 
-ContiguousDoubles JSObject::ensureDoubleSlow(VM& vm)
+ContiguousDoubles JSObject::tryMakeWritableDoubleSlow(VM& vm)
 {
     ASSERT(inherits(vm, info()));
-    
+
+    if (isCopyOnWrite(indexingMode())) {
+        if (leastUpperBoundOfIndexingTypes(indexingType() & IndexingShapeMask, DoubleShape) == DoubleShape) {
+            convertFromCopyOnWrite(vm);
+            if (hasDouble(indexingMode()))
+                return butterfly()->contiguousDouble();
+            ASSERT(hasInt32(indexingMode()));
+        } else
+            return ContiguousDoubles();
+    }
+
     if (structure(vm)->hijacksIndexingHeader())
         return ContiguousDoubles();
     
     switch (indexingType()) {
     case ALL_BLANK_INDEXING_TYPES:
-        if (UNLIKELY(indexingShouldBeSparse() || needsSlowPutIndexing()))
+        if (UNLIKELY(indexingShouldBeSparse(vm) || needsSlowPutIndexing(vm)))
             return ContiguousDoubles();
         return createInitialDouble(vm, 0);
         
@@ -1504,16 +1581,26 @@ ContiguousDoubles JSObject::ensureDoubleSlow(VM& vm)
     }
 }
 
-ContiguousJSValues JSObject::ensureContiguousSlow(VM& vm)
+ContiguousJSValues JSObject::tryMakeWritableContiguousSlow(VM& vm)
 {
     ASSERT(inherits(vm, info()));
-    
+
+    if (isCopyOnWrite(indexingMode())) {
+        if (leastUpperBoundOfIndexingTypes(indexingType() & IndexingShapeMask, ContiguousShape) == ContiguousShape) {
+            convertFromCopyOnWrite(vm);
+            if (hasContiguous(indexingMode()))
+                return butterfly()->contiguous();
+            ASSERT(hasInt32(indexingMode()) || hasDouble(indexingMode()));
+        } else
+            return ContiguousJSValues();
+    }
+
     if (structure(vm)->hijacksIndexingHeader())
         return ContiguousJSValues();
     
     switch (indexingType()) {
     case ALL_BLANK_INDEXING_TYPES:
-        if (UNLIKELY(indexingShouldBeSparse() || needsSlowPutIndexing()))
+        if (UNLIKELY(indexingShouldBeSparse(vm) || needsSlowPutIndexing(vm)))
             return ContiguousJSValues();
         return createInitialContiguous(vm, 0);
         
@@ -1541,31 +1628,33 @@ ArrayStorage* JSObject::ensureArrayStorageSlow(VM& vm)
 
     if (structure(vm)->hijacksIndexingHeader())
         return nullptr;
-    
+
+    ensureWritable(vm);
+
     switch (indexingType()) {
     case ALL_BLANK_INDEXING_TYPES:
-        if (UNLIKELY(indexingShouldBeSparse()))
+        if (UNLIKELY(indexingShouldBeSparse(vm)))
             return ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm);
         return createInitialArrayStorage(vm);
         
     case ALL_UNDECIDED_INDEXING_TYPES:
-        ASSERT(!indexingShouldBeSparse());
-        ASSERT(!needsSlowPutIndexing());
+        ASSERT(!indexingShouldBeSparse(vm));
+        ASSERT(!needsSlowPutIndexing(vm));
         return convertUndecidedToArrayStorage(vm);
         
     case ALL_INT32_INDEXING_TYPES:
-        ASSERT(!indexingShouldBeSparse());
-        ASSERT(!needsSlowPutIndexing());
+        ASSERT(!indexingShouldBeSparse(vm));
+        ASSERT(!needsSlowPutIndexing(vm));
         return convertInt32ToArrayStorage(vm);
         
     case ALL_DOUBLE_INDEXING_TYPES:
-        ASSERT(!indexingShouldBeSparse());
-        ASSERT(!needsSlowPutIndexing());
+        ASSERT(!indexingShouldBeSparse(vm));
+        ASSERT(!needsSlowPutIndexing(vm));
         return convertDoubleToArrayStorage(vm);
         
     case ALL_CONTIGUOUS_INDEXING_TYPES:
-        ASSERT(!indexingShouldBeSparse());
-        ASSERT(!needsSlowPutIndexing());
+        ASSERT(!indexingShouldBeSparse(vm));
+        ASSERT(!needsSlowPutIndexing(vm));
         return convertContiguousToArrayStorage(vm);
         
     default:
@@ -1576,6 +1665,8 @@ ArrayStorage* JSObject::ensureArrayStorageSlow(VM& vm)
 
 ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(VM& vm)
 {
+    ensureWritable(vm);
+
     switch (indexingType()) {
     case ALL_BLANK_INDEXING_TYPES: {
         createArrayStorage(vm, 0, 0);
@@ -1607,6 +1698,8 @@ ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(V
 
 void JSObject::switchToSlowPutArrayStorage(VM& vm)
 {
+    ensureWritable(vm);
+
     switch (indexingType()) {
     case ArrayClass:
         ensureArrayStorage(vm);
@@ -1652,13 +1745,13 @@ void JSObject::setPrototypeDirect(VM& vm, JSValue prototype)
         prototype.asCell()->didBecomePrototype();
     
     if (structure(vm)->hasMonoProto()) {
-        DeferredStructureTransitionWatchpointFire deferred;
+        DeferredStructureTransitionWatchpointFire deferred(vm, structure(vm));
         Structure* newStructure = Structure::changePrototypeTransition(vm, structure(vm), prototype, deferred);
         setStructure(vm, newStructure);
     } else
         putDirect(vm, knownPolyProtoOffset, prototype);
 
-    if (!anyObjectInChainMayInterceptIndexedAccesses())
+    if (!anyObjectInChainMayInterceptIndexedAccesses(vm))
         return;
     
     if (mayBePrototype()) {
@@ -1684,7 +1777,7 @@ bool JSObject::setPrototypeWithCycleCheck(VM& vm, ExecState* exec, JSValue proto
         if (this->getPrototype(vm, exec) == prototype)
             return true;
 
-        return typeError(exec, scope, shouldThrowIfCantSet, ASCIILiteral("Cannot set prototype of immutable prototype object"));
+        return typeError(exec, scope, shouldThrowIfCantSet, "Cannot set prototype of immutable prototype object"_s);
     }
 
     ASSERT(methodTable(vm)->toThis(this, exec, NotStrictMode) == this);
@@ -1696,12 +1789,12 @@ bool JSObject::setPrototypeWithCycleCheck(VM& vm, ExecState* exec, JSValue proto
     RETURN_IF_EXCEPTION(scope, false);
 
     if (!isExtensible)
-        return typeError(exec, scope, shouldThrowIfCantSet, ASCIILiteral(ReadonlyPropertyWriteError));
+        return typeError(exec, scope, shouldThrowIfCantSet, ReadonlyPropertyWriteError);
 
     JSValue nextPrototype = prototype;
     while (nextPrototype && nextPrototype.isObject()) {
         if (nextPrototype == this)
-            return typeError(exec, scope, shouldThrowIfCantSet, ASCIILiteral("cyclic __proto__ value"));
+            return typeError(exec, scope, shouldThrowIfCantSet, "cyclic __proto__ value"_s);
         // FIXME: The specification currently says we should check if the [[GetPrototypeOf]] internal method of nextPrototype
         // is not the ordinary object internal method. However, we currently restrict this to Proxy objects as it would allow
         // for cycles with certain HTML objects (WindowProxy, Location) otherwise.
@@ -1757,19 +1850,25 @@ bool JSObject::putSetter(ExecState* exec, PropertyName propertyName, JSValue set
     return defineOwnProperty(this, exec, propertyName, descriptor, true);
 }
 
-bool JSObject::putDirectAccessor(ExecState* exec, PropertyName propertyName, JSValue value, unsigned attributes)
+bool JSObject::putDirectAccessor(ExecState* exec, PropertyName propertyName, GetterSetter* accessor, unsigned attributes)
 {
-    ASSERT(value.isGetterSetter() && (attributes & PropertyAttribute::Accessor));
+    ASSERT(attributes & PropertyAttribute::Accessor);
 
     if (std::optional<uint32_t> index = parseIndex(propertyName))
-        return putDirectIndex(exec, index.value(), value, attributes, PutDirectIndexLikePutDirect);
+        return putDirectIndex(exec, index.value(), accessor, attributes, PutDirectIndexLikePutDirect);
 
-    return putDirectNonIndexAccessor(exec->vm(), propertyName, value, attributes);
+    return putDirectNonIndexAccessor(exec->vm(), propertyName, accessor, attributes);
 }
 
+// FIXME: Introduce a JSObject::putDirectCustomValue() method instead of using
+// JSObject::putDirectCustomAccessor() to put CustomValues.
+// https://bugs.webkit.org/show_bug.cgi?id=192576
 bool JSObject::putDirectCustomAccessor(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes)
 {
     ASSERT(!parseIndex(propertyName));
+    ASSERT(value.isCustomGetterSetter());
+    if (!(attributes & PropertyAttribute::CustomAccessor))
+        attributes |= PropertyAttribute::CustomValue;
 
     PutPropertySlot slot(this);
     bool result = putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, attributes, slot);
@@ -1783,10 +1882,11 @@ bool JSObject::putDirectCustomAccessor(VM& vm, PropertyName propertyName, JSValu
     return result;
 }
 
-bool JSObject::putDirectNonIndexAccessor(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes)
+bool JSObject::putDirectNonIndexAccessor(VM& vm, PropertyName propertyName, GetterSetter* accessor, unsigned attributes)
 {
+    ASSERT(attributes & PropertyAttribute::Accessor);
     PutPropertySlot slot(this);
-    bool result = putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, attributes, slot);
+    bool result = putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, accessor, attributes, slot);
 
     Structure* structure = this->structure(vm);
     if (attributes & PropertyAttribute::ReadOnly)
@@ -1831,7 +1931,7 @@ bool JSObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName proper
 
     unsigned attributes;
 
-    if (!thisObject->staticPropertiesReified()) {
+    if (!thisObject->staticPropertiesReified(vm)) {
         if (auto entry = thisObject->findPropertyHashEntry(vm, propertyName)) {
             // If the static table contains a non-configurable (DontDelete) property then we can return early;
             // if there is a property in the storage array it too must be non-configurable (the language does
@@ -1858,7 +1958,7 @@ bool JSObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName proper
             thisObject->setStructure(vm, Structure::removePropertyTransition(vm, structure, propertyName, offset));
 
         if (offset != invalidOffset)
-            thisObject->putDirectUndefined(offset);
+            thisObject->locationForOffset(offset)->clear();
     }
 
     return true;
@@ -1866,26 +1966,44 @@ bool JSObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName proper
 
 bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i)
 {
+    VM& vm = exec->vm();
     JSObject* thisObject = jsCast<JSObject*>(cell);
     
     if (i > MAX_ARRAY_INDEX)
-        return thisObject->methodTable(exec->vm())->deleteProperty(thisObject, exec, Identifier::from(exec, i));
+        return thisObject->methodTable(vm)->deleteProperty(thisObject, exec, Identifier::from(exec, i));
     
-    switch (thisObject->indexingType()) {
+    switch (thisObject->indexingMode()) {
     case ALL_BLANK_INDEXING_TYPES:
     case ALL_UNDECIDED_INDEXING_TYPES:
         return true;
-        
-    case ALL_INT32_INDEXING_TYPES:
-    case ALL_CONTIGUOUS_INDEXING_TYPES: {
+
+    case CopyOnWriteArrayWithInt32:
+    case CopyOnWriteArrayWithContiguous: {
+        Butterfly* butterfly = thisObject->butterfly();
+        if (i >= butterfly->vectorLength())
+            return true;
+        thisObject->convertFromCopyOnWrite(vm);
+        FALLTHROUGH;
+    }
+
+    case ALL_WRITABLE_INT32_INDEXING_TYPES:
+    case ALL_WRITABLE_CONTIGUOUS_INDEXING_TYPES: {
         Butterfly* butterfly = thisObject->butterfly();
         if (i >= butterfly->vectorLength())
             return true;
         butterfly->contiguous().at(thisObject, i).clear();
         return true;
     }
-        
-    case ALL_DOUBLE_INDEXING_TYPES: {
+
+    case CopyOnWriteArrayWithDouble: {
+        Butterfly* butterfly = thisObject->butterfly();
+        if (i >= butterfly->vectorLength())
+            return true;
+        thisObject->convertFromCopyOnWrite(vm);
+        FALLTHROUGH;
+    }
+
+    case ALL_WRITABLE_DOUBLE_INDEXING_TYPES: {
         Butterfly* butterfly = thisObject->butterfly();
         if (i >= butterfly->vectorLength())
             return true;
@@ -1905,7 +2023,7 @@ bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i)
         } else if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
             SparseArrayValueMap::iterator it = map->find(i);
             if (it != map->notFound()) {
-                if (it->value.attributes & PropertyAttribute::DontDelete)
+                if (it->value.attributes() & PropertyAttribute::DontDelete)
                     return false;
                 map->remove(it);
             }
@@ -1928,15 +2046,21 @@ static ALWAYS_INLINE JSValue callToPrimitiveFunction(ExecState* exec, const JSOb
     VM& vm = exec->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
 
-    JSValue function = object->get(exec, propertyName);
+    PropertySlot slot(object, PropertySlot::InternalMethodType::Get);
+    // FIXME: Remove this when we have fixed: rdar://problem/33451840
+    // https://bugs.webkit.org/show_bug.cgi?id=187109.
+    constexpr bool debugNullStructure = mode == TypeHintMode::TakesHint;
+    bool hasProperty = const_cast<JSObject*>(object)->getPropertySlot<debugNullStructure>(exec, propertyName, slot);
+    RETURN_IF_EXCEPTION(scope, scope.exception());
+    JSValue function = hasProperty ? slot.getValue(exec, propertyName) : jsUndefined();
     RETURN_IF_EXCEPTION(scope, scope.exception());
     if (function.isUndefinedOrNull() && mode == TypeHintMode::TakesHint)
         return JSValue();
     CallData callData;
-    CallType callType = getCallData(function, callData);
+    CallType callType = getCallData(vm, function, callData);
     if (callType == CallType::None) {
         if (mode == TypeHintMode::TakesHint)
-            throwTypeError(exec, scope, ASCIILiteral("Symbol.toPrimitive is not a function, undefined, or null"));
+            throwTypeError(exec, scope, "Symbol.toPrimitive is not a function, undefined, or null"_s);
         return scope.exception();
     }
 
@@ -1962,7 +2086,7 @@ static ALWAYS_INLINE JSValue callToPrimitiveFunction(ExecState* exec, const JSOb
     RETURN_IF_EXCEPTION(scope, scope.exception());
     ASSERT(!result.isGetterSetter());
     if (result.isObject())
-        return mode == TypeHintMode::DoesNotTakeHint ? JSValue() : throwTypeError(exec, scope, ASCIILiteral("Symbol.toPrimitive returned an object"));
+        return mode == TypeHintMode::DoesNotTakeHint ? JSValue() : throwTypeError(exec, scope, "Symbol.toPrimitive returned an object"_s);
     return result;
 }
 
@@ -2000,7 +2124,7 @@ JSValue JSObject::ordinaryToPrimitive(ExecState* exec, PreferredPrimitiveType hi
 
     scope.assertNoException();
 
-    return throwTypeError(exec, scope, ASCIILiteral("No default value"));
+    return throwTypeError(exec, scope, "No default value"_s);
 }
 
 JSValue JSObject::defaultValue(const JSObject* object, ExecState* exec, PreferredPrimitiveType hint)
@@ -2018,8 +2142,7 @@ JSValue JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredT
     if (value)
         return value;
 
-    scope.release();
-    return this->methodTable(vm)->defaultValue(this, exec, preferredType);
+    RELEASE_AND_RETURN(scope, this->methodTable(vm)->defaultValue(this, exec, preferredType));
 }
 
 bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result) const
@@ -2063,7 +2186,7 @@ bool JSObject::hasInstance(ExecState* exec, JSValue value, JSValue hasInstanceVa
 
     if (!hasInstanceValue.isUndefinedOrNull() && hasInstanceValue != exec->lexicalGlobalObject()->functionProtoHasInstanceSymbolFunction()) {
         CallData callData;
-        CallType callType = JSC::getCallData(hasInstanceValue, callData);
+        CallType callType = JSC::getCallData(vm, hasInstanceValue, callData);
         if (callType == CallType::None) {
             throwException(exec, scope, createInvalidInstanceofParameterErrorHasInstanceValueNotFunction(exec, this));
             return false;
@@ -2081,13 +2204,11 @@ bool JSObject::hasInstance(ExecState* exec, JSValue value, JSValue hasInstanceVa
     if (info.implementsDefaultHasInstance()) {
         JSValue prototype = get(exec, vm.propertyNames->prototype);
         RETURN_IF_EXCEPTION(scope, false);
-        scope.release();
-        return defaultHasInstance(exec, value, prototype);
-    }
-    if (info.implementsHasInstance()) {
-        scope.release();
-        return methodTable(vm)->customHasInstance(this, exec, value);
+        RELEASE_AND_RETURN(scope, defaultHasInstance(exec, value, prototype));
     }
+    if (info.implementsHasInstance())
+        RELEASE_AND_RETURN(scope, methodTable(vm)->customHasInstance(this, exec, value));
+
     throwException(exec, scope, createInvalidInstanceofParameterErrorNotFunction(exec, this));
     return false;
 }
@@ -2099,8 +2220,7 @@ bool JSObject::hasInstance(ExecState* exec, JSValue value)
     JSValue hasInstanceValue = get(exec, vm.propertyNames->hasInstanceSymbol);
     RETURN_IF_EXCEPTION(scope, false);
 
-    scope.release();
-    return hasInstance(exec, value, hasInstanceValue);
+    RELEASE_AND_RETURN(scope, hasInstance(exec, value, hasInstanceValue));
 }
 
 bool JSObject::defaultHasInstance(ExecState* exec, JSValue value, JSValue proto)
@@ -2112,7 +2232,7 @@ bool JSObject::defaultHasInstance(ExecState* exec, JSValue value, JSValue proto)
         return false;
 
     if (!proto.isObject()) {
-        throwTypeError(exec, scope, ASCIILiteral("instanceof called on an object with an invalid prototype property."));
+        throwTypeError(exec, scope, "instanceof called on an object with an invalid prototype property."_s);
         return false;
     }
 
@@ -2224,7 +2344,7 @@ void JSObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNa
                 
                 SparseArrayValueMap::const_iterator end = map->end();
                 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) {
-                    if (mode.includeDontEnumProperties() || !(it->value.attributes & PropertyAttribute::DontEnum))
+                    if (mode.includeDontEnumProperties() || !(it->value.attributes() & PropertyAttribute::DontEnum))
                         keys.uncheckedAppend(static_cast<unsigned>(it->key));
                 }
                 
@@ -2246,7 +2366,7 @@ void JSObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNa
 void JSObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
 {
     VM& vm = exec->vm();
-    if (!object->staticPropertiesReified())
+    if (!object->staticPropertiesReified(vm))
         getClassPropertyNames(exec, object->classInfo(vm), propertyNames, mode);
 
     if (!mode.includeJSObjectProperties())
@@ -2261,8 +2381,7 @@ double JSObject::toNumber(ExecState* exec) const
     auto scope = DECLARE_THROW_SCOPE(vm);
     JSValue primitive = toPrimitive(exec, PreferNumber);
     RETURN_IF_EXCEPTION(scope, 0.0); // should be picked up soon in Nodes.cpp
-    scope.release();
-    return primitive.toNumber(exec);
+    RELEASE_AND_RETURN(scope, primitive.toNumber(exec));
 }
 
 JSString* JSObject::toString(ExecState* exec) const
@@ -2297,22 +2416,22 @@ void JSObject::freeze(VM& vm)
 
 bool JSObject::preventExtensions(JSObject* object, ExecState* exec)
 {
-    if (!object->isStructureExtensible()) {
+    VM& vm = exec->vm();
+    if (!object->isStructureExtensible(vm)) {
         // We've already set the internal [[PreventExtensions]] field to false.
         // We don't call the methodTable isExtensible here because it's not defined
         // that way in the specification. We are just doing an optimization here.
         return true;
     }
 
-    VM& vm = exec->vm();
     object->enterDictionaryIndexingMode(vm);
     object->setStructure(vm, Structure::preventExtensionsTransition(vm, object->structure(vm)));
     return true;
 }
 
-bool JSObject::isExtensible(JSObject* obj, ExecState*)
+bool JSObject::isExtensible(JSObject* obj, ExecState* exec)
 {
-    return obj->isExtensibleImpl();
+    return obj->isExtensibleImpl(exec->vm());
 }
 
 bool JSObject::isExtensible(ExecState* exec)
@@ -2323,8 +2442,8 @@ bool JSObject::isExtensible(ExecState* exec)
 
 void JSObject::reifyAllStaticProperties(ExecState* exec)
 {
-    ASSERT(!staticPropertiesReified());
     VM& vm = exec->vm();
+    ASSERT(!staticPropertiesReified(vm));
 
     // If this object's ClassInfo has no static properties, then nothing to reify!
     // We can safely set the flag to avoid the expensive check again in the future.
@@ -2353,9 +2472,9 @@ void JSObject::reifyAllStaticProperties(ExecState* exec)
     structure(vm)->setStaticPropertiesReified(true);
 }
 
-NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSCell* getterSetter, unsigned attributes, PropertyOffset offset)
+NEVER_INLINE void JSObject::fillGetterPropertySlot(VM& vm, PropertySlot& slot, JSCell* getterSetter, unsigned attributes, PropertyOffset offset)
 {
-    if (structure()->isUncacheableDictionary()) {
+    if (structure(vm)->isUncacheableDictionary()) {
         slot.setGetterSlot(this, attributes, jsCast<GetterSetter*>(getterSetter));
         return;
     }
@@ -2365,51 +2484,46 @@ NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSCell* g
     slot.setCacheableGetterSlot(this, attributes, jsCast<GetterSetter*>(getterSetter), offset);
 }
 
-bool JSObject::putIndexedDescriptor(ExecState* exec, SparseArrayEntry* entryInMap, const PropertyDescriptor& descriptor, PropertyDescriptor& oldDescriptor)
+static bool putIndexedDescriptor(ExecState* exec, SparseArrayValueMap* map, SparseArrayEntry* entryInMap, const PropertyDescriptor& descriptor, PropertyDescriptor& oldDescriptor)
 {
     VM& vm = exec->vm();
-    auto map = m_butterfly->arrayStorage()->m_sparseMap.get();
 
     if (descriptor.isDataDescriptor()) {
+        unsigned attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~PropertyAttribute::Accessor;
         if (descriptor.value())
-            entryInMap->set(vm, map, descriptor.value());
+            entryInMap->forceSet(vm, map, descriptor.value(), attributes);
         else if (oldDescriptor.isAccessorDescriptor())
-            entryInMap->set(vm, map, jsUndefined());
-        entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~PropertyAttribute::Accessor;
+            entryInMap->forceSet(vm, map, jsUndefined(), attributes);
+        else
+            entryInMap->forceSet(attributes);
         return true;
     }
 
     if (descriptor.isAccessorDescriptor()) {
-        JSObject* getter = 0;
+        JSObject* getter = nullptr;
         if (descriptor.getterPresent())
             getter = descriptor.getterObject();
         else if (oldDescriptor.isAccessorDescriptor())
             getter = oldDescriptor.getterObject();
-        JSObject* setter = 0;
+        JSObject* setter = nullptr;
         if (descriptor.setterPresent())
             setter = descriptor.setterObject();
         else if (oldDescriptor.isAccessorDescriptor())
             setter = oldDescriptor.setterObject();
 
-        GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject());
-        if (getter)
-            accessor->setGetter(vm, exec->lexicalGlobalObject(), getter);
-        if (setter)
-            accessor->setSetter(vm, exec->lexicalGlobalObject(), setter);
-
-        entryInMap->set(vm, map, accessor);
-        entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~PropertyAttribute::ReadOnly;
+        GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject(), getter, setter);
+        entryInMap->forceSet(vm, map, accessor, descriptor.attributesOverridingCurrent(oldDescriptor) & ~PropertyAttribute::ReadOnly);
         return true;
     }
 
     ASSERT(descriptor.isGenericDescriptor());
-    entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor);
+    entryInMap->forceSet(descriptor.attributesOverridingCurrent(oldDescriptor));
     return true;
 }
 
 ALWAYS_INLINE static bool canDoFastPutDirectIndex(VM& vm, JSObject* object)
 {
-    return isJSArray(object)
+    return (isJSArray(object) && !isCopyOnWrite(object->indexingMode()))
         || jsDynamicCast<JSFinalObject*>(vm, object)
         || TypeInfo::isArgumentsType(object->type());
 }
@@ -2422,6 +2536,8 @@ bool JSObject::defineOwnIndexedProperty(ExecState* exec, unsigned index, const P
 
     ASSERT(index <= MAX_ARRAY_INDEX);
 
+    ensureWritable(vm);
+
     if (!inSparseIndexingMode()) {
         // Fast case: we're putting a regular property to a regular array
         // FIXME: this will pessimistically assume that if attributes are missing then they'll default to false
@@ -2429,8 +2545,7 @@ bool JSObject::defineOwnIndexedProperty(ExecState* exec, unsigned index, const P
         // state (i.e. defineOwnProperty could be used to set a value without needing to entering 'SparseMode').
         if (!descriptor.attributes() && descriptor.value() && canDoFastPutDirectIndex(vm, this)) {
             ASSERT(!descriptor.isAccessorDescriptor());
-            scope.release();
-            return putDirectIndex(exec, index, descriptor.value(), 0, throwException ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow);
+            RELEASE_AND_RETURN(scope, putDirectIndex(exec, index, descriptor.value(), 0, throwException ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow));
         }
         
         ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm);
@@ -2450,9 +2565,9 @@ bool JSObject::defineOwnIndexedProperty(ExecState* exec, unsigned index, const P
     // 3. If current is undefined and extensible is false, then Reject.
     // 4. If current is undefined and extensible is true, then
     if (result.isNewEntry) {
-        if (!isStructureExtensible()) {
+        if (!isStructureExtensible(vm)) {
             map->remove(result.iterator);
-            return typeError(exec, scope, throwException, ASCIILiteral(NonExtensibleObjectPropertyDefineError));
+            return typeError(exec, scope, throwException, NonExtensibleObjectPropertyDefineError);
         }
 
         // 4.a. If IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then create an own data property
@@ -2465,12 +2580,8 @@ bool JSObject::defineOwnIndexedProperty(ExecState* exec, unsigned index, const P
         // is set to its default value.
         // 4.c. Return true.
 
-        PropertyDescriptor defaults;
-        entryInMap->setWithoutWriteBarrier(jsUndefined());
-        entryInMap->attributes = PropertyAttribute::DontDelete | PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly;
-        entryInMap->get(defaults);
-
-        putIndexedDescriptor(exec, entryInMap, descriptor, defaults);
+        PropertyDescriptor defaults(jsUndefined(), PropertyAttribute::DontDelete | PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly);
+        putIndexedDescriptor(exec, map, entryInMap, descriptor, defaults);
         Butterfly* butterfly = m_butterfly.get();
         if (index >= butterfly->arrayStorage()->length())
             butterfly->arrayStorage()->setLength(index + 1);
@@ -2490,10 +2601,10 @@ bool JSObject::defineOwnIndexedProperty(ExecState* exec, unsigned index, const P
     if (!current.configurable()) {
         // 7.a. Reject, if the [[Configurable]] field of Desc is true.
         if (descriptor.configurablePresent() && descriptor.configurable())
-            return typeError(exec, scope, throwException, ASCIILiteral(UnconfigurablePropertyChangeConfigurabilityError));
+            return typeError(exec, scope, throwException, UnconfigurablePropertyChangeConfigurabilityError);
         // 7.b. Reject, if the [[Enumerable]] field of Desc is present and the [[Enumerable]] fields of current and Desc are the Boolean negation of each other.
         if (descriptor.enumerablePresent() && current.enumerable() != descriptor.enumerable())
-            return typeError(exec, scope, throwException, ASCIILiteral(UnconfigurablePropertyChangeEnumerabilityError));
+            return typeError(exec, scope, throwException, UnconfigurablePropertyChangeEnumerabilityError);
     }
 
     // 8. If IsGenericDescriptor(Desc) is true, then no further validation is required.
@@ -2502,7 +2613,7 @@ bool JSObject::defineOwnIndexedProperty(ExecState* exec, unsigned index, const P
         if (current.isDataDescriptor() != descriptor.isDataDescriptor()) {
             // 9.a. Reject, if the [[Configurable]] field of current is false.
             if (!current.configurable())
-                return typeError(exec, scope, throwException, ASCIILiteral(UnconfigurablePropertyChangeAccessMechanismError));
+                return typeError(exec, scope, throwException, UnconfigurablePropertyChangeAccessMechanismError);
             // 9.b. If IsDataDescriptor(current) is true, then convert the property named P of object O from a
             // data property to an accessor property. Preserve the existing values of the converted property's
             // [[Configurable]] and [[Enumerable]] attributes and set the rest of the property's attributes to
@@ -2516,11 +2627,11 @@ bool JSObject::defineOwnIndexedProperty(ExecState* exec, unsigned index, const P
             if (!current.configurable() && !current.writable()) {
                 // 10.a.i. Reject, if the [[Writable]] field of current is false and the [[Writable]] field of Desc is true.
                 if (descriptor.writable())
-                    return typeError(exec, scope, throwException, ASCIILiteral(UnconfigurablePropertyChangeWritabilityError));
+                    return typeError(exec, scope, throwException, UnconfigurablePropertyChangeWritabilityError);
                 // 10.a.ii. If the [[Writable]] field of current is false, then
                 // 10.a.ii.1. Reject, if the [[Value]] field of Desc is present and SameValue(Desc.[[Value]], current.[[Value]]) is false.
                 if (descriptor.value() && !sameValue(exec, descriptor.value(), current.value()))
-                    return typeError(exec, scope, throwException, ASCIILiteral(ReadonlyPropertyChangeError));
+                    return typeError(exec, scope, throwException, ReadonlyPropertyChangeError);
             }
             // 10.b. else, the [[Configurable]] field of current is true, so any change is acceptable.
         } else {
@@ -2529,16 +2640,16 @@ bool JSObject::defineOwnIndexedProperty(ExecState* exec, unsigned index, const P
             if (!current.configurable()) {
                 // 11.i. Reject, if the [[Set]] field of Desc is present and SameValue(Desc.[[Set]], current.[[Set]]) is false.
                 if (descriptor.setterPresent() && descriptor.setter() != current.setter())
-                    return typeError(exec, scope, throwException, ASCIILiteral("Attempting to change the setter of an unconfigurable property."));
+                    return typeError(exec, scope, throwException, "Attempting to change the setter of an unconfigurable property."_s);
                 // 11.ii. Reject, if the [[Get]] field of Desc is present and SameValue(Desc.[[Get]], current.[[Get]]) is false.
                 if (descriptor.getterPresent() && descriptor.getter() != current.getter())
-                    return typeError(exec, scope, throwException, ASCIILiteral("Attempting to change the getter of an unconfigurable property."));
+                    return typeError(exec, scope, throwException, "Attempting to change the getter of an unconfigurable property."_s);
             }
         }
     }
 
     // 12. For each attribute field of Desc that is present, set the correspondingly named attribute of the property named P of object O to the value of the field.
-    putIndexedDescriptor(exec, entryInMap, descriptor, current);
+    putIndexedDescriptor(exec, map, entryInMap, descriptor, current);
     // 13. Return true.
     return true;
 }
@@ -2568,7 +2679,7 @@ bool JSObject::attemptToInterceptPutByIndexOnHoleForPrototype(ExecState* exec, J
         ArrayStorage* storage = current->arrayStorageOrNull();
         if (storage && storage->m_sparseMap) {
             SparseArrayValueMap::iterator iter = storage->m_sparseMap->find(i);
-            if (iter != storage->m_sparseMap->notFound() && (iter->value.attributes & (PropertyAttribute::Accessor | PropertyAttribute::ReadOnly))) {
+            if (iter != storage->m_sparseMap->notFound() && (iter->value.attributes() & (PropertyAttribute::Accessor | PropertyAttribute::ReadOnly))) {
                 putResult = iter->value.put(exec, thisValue, storage->m_sparseMap.get(), value, shouldThrow);
                 return true;
             }
@@ -2603,8 +2714,9 @@ bool JSObject::putByIndexBeyondVectorLengthWithoutAttributes(ExecState* exec, un
     VM& vm = exec->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
 
+    RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!isCopyOnWrite(indexingMode()));
     ASSERT((indexingType() & IndexingShapeMask) == indexingShape);
-    ASSERT(!indexingShouldBeSparse());
+    ASSERT(!indexingShouldBeSparse(vm));
 
     Butterfly* butterfly = m_butterfly.get();
     
@@ -2666,6 +2778,7 @@ bool JSObject::putByIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, uns
     VM& vm = exec->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
 
+    ASSERT(!isCopyOnWrite(indexingMode()));
     // i should be a valid array index that is outside of the current vector.
     ASSERT(i <= MAX_ARRAY_INDEX);
     ASSERT(i >= storage->vectorLength());
@@ -2675,7 +2788,7 @@ bool JSObject::putByIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, uns
     // First, handle cases where we don't currently have a sparse map.
     if (LIKELY(!map)) {
         // If the array is not extensible, we should have entered dictionary mode, and created the sparse map.
-        ASSERT(isStructureExtensible());
+        ASSERT(isStructureExtensible(vm));
     
         // Update m_length if necessary.
         if (i >= storage->length())
@@ -2693,16 +2806,15 @@ bool JSObject::putByIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, uns
         }
         // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.
         map = allocateSparseIndexMap(vm);
-        scope.release();
-        return map->putEntry(exec, this, i, value, shouldThrow);
+        RELEASE_AND_RETURN(scope, map->putEntry(exec, this, i, value, shouldThrow));
     }
 
     // Update m_length if necessary.
     unsigned length = storage->length();
     if (i >= length) {
         // Prohibit growing the array if length is not writable.
-        if (map->lengthIsReadOnly() || !isStructureExtensible())
-            return typeError(exec, scope, shouldThrow, ASCIILiteral(ReadonlyPropertyWriteError));
+        if (map->lengthIsReadOnly() || !isStructureExtensible(vm))
+            return typeError(exec, scope, shouldThrow, ReadonlyPropertyWriteError);
         length = i + 1;
         storage->setLength(length);
     }
@@ -2710,10 +2822,8 @@ bool JSObject::putByIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, uns
     // We are currently using a map - check whether we still want to be doing so.
     // We will continue  to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.
     unsigned numValuesInArray = storage->m_numValuesInVector + map->size();
-    if (map->sparseMode() || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(vm, length)) {
-        scope.release();
-        return map->putEntry(exec, this, i, value, shouldThrow);
-    }
+    if (map->sparseMode() || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(vm, length))
+        RELEASE_AND_RETURN(scope, map->putEntry(exec, this, i, value, shouldThrow));
 
     // Reread m_storage after increaseVectorLength, update m_numValuesInVector.
     storage = arrayStorage();
@@ -2738,12 +2848,14 @@ bool JSObject::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue
 {
     VM& vm = exec->vm();
 
+    RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!isCopyOnWrite(indexingMode()));
+
     // i should be a valid array index that is outside of the current vector.
     ASSERT(i <= MAX_ARRAY_INDEX);
     
     switch (indexingType()) {
     case ALL_BLANK_INDEXING_TYPES: {
-        if (indexingShouldBeSparse()) {
+        if (indexingShouldBeSparse(vm)) {
             return putByIndexBeyondVectorLengthWithArrayStorage(
                 exec, i, value, shouldThrow,
                 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
@@ -2752,9 +2864,9 @@ bool JSObject::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue
             return putByIndexBeyondVectorLengthWithArrayStorage(
                 exec, i, value, shouldThrow, createArrayStorage(vm, 0, 0));
         }
-        if (needsSlowPutIndexing()) {
+        if (needsSlowPutIndexing(vm)) {
             // Convert the indexing type to the SlowPutArrayStorage and retry.
-            createArrayStorage(vm, i + 1, getNewVectorLength(0, 0, 0, i + 1));
+            createArrayStorage(vm, i + 1, getNewVectorLength(vm, 0, 0, 0, i + 1));
             return putByIndex(this, exec, i, value, shouldThrow);
         }
         
@@ -2812,7 +2924,7 @@ bool JSObject::putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState* exec,
     // First, handle cases where we don't currently have a sparse map.
     if (LIKELY(!map)) {
         // If the array is not extensible, we should have entered dictionary mode, and created the spare map.
-        ASSERT(isStructureExtensible());
+        ASSERT(isStructureExtensible(vm));
     
         // Update m_length if necessary.
         if (i >= storage->length())
@@ -2832,8 +2944,7 @@ bool JSObject::putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState* exec,
         }
         // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.
         map = allocateSparseIndexMap(vm);
-        scope.release();
-        return map->putDirect(exec, this, i, value, attributes, mode);
+        RELEASE_AND_RETURN(scope, map->putDirect(exec, this, i, value, attributes, mode));
     }
 
     // Update m_length if necessary.
@@ -2842,9 +2953,9 @@ bool JSObject::putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState* exec,
         if (mode != PutDirectIndexLikePutDirect) {
             // Prohibit growing the array if length is not writable.
             if (map->lengthIsReadOnly())
-                return typeError(exec, scope, mode == PutDirectIndexShouldThrow, ASCIILiteral(ReadonlyPropertyWriteError));
-            if (!isStructureExtensible())
-                return typeError(exec, scope, mode == PutDirectIndexShouldThrow, ASCIILiteral(NonExtensibleObjectPropertyDefineError));
+                return typeError(exec, scope, mode == PutDirectIndexShouldThrow, ReadonlyPropertyWriteError);
+            if (!isStructureExtensible(vm))
+                return typeError(exec, scope, mode == PutDirectIndexShouldThrow, NonExtensibleObjectPropertyDefineError);
         }
         length = i + 1;
         storage->setLength(length);
@@ -2853,10 +2964,8 @@ bool JSObject::putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState* exec,
     // We are currently using a map - check whether we still want to be doing so.
     // We will continue  to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.
     unsigned numValuesInArray = storage->m_numValuesInVector + map->size();
-    if (map->sparseMode() || attributes || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(vm, length)) {
-        scope.release();
-        return map->putDirect(exec, this, i, value, attributes, mode);
-    }
+    if (map->sparseMode() || attributes || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(vm, length))
+        RELEASE_AND_RETURN(scope, map->putDirect(exec, this, i, value, attributes, mode));
 
     // Reread m_storage after increaseVectorLength, update m_numValuesInVector.
     storage = arrayStorage();
@@ -2880,7 +2989,8 @@ bool JSObject::putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState* exec,
 bool JSObject::putDirectIndexSlowOrBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode)
 {
     VM& vm = exec->vm();
-    
+    ASSERT(!value.isCustomGetterSetter());
+
     if (!canDoFastPutDirectIndex(vm, this)) {
         PropertyDescriptor descriptor;
         descriptor.setDescriptor(value, attributes);
@@ -2895,7 +3005,7 @@ bool JSObject::putDirectIndexSlowOrBeyondVectorLength(ExecState* exec, unsigned
     
     switch (indexingType()) {
     case ALL_BLANK_INDEXING_TYPES: {
-        if (indexingShouldBeSparse() || attributes) {
+        if (indexingShouldBeSparse(vm) || attributes) {
             return putDirectIndexBeyondVectorLengthWithArrayStorage(
                 exec, i, value, attributes, mode,
                 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
@@ -2904,8 +3014,8 @@ bool JSObject::putDirectIndexSlowOrBeyondVectorLength(ExecState* exec, unsigned
             return putDirectIndexBeyondVectorLengthWithArrayStorage(
                 exec, i, value, attributes, mode, createArrayStorage(vm, 0, 0));
         }
-        if (needsSlowPutIndexing()) {
-            ArrayStorage* storage = createArrayStorage(vm, i + 1, getNewVectorLength(0, 0, 0, i + 1));
+        if (needsSlowPutIndexing(vm)) {
+            ArrayStorage* storage = createArrayStorage(vm, i + 1, getNewVectorLength(vm, 0, 0, 0, i + 1));
             storage->m_vector[i].set(vm, this, value);
             storage->m_numValuesInVector++;
             return true;
@@ -2922,7 +3032,7 @@ bool JSObject::putDirectIndexSlowOrBeyondVectorLength(ExecState* exec, unsigned
     }
         
     case ALL_INT32_INDEXING_TYPES: {
-        ASSERT(!indexingShouldBeSparse());
+        ASSERT(!indexingShouldBeSparse(vm));
         if (attributes)
             return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
         if (!value.isInt32()) {
@@ -2934,7 +3044,7 @@ bool JSObject::putDirectIndexSlowOrBeyondVectorLength(ExecState* exec, unsigned
     }
         
     case ALL_DOUBLE_INDEXING_TYPES: {
-        ASSERT(!indexingShouldBeSparse());
+        ASSERT(!indexingShouldBeSparse(vm));
         if (attributes)
             return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
         if (!value.isNumber()) {
@@ -2951,7 +3061,7 @@ bool JSObject::putDirectIndexSlowOrBeyondVectorLength(ExecState* exec, unsigned
     }
         
     case ALL_CONTIGUOUS_INDEXING_TYPES: {
-        ASSERT(!indexingShouldBeSparse());
+        ASSERT(!indexingShouldBeSparse(vm));
         if (attributes)
             return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
         putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, i, value);
@@ -2971,9 +3081,8 @@ bool JSObject::putDirectIndexSlowOrBeyondVectorLength(ExecState* exec, unsigned
 
 bool JSObject::putDirectNativeIntrinsicGetter(VM& vm, JSGlobalObject* globalObject, Identifier name, NativeFunction nativeFunction, Intrinsic intrinsic, unsigned attributes)
 {
-    GetterSetter* accessor = GetterSetter::create(vm, globalObject);
     JSFunction* function = JSFunction::create(vm, globalObject, 0, makeString("get ", name.string()), nativeFunction, intrinsic);
-    accessor->setGetter(vm, globalObject, function);
+    GetterSetter* accessor = GetterSetter::create(vm, globalObject, function, nullptr);
     return putDirectNonIndexAccessor(vm, name, accessor, attributes);
 }
 
@@ -3028,7 +3137,7 @@ JSFunction* JSObject::putDirectBuiltinFunctionWithoutTransition(VM& vm, JSGlobal
 }
 
 // NOTE: This method is for ArrayStorage vectors.
-ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned indexBias, unsigned currentVectorLength, unsigned currentLength, unsigned desiredLength)
+ALWAYS_INLINE unsigned JSObject::getNewVectorLength(VM& vm, unsigned indexBias, unsigned currentVectorLength, unsigned currentLength, unsigned desiredLength)
 {
     ASSERT(desiredLength <= MAX_STORAGE_VECTOR_LENGTH);
 
@@ -3048,11 +3157,11 @@ ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned indexBias, unsigned
     lastArraySize = std::min(increasedLength, FIRST_ARRAY_STORAGE_VECTOR_GROW);
 
     return ArrayStorage::optimalVectorLength(
-        indexBias, structure()->outOfLineCapacity(),
+        indexBias, structure(vm)->outOfLineCapacity(),
         std::min(increasedLength, MAX_STORAGE_VECTOR_LENGTH));
 }
 
-ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned desiredLength)
+ALWAYS_INLINE unsigned JSObject::getNewVectorLength(VM& vm, unsigned desiredLength)
 {
     unsigned indexBias = 0;
     unsigned vectorLength = 0;
@@ -3065,7 +3174,7 @@ ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned desiredLength)
         length = m_butterfly->publicLength();
     }
 
-    return getNewVectorLength(indexBias, vectorLength, length, desiredLength);
+    return getNewVectorLength(vm, indexBias, vectorLength, length, desiredLength);
 }
 
 template<IndexingType indexingShape>
@@ -3127,7 +3236,6 @@ bool JSObject::increaseVectorLength(VM& vm, unsigned newLength)
         for (unsigned i = vectorLength; i < availableVectorLength; ++i)
             storage->m_vector[i].clear();
         storage->setVectorLength(availableVectorLength);
-        m_butterflyIndexingMask = storage->butterfly()->computeIndexingMask();
         return true;
     }
     
@@ -3142,7 +3250,7 @@ bool JSObject::increaseVectorLength(VM& vm, unsigned newLength)
 
     unsigned indexBias = storage->m_indexBias;
     ASSERT(newLength > vectorLength);
-    unsigned newVectorLength = getNewVectorLength(newLength);
+    unsigned newVectorLength = getNewVectorLength(vm, newLength);
 
     // Fast case - there is no precapacity. In these cases a realloc makes sense.
     Structure* structure = this->structure(vm);
@@ -3179,12 +3287,18 @@ bool JSObject::increaseVectorLength(VM& vm, unsigned newLength)
 
 bool JSObject::ensureLengthSlow(VM& vm, unsigned length)
 {
+    if (isCopyOnWrite(indexingMode())) {
+        convertFromCopyOnWrite(vm);
+        if (m_butterfly->vectorLength() >= length)
+            return true;
+    }
+
     Butterfly* butterfly = this->butterfly();
     
     ASSERT(length <= MAX_STORAGE_VECTOR_LENGTH);
     ASSERT(hasContiguous(indexingType()) || hasInt32(indexingType()) || hasDouble(indexingType()) || hasUndecided(indexingType()));
     ASSERT(length > butterfly->vectorLength());
-    
+
     unsigned oldVectorLength = butterfly->vectorLength();
     unsigned newVectorLength;
     
@@ -3201,7 +3315,7 @@ bool JSObject::ensureLengthSlow(VM& vm, unsigned length)
         newVectorLength = availableOldLength;
     } else {
         newVectorLength = Butterfly::optimalContiguousVectorLength(
-            propertyCapacity, std::min(length << 1, MAX_STORAGE_VECTOR_LENGTH));
+            propertyCapacity, std::min(length * 2, MAX_STORAGE_VECTOR_LENGTH));
         butterfly = butterfly->growArrayRight(
             vm, this, structure, propertyCapacity, true,
             oldVectorLength * sizeof(EncodedJSValue),
@@ -3221,13 +3335,11 @@ bool JSObject::ensureLengthSlow(VM& vm, unsigned length)
 
     if (newButterfly) {
         butterfly->setVectorLength(newVectorLength);
-        m_butterflyIndexingMask = newButterfly->computeIndexingMask();
         WTF::storeStoreFence();
         m_butterfly.set(vm, this, newButterfly);
     } else {
         WTF::storeStoreFence();
         butterfly->setVectorLength(newVectorLength);
-        m_butterflyIndexingMask = m_butterfly->computeIndexingMask();
     }
 
     return true;
@@ -3238,11 +3350,10 @@ void JSObject::reallocateAndShrinkButterfly(VM& vm, unsigned length)
     ASSERT(length <= MAX_STORAGE_VECTOR_LENGTH);
     ASSERT(hasContiguous(indexingType()) || hasInt32(indexingType()) || hasDouble(indexingType()) || hasUndecided(indexingType()));
     ASSERT(m_butterfly->vectorLength() > length);
-    ASSERT(!m_butterfly->indexingHeader()->preCapacity(structure()));
+    ASSERT(!m_butterfly->indexingHeader()->preCapacity(structure(vm)));
 
     DeferGC deferGC(vm.heap);
-    Butterfly* newButterfly = butterfly()->resizeArray(vm, this, structure(), 0, ArrayStorage::sizeFor(length));
-    m_butterflyIndexingMask = newButterfly->computeIndexingMask();
+    Butterfly* newButterfly = butterfly()->resizeArray(vm, this, structure(vm), 0, ArrayStorage::sizeFor(length));
     newButterfly->setVectorLength(length);
     newButterfly->setPublicLength(length);
     WTF::storeStoreFence();
@@ -3332,11 +3443,9 @@ static bool putDescriptor(ExecState* exec, JSObject* target, PropertyName proper
     VM& vm = exec->vm();
     if (descriptor.isGenericDescriptor() || descriptor.isDataDescriptor()) {
         if (descriptor.isGenericDescriptor() && oldDescriptor.isAccessorDescriptor()) {
-            GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject());
-            if (oldDescriptor.getterPresent())
-                accessor->setGetter(vm, exec->lexicalGlobalObject(), oldDescriptor.getterObject());
-            if (oldDescriptor.setterPresent())
-                accessor->setSetter(vm, exec->lexicalGlobalObject(), oldDescriptor.setterObject());
+            JSObject* getter = oldDescriptor.getterPresent() ? oldDescriptor.getterObject() : nullptr;
+            JSObject* setter = oldDescriptor.setterPresent() ? oldDescriptor.setterObject() : nullptr;
+            GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject(), getter, setter);
             target->putDirectAccessor(exec, propertyName, accessor, attributes | PropertyAttribute::Accessor);
             return true;
         }
@@ -3351,16 +3460,14 @@ static bool putDescriptor(ExecState* exec, JSObject* target, PropertyName proper
         return true;
     }
     attributes &= ~PropertyAttribute::ReadOnly;
-    GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject());
 
-    if (descriptor.getterPresent())
-        accessor->setGetter(vm, exec->lexicalGlobalObject(), descriptor.getterObject());
-    else if (oldDescriptor.getterPresent())
-        accessor->setGetter(vm, exec->lexicalGlobalObject(), oldDescriptor.getterObject());
-    if (descriptor.setterPresent())
-        accessor->setSetter(vm, exec->lexicalGlobalObject(), descriptor.setterObject());
-    else if (oldDescriptor.setterPresent())
-        accessor->setSetter(vm, exec->lexicalGlobalObject(), oldDescriptor.setterObject());
+    JSObject* getter = descriptor.getterPresent()
+        ? descriptor.getterObject() : oldDescriptor.getterPresent()
+        ? oldDescriptor.getterObject() : nullptr;
+    JSObject* setter = descriptor.setterPresent()
+        ? descriptor.setterObject() : oldDescriptor.setterPresent()
+        ? oldDescriptor.setterObject() : nullptr;
+    GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject(), getter, setter);
 
     target->putDirectAccessor(exec, propertyName, accessor, attributes | PropertyAttribute::Accessor);
     return true;
@@ -3387,7 +3494,7 @@ bool validateAndApplyPropertyDescriptor(ExecState* exec, JSObject* object, Prope
         // unless extensions are prevented!
         // Step 2.a
         if (!isExtensible)
-            return typeError(exec, scope, throwException, ASCIILiteral(NonExtensibleObjectPropertyDefineError));
+            return typeError(exec, scope, throwException, NonExtensibleObjectPropertyDefineError);
         if (!object)
             return true;
         // Step 2.c/d
@@ -3409,9 +3516,9 @@ bool validateAndApplyPropertyDescriptor(ExecState* exec, JSObject* object, Prope
     // Filter out invalid changes
     if (!current.configurable()) {
         if (descriptor.configurable())
-            return typeError(exec, scope, throwException, ASCIILiteral(UnconfigurablePropertyChangeConfigurabilityError));
+            return typeError(exec, scope, throwException, UnconfigurablePropertyChangeConfigurabilityError);
         if (descriptor.enumerablePresent() && descriptor.enumerable() != current.enumerable())
-            return typeError(exec, scope, throwException, ASCIILiteral(UnconfigurablePropertyChangeEnumerabilityError));
+            return typeError(exec, scope, throwException, UnconfigurablePropertyChangeEnumerabilityError);
     }
     
     // Step 6.
@@ -3429,7 +3536,7 @@ bool validateAndApplyPropertyDescriptor(ExecState* exec, JSObject* object, Prope
     // Changing between a normal property or an accessor property
     if (descriptor.isDataDescriptor() != current.isDataDescriptor()) {
         if (!current.configurable())
-            return typeError(exec, scope, throwException, ASCIILiteral(UnconfigurablePropertyChangeAccessMechanismError));
+            return typeError(exec, scope, throwException, UnconfigurablePropertyChangeAccessMechanismError);
 
         if (!object)
             return true;
@@ -3444,10 +3551,10 @@ bool validateAndApplyPropertyDescriptor(ExecState* exec, JSObject* object, Prope
     if (descriptor.isDataDescriptor()) {
         if (!current.configurable()) {
             if (!current.writable() && descriptor.writable())
-                return typeError(exec, scope, throwException, ASCIILiteral(UnconfigurablePropertyChangeWritabilityError));
+                return typeError(exec, scope, throwException, UnconfigurablePropertyChangeWritabilityError);
             if (!current.writable()) {
                 if (descriptor.value() && !sameValue(exec, current.value(), descriptor.value()))
-                    return typeError(exec, scope, throwException, ASCIILiteral(ReadonlyPropertyChangeError));
+                    return typeError(exec, scope, throwException, ReadonlyPropertyChangeError);
             }
         }
         if (current.attributesEqual(descriptor) && !descriptor.value())
@@ -3464,11 +3571,11 @@ bool validateAndApplyPropertyDescriptor(ExecState* exec, JSObject* object, Prope
     ASSERT(descriptor.isAccessorDescriptor());
     if (!current.configurable()) {
         if (descriptor.setterPresent() && !(current.setterPresent() && JSValue::strictEqual(exec, current.setter(), descriptor.setter())))
-            return typeError(exec, scope, throwException, ASCIILiteral("Attempting to change the setter of an unconfigurable property."));
+            return typeError(exec, scope, throwException, "Attempting to change the setter of an unconfigurable property."_s);
         if (descriptor.getterPresent() && !(current.getterPresent() && JSValue::strictEqual(exec, current.getter(), descriptor.getter())))
-            return typeError(exec, scope, throwException, ASCIILiteral("Attempting to change the getter of an unconfigurable property."));
+            return typeError(exec, scope, throwException, "Attempting to change the getter of an unconfigurable property."_s);
         if (current.attributes() & PropertyAttribute::CustomAccessor)
-            return typeError(exec, scope, throwException, ASCIILiteral(UnconfigurablePropertyChangeAccessMechanismError));
+            return typeError(exec, scope, throwException, UnconfigurablePropertyChangeAccessMechanismError);
     }
 
     // Step 10/11.
@@ -3477,29 +3584,36 @@ bool validateAndApplyPropertyDescriptor(ExecState* exec, JSObject* object, Prope
     JSValue accessor = object->getDirect(vm, propertyName);
     if (!accessor)
         return false;
-    GetterSetter* getterSetter;
+    JSObject* getter = nullptr;
+    JSObject* setter = nullptr;
     bool getterSetterChanged = false;
+
     if (accessor.isCustomGetterSetter()) {
-        getterSetter = GetterSetter::create(vm, exec->lexicalGlobalObject());
         auto* customGetterSetter = jsCast<CustomGetterSetter*>(accessor);
         if (customGetterSetter->setter())
-            getterSetter->setSetter(vm, exec->lexicalGlobalObject(), getCustomGetterSetterFunctionForGetterSetter(exec, propertyName, customGetterSetter, JSCustomGetterSetterFunction::Type::Setter));
+            setter = getCustomGetterSetterFunctionForGetterSetter(exec, propertyName, customGetterSetter, JSCustomGetterSetterFunction::Type::Setter);
         if (customGetterSetter->getter())
-            getterSetter->setGetter(vm, exec->lexicalGlobalObject(), getCustomGetterSetterFunctionForGetterSetter(exec, propertyName, customGetterSetter, JSCustomGetterSetterFunction::Type::Getter));
+            getter = getCustomGetterSetterFunctionForGetterSetter(exec, propertyName, customGetterSetter, JSCustomGetterSetterFunction::Type::Getter);
     } else {
         ASSERT(accessor.isGetterSetter());
-        getterSetter = asGetterSetter(accessor);
+        auto* getterSetter = jsCast<GetterSetter*>(accessor);
+        getter = getterSetter->getter();
+        setter = getterSetter->setter();
     }
     if (descriptor.setterPresent()) {
-        getterSetter = getterSetter->withSetter(vm, exec->lexicalGlobalObject(), descriptor.setterObject());
+        setter = descriptor.setterObject();
         getterSetterChanged = true;
     }
     if (descriptor.getterPresent()) {
-        getterSetter = getterSetter->withGetter(vm, exec->lexicalGlobalObject(), descriptor.getterObject());
+        getter = descriptor.getterObject();
         getterSetterChanged = true;
     }
+
     if (current.attributesEqual(descriptor) && !getterSetterChanged)
         return true;
+
+    GetterSetter* getterSetter = GetterSetter::create(vm, exec->lexicalGlobalObject(), getter, setter);
+
     object->methodTable(vm)->deleteProperty(object, exec, propertyName);
     RETURN_IF_EXCEPTION(scope, false);
     unsigned attrs = descriptor.attributesOverridingCurrent(current);
@@ -3521,8 +3635,7 @@ bool JSObject::defineOwnNonIndexProperty(ExecState* exec, PropertyName propertyN
     bool isCurrentDefined = getOwnPropertyDescriptor(exec, propertyName, current);
     bool isExtensible = this->isExtensible(exec);
     RETURN_IF_EXCEPTION(throwScope, false);
-    throwScope.release();
-    return validateAndApplyPropertyDescriptor(exec, this, propertyName, isExtensible, descriptor, isCurrentDefined, current, throwException);
+    RELEASE_AND_RETURN(throwScope, validateAndApplyPropertyDescriptor(exec, this, propertyName, isExtensible, descriptor, isCurrentDefined, current, throwException));
 }
 
 bool JSObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException)
@@ -3543,7 +3656,7 @@ bool JSObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName
 
 void JSObject::convertToDictionary(VM& vm)
 {
-    DeferredStructureTransitionWatchpointFire deferredWatchpointFire;
+    DeferredStructureTransitionWatchpointFire deferredWatchpointFire(vm, structure(vm));
     setStructure(
         vm, Structure::toCacheableDictionaryTransition(vm, structure(vm), &deferredWatchpointFire));
 }
@@ -3553,11 +3666,11 @@ void JSObject::shiftButterflyAfterFlattening(const GCSafeConcurrentJSLocker&, VM
     // This could interleave visitChildren because some old structure could have been a non
     // dictionary structure. We have to be crazy careful. But, we are guaranteed to be holding
     // the structure's lock right now, and that helps a bit.
-    
+
     Butterfly* oldButterfly = this->butterfly();
     size_t preCapacity;
     size_t indexingPayloadSizeInBytes;
-    bool hasIndexingHeader = this->hasIndexingHeader();
+    bool hasIndexingHeader = this->hasIndexingHeader(vm);
     if (UNLIKELY(hasIndexingHeader)) {
         preCapacity = oldButterfly->indexingHeader()->preCapacity(structure);
         indexingPayloadSizeInBytes = oldButterfly->indexingHeader()->indexingPayloadSizeInBytes(structure);
@@ -3691,9 +3804,8 @@ JSValue JSObject::getMethod(ExecState* exec, CallData& callData, CallType& callT
     return method;
 }
 
-bool JSObject::anyObjectInChainMayInterceptIndexedAccesses() const
+bool JSObject::anyObjectInChainMayInterceptIndexedAccesses(VM& vm) const
 {
-    VM& vm = *this->vm();
     for (const JSObject* current = this; ;) {
         if (current->structure(vm)->mayInterceptIndexedAccesses())
             return true;
@@ -3709,7 +3821,7 @@ bool JSObject::anyObjectInChainMayInterceptIndexedAccesses() const
 bool JSObject::prototypeChainMayInterceptStoreTo(VM& vm, PropertyName propertyName)
 {
     if (parseIndex(propertyName))
-        return anyObjectInChainMayInterceptIndexedAccesses();
+        return anyObjectInChainMayInterceptIndexedAccesses(vm);
     
     for (JSObject* current = this; ;) {
         JSValue prototype = current->getPrototypeDirect(vm);
@@ -3730,14 +3842,14 @@ bool JSObject::prototypeChainMayInterceptStoreTo(VM& vm, PropertyName propertyNa
     }
 }
 
-bool JSObject::needsSlowPutIndexing() const
+bool JSObject::needsSlowPutIndexing(VM& vm) const
 {
-    return anyObjectInChainMayInterceptIndexedAccesses() || globalObject()->isHavingABadTime();
+    return anyObjectInChainMayInterceptIndexedAccesses(vm) || globalObject(vm)->isHavingABadTime();
 }
 
-NonPropertyTransition JSObject::suggestedArrayStorageTransition() const
+NonPropertyTransition JSObject::suggestedArrayStorageTransition(VM& vm) const
 {
-    if (needsSlowPutIndexing())
+    if (needsSlowPutIndexing(vm))
         return NonPropertyTransition::AllocateSlowPutArrayStorage;
     
     return NonPropertyTransition::AllocateArrayStorage;