SparseArrayValueMap.cpp
1 /* 2 * Copyright (C) 2011-2017 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "config.h" 27 #include "SparseArrayValueMap.h" 28 29 #include "GetterSetter.h" 30 #include "JSCJSValueInlines.h" 31 #include "JSObjectInlines.h" 32 #include "PropertySlot.h" 33 #include "StructureInlines.h" 34 #include "TypeError.h" 35 36 namespace JSC { 37 38 const ClassInfo SparseArrayValueMap::s_info = { "SparseArrayValueMap", nullptr, nullptr, nullptr, CREATE_METHOD_TABLE(SparseArrayValueMap) }; 39 40 SparseArrayValueMap::SparseArrayValueMap(VM& vm) 41 : Base(vm, vm.sparseArrayValueMapStructure.get()) 42 { 43 } 44 45 void SparseArrayValueMap::finishCreation(VM& vm) 46 { 47 Base::finishCreation(vm); 48 } 49 50 SparseArrayValueMap* SparseArrayValueMap::create(VM& vm) 51 { 52 SparseArrayValueMap* result = new (NotNull, allocateCell<SparseArrayValueMap>(vm.heap)) SparseArrayValueMap(vm); 53 result->finishCreation(vm); 54 return result; 55 } 56 57 void SparseArrayValueMap::destroy(JSCell* cell) 58 { 59 static_cast<SparseArrayValueMap*>(cell)->SparseArrayValueMap::~SparseArrayValueMap(); 60 } 61 62 Structure* SparseArrayValueMap::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) 63 { 64 return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info()); 65 } 66 67 SparseArrayValueMap::AddResult SparseArrayValueMap::add(JSObject* array, unsigned i) 68 { 69 AddResult result; 70 size_t increasedCapacity = 0; 71 { 72 auto locker = holdLock(cellLock()); 73 result = m_map.add(i, SparseArrayEntry()); 74 size_t capacity = m_map.capacity(); 75 if (capacity > m_reportedCapacity) { 76 increasedCapacity = capacity - m_reportedCapacity; 77 m_reportedCapacity = capacity; 78 } 79 } 80 if (increasedCapacity) 81 Heap::heap(array)->reportExtraMemoryAllocated(increasedCapacity * sizeof(Map::KeyValuePairType)); 82 return result; 83 } 84 85 void SparseArrayValueMap::remove(iterator it) 86 { 87 auto locker = holdLock(cellLock()); 88 m_map.remove(it); 89 } 90 91 void SparseArrayValueMap::remove(unsigned i) 92 { 93 auto locker = holdLock(cellLock()); 94 m_map.remove(i); 95 } 96 97 bool SparseArrayValueMap::putEntry(JSGlobalObject* globalObject, JSObject* array, unsigned i, JSValue value, bool shouldThrow) 98 { 99 VM& vm = globalObject->vm(); 100 auto scope = DECLARE_THROW_SCOPE(vm); 101 ASSERT(value); 102 103 AddResult result = add(array, i); 104 SparseArrayEntry& entry = result.iterator->value; 105 106 // To save a separate find & add, we first always add to the sparse map. 107 // In the uncommon case that this is a new property, and the array is not 108 // extensible, this is not the right thing to have done - so remove again. 109 if (result.isNewEntry && !array->isStructureExtensible(vm)) { 110 remove(result.iterator); 111 return typeError(globalObject, scope, shouldThrow, ReadonlyPropertyWriteError); 112 } 113 114 RELEASE_AND_RETURN(scope, entry.put(globalObject, array, this, value, shouldThrow)); 115 } 116 117 bool SparseArrayValueMap::putDirect(JSGlobalObject* globalObject, JSObject* array, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode) 118 { 119 VM& vm = globalObject->vm(); 120 auto scope = DECLARE_THROW_SCOPE(vm); 121 ASSERT(value); 122 123 bool shouldThrow = (mode == PutDirectIndexShouldThrow); 124 125 AddResult result = add(array, i); 126 SparseArrayEntry& entry = result.iterator->value; 127 128 // To save a separate find & add, we first always add to the sparse map. 129 // In the uncommon case that this is a new property, and the array is not 130 // extensible, this is not the right thing to have done - so remove again. 131 if (mode != PutDirectIndexLikePutDirect && result.isNewEntry && !array->isStructureExtensible(vm)) { 132 remove(result.iterator); 133 return typeError(globalObject, scope, shouldThrow, NonExtensibleObjectPropertyDefineError); 134 } 135 136 if (entry.attributes() & PropertyAttribute::ReadOnly) 137 return typeError(globalObject, scope, shouldThrow, ReadonlyPropertyWriteError); 138 139 entry.forceSet(vm, this, value, attributes); 140 return true; 141 } 142 143 JSValue SparseArrayValueMap::getConcurrently(unsigned i) 144 { 145 auto locker = holdLock(cellLock()); 146 auto iterator = m_map.find(i); 147 if (iterator == m_map.end()) 148 return JSValue(); 149 return iterator->value.getConcurrently(); 150 } 151 152 void SparseArrayEntry::get(JSObject* thisObject, PropertySlot& slot) const 153 { 154 JSValue value = Base::get(); 155 ASSERT(value); 156 157 if (LIKELY(!value.isGetterSetter())) { 158 slot.setValue(thisObject, m_attributes, value); 159 return; 160 } 161 162 slot.setGetterSlot(thisObject, m_attributes, jsCast<GetterSetter*>(value)); 163 } 164 165 void SparseArrayEntry::get(PropertyDescriptor& descriptor) const 166 { 167 descriptor.setDescriptor(Base::get(), m_attributes); 168 } 169 170 JSValue SparseArrayEntry::getConcurrently() const 171 { 172 // These attributes and value can be updated while executing getConcurrently. 173 // But this is OK since attributes should be never weaken once it gets DontDelete and ReadOnly. 174 // By emitting store-store-fence and load-load-fence between value setting and attributes setting, 175 // we can ensure that the value is what we want once the attributes get ReadOnly & DontDelete: 176 // once attributes get this state, the value should not be changed. 177 unsigned attributes = m_attributes; 178 Dependency attributesDependency = Dependency::fence(attributes); 179 if (attributes & PropertyAttribute::Accessor) 180 return JSValue(); 181 182 if (!(attributes & PropertyAttribute::ReadOnly)) 183 return JSValue(); 184 185 if (!(attributes & PropertyAttribute::DontDelete)) 186 return JSValue(); 187 188 return attributesDependency.consume(this)->Base::get(); 189 } 190 191 bool SparseArrayEntry::put(JSGlobalObject* globalObject, JSValue thisValue, SparseArrayValueMap* map, JSValue value, bool shouldThrow) 192 { 193 VM& vm = globalObject->vm(); 194 auto scope = DECLARE_THROW_SCOPE(vm); 195 196 if (!(m_attributes & PropertyAttribute::Accessor)) { 197 if (m_attributes & PropertyAttribute::ReadOnly) 198 return typeError(globalObject, scope, shouldThrow, ReadonlyPropertyWriteError); 199 200 set(vm, map, value); 201 return true; 202 } 203 204 RELEASE_AND_RETURN(scope, callSetter(globalObject, thisValue, Base::get(), value, shouldThrow ? ECMAMode::strict() : ECMAMode::sloppy())); 205 } 206 207 JSValue SparseArrayEntry::getNonSparseMode() const 208 { 209 ASSERT(!m_attributes); 210 return Base::get(); 211 } 212 213 void SparseArrayValueMap::visitChildren(JSCell* cell, SlotVisitor& visitor) 214 { 215 SparseArrayValueMap* thisObject = jsCast<SparseArrayValueMap*>(cell); 216 ASSERT_GC_OBJECT_INHERITS(thisObject, info()); 217 Base::visitChildren(cell, visitor); 218 { 219 auto locker = holdLock(thisObject->cellLock()); 220 for (auto& entry : thisObject->m_map) 221 visitor.append(entry.value.asValue()); 222 } 223 visitor.reportExtraMemoryVisited(thisObject->m_reportedCapacity * sizeof(Map::KeyValuePairType)); 224 } 225 226 } // namespace JSC 227