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 "ClassInfo.h"
30#include "GetterSetter.h"
31#include "JSObject.h"
32#include "JSCInlines.h"
33#include "PropertySlot.h"
34#include "SlotVisitor.h"
35#include "Structure.h"
36#include "TypeError.h"
37
38namespace JSC {
39
40const ClassInfo SparseArrayValueMap::s_info = { "SparseArrayValueMap", nullptr, nullptr, nullptr, CREATE_METHOD_TABLE(SparseArrayValueMap) };
41
42SparseArrayValueMap::SparseArrayValueMap(VM& vm)
43 : Base(vm, vm.sparseArrayValueMapStructure.get())
44{
45}
46
47void SparseArrayValueMap::finishCreation(VM& vm)
48{
49 Base::finishCreation(vm);
50}
51
52SparseArrayValueMap* SparseArrayValueMap::create(VM& vm)
53{
54 SparseArrayValueMap* result = new (NotNull, allocateCell<SparseArrayValueMap>(vm.heap)) SparseArrayValueMap(vm);
55 result->finishCreation(vm);
56 return result;
57}
58
59void SparseArrayValueMap::destroy(JSCell* cell)
60{
61 static_cast<SparseArrayValueMap*>(cell)->SparseArrayValueMap::~SparseArrayValueMap();
62}
63
64Structure* SparseArrayValueMap::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
65{
66 return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info());
67}
68
69SparseArrayValueMap::AddResult SparseArrayValueMap::add(JSObject* array, unsigned i)
70{
71 AddResult result;
72 size_t increasedCapacity = 0;
73 {
74 auto locker = holdLock(cellLock());
75 result = m_map.add(i, SparseArrayEntry());
76 size_t capacity = m_map.capacity();
77 if (capacity > m_reportedCapacity) {
78 increasedCapacity = capacity - m_reportedCapacity;
79 m_reportedCapacity = capacity;
80 }
81 }
82 if (increasedCapacity)
83 Heap::heap(array)->reportExtraMemoryAllocated(increasedCapacity * sizeof(Map::KeyValuePairType));
84 return result;
85}
86
87void SparseArrayValueMap::remove(iterator it)
88{
89 auto locker = holdLock(cellLock());
90 m_map.remove(it);
91}
92
93void SparseArrayValueMap::remove(unsigned i)
94{
95 auto locker = holdLock(cellLock());
96 m_map.remove(i);
97}
98
99bool SparseArrayValueMap::putEntry(ExecState* exec, JSObject* array, unsigned i, JSValue value, bool shouldThrow)
100{
101 VM& vm = exec->vm();
102 auto scope = DECLARE_THROW_SCOPE(vm);
103 ASSERT(value);
104
105 AddResult result = add(array, i);
106 SparseArrayEntry& entry = result.iterator->value;
107
108 // To save a separate find & add, we first always add to the sparse map.
109 // In the uncommon case that this is a new property, and the array is not
110 // extensible, this is not the right thing to have done - so remove again.
111 if (result.isNewEntry && !array->isStructureExtensible(vm)) {
112 remove(result.iterator);
113 return typeError(exec, scope, shouldThrow, ReadonlyPropertyWriteError);
114 }
115
116 RELEASE_AND_RETURN(scope, entry.put(exec, array, this, value, shouldThrow));
117}
118
119bool SparseArrayValueMap::putDirect(ExecState* exec, JSObject* array, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode)
120{
121 VM& vm = exec->vm();
122 auto scope = DECLARE_THROW_SCOPE(vm);
123 ASSERT(value);
124
125 bool shouldThrow = (mode == PutDirectIndexShouldThrow);
126
127 AddResult result = add(array, i);
128 SparseArrayEntry& entry = result.iterator->value;
129
130 // To save a separate find & add, we first always add to the sparse map.
131 // In the uncommon case that this is a new property, and the array is not
132 // extensible, this is not the right thing to have done - so remove again.
133 if (mode != PutDirectIndexLikePutDirect && result.isNewEntry && !array->isStructureExtensible(vm)) {
134 remove(result.iterator);
135 return typeError(exec, scope, shouldThrow, NonExtensibleObjectPropertyDefineError);
136 }
137
138 if (entry.attributes() & PropertyAttribute::ReadOnly)
139 return typeError(exec, scope, shouldThrow, ReadonlyPropertyWriteError);
140
141 entry.forceSet(vm, this, value, attributes);
142 return true;
143}
144
145JSValue SparseArrayValueMap::getConcurrently(unsigned i)
146{
147 auto locker = holdLock(cellLock());
148 auto iterator = m_map.find(i);
149 if (iterator == m_map.end())
150 return JSValue();
151 return iterator->value.getConcurrently();
152}
153
154void SparseArrayEntry::get(JSObject* thisObject, PropertySlot& slot) const
155{
156 JSValue value = Base::get();
157 ASSERT(value);
158
159 if (LIKELY(!value.isGetterSetter())) {
160 slot.setValue(thisObject, m_attributes, value);
161 return;
162 }
163
164 slot.setGetterSlot(thisObject, m_attributes, jsCast<GetterSetter*>(value));
165}
166
167void SparseArrayEntry::get(PropertyDescriptor& descriptor) const
168{
169 descriptor.setDescriptor(Base::get(), m_attributes);
170}
171
172JSValue SparseArrayEntry::getConcurrently() const
173{
174 // These attributes and value can be updated while executing getConcurrently.
175 // But this is OK since attributes should be never weaken once it gets DontDelete and ReadOnly.
176 // By emitting store-store-fence and load-load-fence between value setting and attributes setting,
177 // we can ensure that the value is what we want once the attributes get ReadOnly & DontDelete:
178 // once attributes get this state, the value should not be changed.
179 unsigned attributes = m_attributes;
180 Dependency attributesDependency = Dependency::fence(attributes);
181 if (attributes & PropertyAttribute::Accessor)
182 return JSValue();
183
184 if (!(attributes & PropertyAttribute::ReadOnly))
185 return JSValue();
186
187 if (!(attributes & PropertyAttribute::DontDelete))
188 return JSValue();
189
190 return attributesDependency.consume(this)->Base::get();
191}
192
193bool SparseArrayEntry::put(ExecState* exec, JSValue thisValue, SparseArrayValueMap* map, JSValue value, bool shouldThrow)
194{
195 VM& vm = exec->vm();
196 auto scope = DECLARE_THROW_SCOPE(vm);
197
198 if (!(m_attributes & PropertyAttribute::Accessor)) {
199 if (m_attributes & PropertyAttribute::ReadOnly)
200 return typeError(exec, scope, shouldThrow, ReadonlyPropertyWriteError);
201
202 set(vm, map, value);
203 return true;
204 }
205
206 RELEASE_AND_RETURN(scope, callSetter(exec, thisValue, Base::get(), value, shouldThrow ? StrictMode : NotStrictMode));
207}
208
209JSValue SparseArrayEntry::getNonSparseMode() const
210{
211 ASSERT(!m_attributes);
212 return Base::get();
213}
214
215void SparseArrayValueMap::visitChildren(JSCell* cell, SlotVisitor& visitor)
216{
217 Base::visitChildren(cell, visitor);
218 SparseArrayValueMap* thisObject = jsCast<SparseArrayValueMap*>(cell);
219 {
220 auto locker = holdLock(thisObject->cellLock());
221 for (auto& entry : thisObject->m_map)
222 visitor.append(entry.value.asValue());
223 }
224 visitor.reportExtraMemoryVisited(thisObject->m_reportedCapacity * sizeof(Map::KeyValuePairType));
225}
226
227} // namespace JSC
228
229