]>
git.saurik.com Git - apple/javascriptcore.git/blob - runtime/MapData.cpp
2 * Copyright (C) 2013 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
29 #include "CopiedAllocator.h"
30 #include "CopyVisitorInlines.h"
31 #include "ExceptionHelpers.h"
32 #include "JSCJSValueInlines.h"
33 #include "SlotVisitorInlines.h"
35 #include <wtf/CryptographicallyRandomNumber.h>
36 #include <wtf/MathExtras.h>
41 const ClassInfo
MapData::s_info
= { "MapData", 0, 0, 0, CREATE_METHOD_TABLE(MapData
) };
43 static const int32_t minimumMapSize
= 8;
45 MapData::MapData(VM
& vm
)
46 : Base(vm
, vm
.mapDataStructure
.get())
55 MapData::Entry
* MapData::find(CallFrame
* callFrame
, KeyType key
)
57 if (key
.value
.isString()) {
58 auto iter
= m_stringKeyedTable
.find(asString(key
.value
)->value(callFrame
).impl());
59 if (iter
== m_stringKeyedTable
.end())
61 return &m_entries
[iter
->value
];
63 if (key
.value
.isCell()) {
64 auto iter
= m_cellKeyedTable
.find(key
.value
.asCell());
65 if (iter
== m_cellKeyedTable
.end())
67 return &m_entries
[iter
->value
];
70 auto iter
= m_valueKeyedTable
.find(JSValue::encode(key
.value
));
71 if (iter
== m_valueKeyedTable
.end())
73 return &m_entries
[iter
->value
];
76 bool MapData::contains(CallFrame
* callFrame
, KeyType key
)
78 return find(callFrame
, key
);
81 template <typename Map
, typename Key
> MapData::Entry
* MapData::add(CallFrame
* callFrame
, Map
& map
, Key key
, KeyType keyValue
)
83 typename
Map::iterator location
= map
.find(key
);
84 if (location
!= map
.end())
85 return &m_entries
[location
->value
];
87 if (!ensureSpaceForAppend(callFrame
))
90 auto result
= map
.add(key
, m_size
);
91 RELEASE_ASSERT(result
.isNewEntry
);
92 Entry
* entry
= &m_entries
[m_size
++];
94 entry
->key
.set(callFrame
->vm(), this, keyValue
.value
);
98 void MapData::set(CallFrame
* callFrame
, KeyType key
, JSValue value
)
100 Entry
* location
= add(callFrame
, key
);
103 location
->value
.set(callFrame
->vm(), this, value
);
106 MapData::Entry
* MapData::add(CallFrame
* callFrame
, KeyType key
)
108 if (key
.value
.isString())
109 return add(callFrame
, m_stringKeyedTable
, asString(key
.value
)->value(callFrame
).impl(), key
);
110 if (key
.value
.isCell())
111 return add(callFrame
, m_cellKeyedTable
, key
.value
.asCell(), key
);
112 return add(callFrame
, m_valueKeyedTable
, JSValue::encode(key
.value
), key
);
115 JSValue
MapData::get(CallFrame
* callFrame
, KeyType key
)
117 if (Entry
* entry
= find(callFrame
, key
))
118 return entry
->value
.get();
122 bool MapData::remove(CallFrame
* callFrame
, KeyType key
)
125 if (key
.value
.isString()) {
126 auto iter
= m_stringKeyedTable
.find(asString(key
.value
)->value(callFrame
).impl());
127 if (iter
== m_stringKeyedTable
.end())
129 location
= iter
->value
;
130 m_stringKeyedTable
.remove(iter
);
131 } else if (key
.value
.isCell()) {
132 auto iter
= m_cellKeyedTable
.find(key
.value
.asCell());
133 if (iter
== m_cellKeyedTable
.end())
135 location
= iter
->value
;
136 m_cellKeyedTable
.remove(iter
);
138 auto iter
= m_valueKeyedTable
.find(JSValue::encode(key
.value
));
139 if (iter
== m_valueKeyedTable
.end())
141 location
= iter
->value
;
142 m_valueKeyedTable
.remove(iter
);
144 m_entries
[location
].key
.clear();
145 m_entries
[location
].value
.clear();
150 void MapData::replaceAndPackBackingStore(Entry
* destination
, int32_t newCapacity
)
152 ASSERT(shouldPack());
154 RELEASE_ASSERT(newCapacity
> 0);
155 for (int32_t i
= 0; i
< m_size
; i
++) {
156 Entry
& entry
= m_entries
[i
];
159 ASSERT(newEnd
< newCapacity
);
160 destination
[newEnd
] = entry
;
162 // We overwrite the old entry with a forwarding index for the new entry,
163 // so that we can fix up our hash tables below without doing additional
165 entry
.value
.setWithoutWriteBarrier(jsNumber(newEnd
));
169 // Fixup for the hashmaps
170 for (auto ptr
= m_valueKeyedTable
.begin(); ptr
!= m_valueKeyedTable
.end(); ++ptr
)
171 ptr
->value
= m_entries
[ptr
->value
].value
.get().asInt32();
172 for (auto ptr
= m_cellKeyedTable
.begin(); ptr
!= m_cellKeyedTable
.end(); ++ptr
)
173 ptr
->value
= m_entries
[ptr
->value
].value
.get().asInt32();
174 for (auto ptr
= m_stringKeyedTable
.begin(); ptr
!= m_stringKeyedTable
.end(); ++ptr
)
175 ptr
->value
= m_entries
[ptr
->value
].value
.get().asInt32();
177 ASSERT((m_size
- newEnd
) == m_deletedCount
);
180 m_capacity
= newCapacity
;
182 m_entries
= destination
;
186 void MapData::replaceBackingStore(Entry
* destination
, int32_t newCapacity
)
188 ASSERT(!shouldPack());
189 RELEASE_ASSERT(newCapacity
> 0);
190 ASSERT(newCapacity
>= m_capacity
);
191 memcpy(destination
, m_entries
, sizeof(Entry
) * m_size
);
192 m_capacity
= newCapacity
;
193 m_entries
= destination
;
196 CheckedBoolean
MapData::ensureSpaceForAppend(CallFrame
* callFrame
)
198 if (m_capacity
> m_size
)
201 size_t requiredSize
= std::max(m_capacity
+ (m_capacity
/ 2) + 1, minimumMapSize
);
202 void* newStorage
= 0;
203 DeferGC
defer(*callFrame
->heap());
204 if (!callFrame
->heap()->tryAllocateStorage(this, requiredSize
* sizeof(Entry
), &newStorage
)) {
205 throwOutOfMemoryError(callFrame
);
208 Entry
* newEntries
= static_cast<Entry
*>(newStorage
);
210 replaceAndPackBackingStore(newEntries
, requiredSize
);
212 replaceBackingStore(newEntries
, requiredSize
);
213 callFrame
->heap()->writeBarrier(this);
217 void MapData::visitChildren(JSCell
* cell
, SlotVisitor
& visitor
)
219 Base::visitChildren(cell
, visitor
);
220 MapData
* thisObject
= jsCast
<MapData
*>(cell
);
221 Entry
* entries
= thisObject
->m_entries
;
224 size_t size
= thisObject
->m_size
;
225 if (thisObject
->m_deletedCount
) {
226 for (size_t i
= 0; i
< size
; i
++) {
229 visitor
.append(&entries
[i
].key
);
230 visitor
.append(&entries
[i
].value
);
233 visitor
.appendValues(&entries
[0].key
, size
* (sizeof(Entry
) / sizeof(WriteBarrier
<Unknown
>)));
235 visitor
.copyLater(thisObject
, MapBackingStoreCopyToken
, entries
, thisObject
->capacityInBytes());
238 void MapData::copyBackingStore(JSCell
* cell
, CopyVisitor
& visitor
, CopyToken token
)
240 MapData
* thisObject
= jsCast
<MapData
*>(cell
);
241 if (token
== MapBackingStoreCopyToken
&& visitor
.checkIfShouldCopy(thisObject
->m_entries
)) {
242 Entry
* oldEntries
= thisObject
->m_entries
;
243 Entry
* newEntries
= static_cast<Entry
*>(visitor
.allocateNewSpace(thisObject
->capacityInBytes()));
244 if (thisObject
->shouldPack())
245 thisObject
->replaceAndPackBackingStore(newEntries
, thisObject
->m_capacity
);
247 thisObject
->replaceBackingStore(newEntries
, thisObject
->m_capacity
);
248 visitor
.didCopy(oldEntries
, thisObject
->capacityInBytes());
250 Base::copyBackingStore(cell
, visitor
, token
);
253 void MapData::destroy(JSCell
* cell
)
255 static_cast<MapData
*>(cell
)->~MapData();