Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
3 : * This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #ifndef vm_Shape_inl_h
8 : #define vm_Shape_inl_h
9 :
10 : #include "vm/Shape.h"
11 :
12 : #include "mozilla/TypeTraits.h"
13 :
14 : #include "jsobj.h"
15 :
16 : #include "gc/Allocator.h"
17 : #include "vm/Interpreter.h"
18 : #include "vm/TypedArrayObject.h"
19 :
20 : #include "jsatominlines.h"
21 : #include "jscntxtinlines.h"
22 : #include "jsgcinlines.h"
23 :
24 : namespace js {
25 :
26 : inline
27 127230 : AutoKeepShapeTables::AutoKeepShapeTables(JSContext* cx)
28 : : cx_(cx),
29 127230 : prev_(cx->zone()->keepShapeTables())
30 : {
31 127233 : cx->zone()->setKeepShapeTables(true);
32 127233 : }
33 :
34 : inline
35 254464 : AutoKeepShapeTables::~AutoKeepShapeTables()
36 : {
37 127232 : cx_->zone()->setKeepShapeTables(prev_);
38 127232 : }
39 :
40 : inline
41 9010 : StackBaseShape::StackBaseShape(JSContext* cx, const Class* clasp, uint32_t objectFlags)
42 : : flags(objectFlags),
43 9010 : clasp(clasp)
44 9010 : {}
45 :
46 : MOZ_ALWAYS_INLINE Shape*
47 314580 : Shape::search(JSContext* cx, jsid id)
48 : {
49 314580 : return search(cx, this, id);
50 : }
51 :
52 : MOZ_ALWAYS_INLINE bool
53 427129 : Shape::maybeCreateTableForLookup(JSContext* cx)
54 : {
55 427129 : if (hasTable())
56 162522 : return true;
57 :
58 264606 : if (!inDictionary() && numLinearSearches() < LINEAR_SEARCHES_MAX) {
59 109448 : incrementNumLinearSearches();
60 109448 : return true;
61 : }
62 :
63 155166 : if (!isBigEnoughForAShapeTable())
64 153397 : return true;
65 :
66 1769 : return Shape::hashify(cx, this);
67 : }
68 :
69 : template<MaybeAdding Adding>
70 : /* static */ inline bool
71 9809 : Shape::search(JSContext* cx, Shape* start, jsid id, const AutoKeepShapeTables& keep,
72 : Shape** pshape, ShapeTable::Entry** pentry)
73 : {
74 9809 : if (start->inDictionary()) {
75 853 : ShapeTable* table = start->ensureTableForDictionary(cx, keep);
76 853 : if (!table)
77 0 : return false;
78 853 : *pentry = &table->search<Adding>(id, keep);
79 853 : *pshape = (*pentry)->shape();
80 853 : return true;
81 : }
82 :
83 8956 : *pentry = nullptr;
84 8956 : *pshape = Shape::search<Adding>(cx, start, id);
85 8956 : return true;
86 : }
87 :
88 : template<MaybeAdding Adding>
89 : /* static */ MOZ_ALWAYS_INLINE Shape*
90 427129 : Shape::search(JSContext* cx, Shape* start, jsid id)
91 : {
92 427129 : if (start->maybeCreateTableForLookup(cx)) {
93 689975 : JS::AutoCheckCannotGC nogc;
94 427133 : if (ShapeTable* table = start->maybeTable(nogc)) {
95 164291 : ShapeTable::Entry& entry = table->search<Adding>(id, nogc);
96 164291 : return entry.shape();
97 : }
98 : } else {
99 : // Just do a linear search.
100 0 : cx->recoverFromOutOfMemory();
101 : }
102 :
103 262845 : return start->searchLinear(id);
104 : }
105 :
106 : inline Shape*
107 72598 : Shape::new_(JSContext* cx, Handle<StackShape> other, uint32_t nfixed)
108 : {
109 72598 : Shape* shape = other.isAccessorShape()
110 72598 : ? js::Allocate<AccessorShape>(cx)
111 72598 : : js::Allocate<Shape>(cx);
112 72598 : if (!shape) {
113 0 : ReportOutOfMemory(cx);
114 0 : return nullptr;
115 : }
116 :
117 72598 : if (other.isAccessorShape())
118 15935 : new (shape) AccessorShape(other, nfixed);
119 : else
120 56663 : new (shape) Shape(other, nfixed);
121 :
122 72596 : return shape;
123 : }
124 :
125 : inline void
126 0 : Shape::updateBaseShapeAfterMovingGC()
127 : {
128 0 : BaseShape* base = base_;
129 0 : if (IsForwarded(base))
130 0 : base_.unsafeSet(Forwarded(base));
131 0 : }
132 :
133 : template<class ObjectSubclass>
134 : /* static */ inline bool
135 951 : EmptyShape::ensureInitialCustomShape(JSContext* cx, Handle<ObjectSubclass*> obj)
136 : {
137 : static_assert(mozilla::IsBaseOf<JSObject, ObjectSubclass>::value,
138 : "ObjectSubclass must be a subclass of JSObject");
139 :
140 : // If the provided object has a non-empty shape, it was given the cached
141 : // initial shape when created: nothing to do.
142 951 : if (!obj->empty())
143 886 : return true;
144 :
145 : // If no initial shape was assigned, do so.
146 130 : RootedShape shape(cx, ObjectSubclass::assignInitialShape(cx, obj));
147 65 : if (!shape)
148 0 : return false;
149 65 : MOZ_ASSERT(!obj->empty());
150 :
151 : // If the object is a standard prototype -- |RegExp.prototype|,
152 : // |String.prototype|, |RangeError.prototype|, &c. -- GlobalObject.cpp's
153 : // |CreateBlankProto| marked it as a delegate. These are the only objects
154 : // of this class that won't use the standard prototype, and there's no
155 : // reason to pollute the initial shape cache with entries for them.
156 65 : if (obj->isDelegate())
157 48 : return true;
158 :
159 : // Cache the initial shape for non-prototype objects, however, so that
160 : // future instances will begin life with that shape.
161 34 : RootedObject proto(cx, obj->staticPrototype());
162 17 : EmptyShape::insertInitialShape(cx, shape, proto);
163 17 : return true;
164 : }
165 :
166 : inline
167 19963 : AutoRooterGetterSetter::Inner::Inner(JSContext* cx, uint8_t attrs,
168 19963 : GetterOp* pgetter_, SetterOp* psetter_)
169 : : CustomAutoRooter(cx), attrs(attrs),
170 19963 : pgetter(pgetter_), psetter(psetter_)
171 19963 : {}
172 :
173 : inline
174 131523 : AutoRooterGetterSetter::AutoRooterGetterSetter(JSContext* cx, uint8_t attrs,
175 : GetterOp* pgetter, SetterOp* psetter
176 131523 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
177 : {
178 131523 : if (attrs & (JSPROP_GETTER | JSPROP_SETTER))
179 19659 : inner.emplace(cx, attrs, pgetter, psetter);
180 131523 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
181 131523 : }
182 :
183 : inline
184 8106 : AutoRooterGetterSetter::AutoRooterGetterSetter(JSContext* cx, uint8_t attrs,
185 : JSNative* pgetter, JSNative* psetter
186 8106 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
187 : {
188 8106 : if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
189 608 : inner.emplace(cx, attrs, reinterpret_cast<GetterOp*>(pgetter),
190 912 : reinterpret_cast<SetterOp*>(psetter));
191 : }
192 8106 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
193 8106 : }
194 :
195 : static inline uint8_t
196 13151 : GetPropertyAttributes(JSObject* obj, PropertyResult prop)
197 : {
198 13151 : MOZ_ASSERT(obj->isNative());
199 :
200 13151 : if (prop.isDenseOrTypedArrayElement()) {
201 0 : if (obj->is<TypedArrayObject>())
202 0 : return JSPROP_ENUMERATE | JSPROP_PERMANENT;
203 0 : return obj->as<NativeObject>().getElementsHeader()->elementAttributes();
204 : }
205 :
206 13151 : return prop.shape()->attributes();
207 : }
208 :
209 : /*
210 : * Double hashing needs the second hash code to be relatively prime to table
211 : * size, so we simply make hash2 odd.
212 : */
213 : MOZ_ALWAYS_INLINE HashNumber
214 306519 : Hash1(HashNumber hash0, uint32_t shift)
215 : {
216 306519 : return hash0 >> shift;
217 : }
218 :
219 : MOZ_ALWAYS_INLINE HashNumber
220 111440 : Hash2(HashNumber hash0, uint32_t log2, uint32_t shift)
221 : {
222 111440 : return ((hash0 << log2) >> shift) | 1;
223 : }
224 :
225 : template<MaybeAdding Adding>
226 : MOZ_ALWAYS_INLINE ShapeTable::Entry&
227 306499 : ShapeTable::searchUnchecked(jsid id)
228 : {
229 306499 : MOZ_ASSERT(entries_);
230 306499 : MOZ_ASSERT(!JSID_IS_EMPTY(id));
231 :
232 : /* Compute the primary hash address. */
233 306499 : HashNumber hash0 = HashId(id);
234 306499 : HashNumber hash1 = Hash1(hash0, hashShift_);
235 306499 : Entry* entry = &getEntry(hash1);
236 :
237 : /* Miss: return space for a new entry. */
238 306499 : if (entry->isFree())
239 118051 : return *entry;
240 :
241 : /* Hit: return entry. */
242 188448 : Shape* shape = entry->shape();
243 188449 : if (shape && shape->propidRaw() == id)
244 77016 : return *entry;
245 :
246 : /* Collision: double hash. */
247 111433 : uint32_t sizeLog2 = HASH_BITS - hashShift_;
248 111433 : HashNumber hash2 = Hash2(hash0, sizeLog2, hashShift_);
249 111433 : uint32_t sizeMask = JS_BITMASK(sizeLog2);
250 :
251 : /* Save the first removed entry pointer so we can recycle it if adding. */
252 : Entry* firstRemoved;
253 : if (Adding == MaybeAdding::Adding) {
254 19869 : if (entry->isRemoved()) {
255 124 : firstRemoved = entry;
256 : } else {
257 19745 : firstRemoved = nullptr;
258 19745 : if (!entry->hadCollision())
259 14778 : entry->flagCollision();
260 : }
261 : }
262 :
263 : #ifdef DEBUG
264 111433 : bool collisionFlag = true;
265 111433 : if (!entry->isRemoved())
266 111040 : collisionFlag = entry->hadCollision();
267 : #endif
268 :
269 : while (true) {
270 318531 : hash1 -= hash2;
271 214982 : hash1 &= sizeMask;
272 214982 : entry = &getEntry(hash1);
273 :
274 214982 : if (entry->isFree())
275 81664 : return (Adding == MaybeAdding::Adding && firstRemoved) ? *firstRemoved : *entry;
276 :
277 133318 : shape = entry->shape();
278 133318 : if (shape && shape->propidRaw() == id) {
279 29769 : MOZ_ASSERT(collisionFlag);
280 29769 : return *entry;
281 : }
282 :
283 : if (Adding == MaybeAdding::Adding) {
284 11637 : if (entry->isRemoved()) {
285 12 : if (!firstRemoved)
286 12 : firstRemoved = entry;
287 : } else {
288 11625 : if (!entry->hadCollision())
289 8136 : entry->flagCollision();
290 : }
291 : }
292 :
293 : #ifdef DEBUG
294 103549 : if (!entry->isRemoved())
295 103510 : collisionFlag &= entry->hadCollision();
296 : #endif
297 : }
298 :
299 : MOZ_CRASH("Shape::search failed to find an expected entry.");
300 : }
301 :
302 : template<MaybeAdding Adding>
303 : MOZ_ALWAYS_INLINE ShapeTable::Entry&
304 4032 : ShapeTable::search(jsid id, const AutoKeepShapeTables&)
305 : {
306 4032 : return searchUnchecked<Adding>(id);
307 : }
308 :
309 : template<MaybeAdding Adding>
310 : MOZ_ALWAYS_INLINE ShapeTable::Entry&
311 238229 : ShapeTable::search(jsid id, const JS::AutoCheckCannotGC&)
312 : {
313 238229 : return searchUnchecked<Adding>(id);
314 : }
315 :
316 : /*
317 : * Keep this function in sync with search. It neither hashifies the start
318 : * shape nor increments linear search count.
319 : */
320 : MOZ_ALWAYS_INLINE Shape*
321 200283 : Shape::searchNoHashify(Shape* start, jsid id)
322 : {
323 : /*
324 : * If we have a table, search in the shape table, else do a linear
325 : * search. We never hashify into a table in parallel.
326 : */
327 400572 : JS::AutoCheckCannotGC nogc;
328 200286 : if (ShapeTable* table = start->maybeTable(nogc)) {
329 55999 : ShapeTable::Entry& entry = table->search<MaybeAdding::NotAdding>(id, nogc);
330 55999 : return entry.shape();
331 : }
332 :
333 144289 : return start->searchLinear(id);
334 : }
335 :
336 : /* static */ MOZ_ALWAYS_INLINE Shape*
337 117419 : NativeObject::addProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
338 : GetterOp getter, SetterOp setter, uint32_t slot, unsigned attrs,
339 : unsigned flags, bool allowDictionary)
340 : {
341 117419 : MOZ_ASSERT(!JSID_IS_VOID(id));
342 117420 : MOZ_ASSERT(getter != JS_PropertyStub);
343 117420 : MOZ_ASSERT(setter != JS_StrictPropertyStub);
344 117420 : MOZ_ASSERT(obj->uninlinedNonProxyIsExtensible());
345 117420 : MOZ_ASSERT(!obj->containsPure(id));
346 :
347 234840 : AutoKeepShapeTables keep(cx);
348 117422 : ShapeTable::Entry* entry = nullptr;
349 117422 : if (obj->inDictionaryMode()) {
350 2659 : ShapeTable* table = obj->lastProperty()->ensureTableForDictionary(cx, keep);
351 2659 : if (!table)
352 0 : return nullptr;
353 2659 : entry = &table->search<MaybeAdding::Adding>(id, keep);
354 : }
355 :
356 117422 : return addPropertyInternal(cx, obj, id, getter, setter, slot, attrs, flags, entry,
357 117422 : allowDictionary, keep);
358 : }
359 :
360 : } /* namespace js */
361 :
362 : #endif /* vm_Shape_inl_h */
|