1 | /* |
2 | * Copyright (C) 2008-2019 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 | #pragma once |
27 | |
28 | #include "ClassInfo.h" |
29 | #include "ConcurrentJSLock.h" |
30 | #include "IndexingType.h" |
31 | #include "JSCJSValue.h" |
32 | #include "JSCast.h" |
33 | #include "JSType.h" |
34 | #include "PropertyName.h" |
35 | #include "PropertyNameArray.h" |
36 | #include "PropertyOffset.h" |
37 | #include "PutPropertySlot.h" |
38 | #include "StructureIDBlob.h" |
39 | #include "StructureRareData.h" |
40 | #include "StructureTransitionTable.h" |
41 | #include "JSTypeInfo.h" |
42 | #include "Watchpoint.h" |
43 | #include "WriteBarrierInlines.h" |
44 | #include <wtf/PrintStream.h> |
45 | |
46 | namespace WTF { |
47 | |
48 | class UniquedStringImpl; |
49 | |
50 | } // namespace WTF |
51 | |
52 | namespace JSC { |
53 | |
54 | class DeferGC; |
55 | class ; |
56 | class PropertyNameArray; |
57 | class PropertyNameArrayData; |
58 | class PropertyTable; |
59 | class StructureChain; |
60 | class StructureShape; |
61 | class SlotVisitor; |
62 | class JSString; |
63 | struct DumpContext; |
64 | |
65 | // The out-of-line property storage capacity to use when first allocating out-of-line |
66 | // storage. Note that all objects start out without having any out-of-line storage; |
67 | // this comes into play only on the first property store that exhausts inline storage. |
68 | static const unsigned initialOutOfLineCapacity = 4; |
69 | |
70 | // The factor by which to grow out-of-line storage when it is exhausted, after the |
71 | // initial allocation. |
72 | static const unsigned outOfLineGrowthFactor = 2; |
73 | |
74 | struct PropertyMapEntry { |
75 | UniquedStringImpl* key; |
76 | PropertyOffset offset; |
77 | uint8_t attributes; |
78 | |
79 | PropertyMapEntry() |
80 | : key(nullptr) |
81 | , offset(invalidOffset) |
82 | , attributes(0) |
83 | { |
84 | } |
85 | |
86 | PropertyMapEntry(UniquedStringImpl* key, PropertyOffset offset, unsigned attributes) |
87 | : key(key) |
88 | , offset(offset) |
89 | , attributes(attributes) |
90 | { |
91 | ASSERT(this->attributes == attributes); |
92 | } |
93 | }; |
94 | |
95 | class StructureFireDetail : public FireDetail { |
96 | public: |
97 | StructureFireDetail(const Structure* structure) |
98 | : m_structure(structure) |
99 | { |
100 | } |
101 | |
102 | void dump(PrintStream& out) const override; |
103 | |
104 | private: |
105 | const Structure* m_structure; |
106 | }; |
107 | |
108 | class DeferredStructureTransitionWatchpointFire : public DeferredWatchpointFire { |
109 | WTF_MAKE_NONCOPYABLE(DeferredStructureTransitionWatchpointFire); |
110 | public: |
111 | JS_EXPORT_PRIVATE DeferredStructureTransitionWatchpointFire(VM&, Structure*); |
112 | JS_EXPORT_PRIVATE ~DeferredStructureTransitionWatchpointFire(); |
113 | |
114 | void dump(PrintStream& out) const override; |
115 | |
116 | const Structure* structure() const { return m_structure; } |
117 | |
118 | private: |
119 | const Structure* m_structure; |
120 | }; |
121 | |
122 | class Structure final : public JSCell { |
123 | public: |
124 | friend class StructureTransitionTable; |
125 | |
126 | typedef JSCell Base; |
127 | static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal; |
128 | |
129 | enum PolyProtoTag { PolyProto }; |
130 | static Structure* create(VM&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType = NonArray, unsigned inlineCapacity = 0); |
131 | static Structure* create(PolyProtoTag, VM&, JSGlobalObject*, JSObject* prototype, const TypeInfo&, const ClassInfo*, IndexingType = NonArray, unsigned inlineCapacity = 0); |
132 | |
133 | ~Structure(); |
134 | |
135 | template<typename CellType, SubspaceAccess> |
136 | static IsoSubspace* subspaceFor(VM& vm) |
137 | { |
138 | return &vm.structureSpace; |
139 | } |
140 | |
141 | protected: |
142 | void finishCreation(VM& vm) |
143 | { |
144 | Base::finishCreation(vm); |
145 | ASSERT(m_prototype.get().isEmpty() || m_prototype.isObject() || m_prototype.isNull()); |
146 | } |
147 | |
148 | void finishCreation(VM& vm, const Structure* previous) |
149 | { |
150 | this->finishCreation(vm); |
151 | if (previous->hasRareData()) { |
152 | const StructureRareData* previousRareData = previous->rareData(); |
153 | if (previousRareData->hasSharedPolyProtoWatchpoint()) { |
154 | ensureRareData(vm); |
155 | rareData()->setSharedPolyProtoWatchpoint(previousRareData->copySharedPolyProtoWatchpoint()); |
156 | } |
157 | } |
158 | } |
159 | |
160 | void finishCreation(VM& vm, CreatingEarlyCellTag) |
161 | { |
162 | Base::finishCreation(vm, this, CreatingEarlyCell); |
163 | ASSERT(m_prototype); |
164 | ASSERT(m_prototype.isNull()); |
165 | ASSERT(!vm.structureStructure); |
166 | } |
167 | |
168 | public: |
169 | StructureID id() const { return m_blob.structureID(); } |
170 | int32_t objectInitializationBlob() const { return m_blob.blobExcludingStructureID(); } |
171 | int64_t idBlob() const { return m_blob.blob(); } |
172 | |
173 | bool isProxy() const |
174 | { |
175 | JSType type = m_blob.type(); |
176 | return type == ImpureProxyType || type == PureForwardingProxyType || type == ProxyObjectType; |
177 | } |
178 | |
179 | static void dumpStatistics(); |
180 | |
181 | JS_EXPORT_PRIVATE static Structure* addPropertyTransition(VM&, Structure*, PropertyName, unsigned attributes, PropertyOffset&); |
182 | JS_EXPORT_PRIVATE static Structure* addNewPropertyTransition(VM&, Structure*, PropertyName, unsigned attributes, PropertyOffset&, PutPropertySlot::Context = PutPropertySlot::UnknownContext, DeferredStructureTransitionWatchpointFire* = nullptr); |
183 | static Structure* addPropertyTransitionToExistingStructureConcurrently(Structure*, UniquedStringImpl* uid, unsigned attributes, PropertyOffset&); |
184 | JS_EXPORT_PRIVATE static Structure* addPropertyTransitionToExistingStructure(Structure*, PropertyName, unsigned attributes, PropertyOffset&); |
185 | static Structure* removePropertyTransition(VM&, Structure*, PropertyName, PropertyOffset&); |
186 | static Structure* changePrototypeTransition(VM&, Structure*, JSValue prototype, DeferredStructureTransitionWatchpointFire&); |
187 | JS_EXPORT_PRIVATE static Structure* attributeChangeTransition(VM&, Structure*, PropertyName, unsigned attributes); |
188 | JS_EXPORT_PRIVATE static Structure* toCacheableDictionaryTransition(VM&, Structure*, DeferredStructureTransitionWatchpointFire* = nullptr); |
189 | static Structure* toUncacheableDictionaryTransition(VM&, Structure*); |
190 | JS_EXPORT_PRIVATE static Structure* sealTransition(VM&, Structure*); |
191 | JS_EXPORT_PRIVATE static Structure* freezeTransition(VM&, Structure*); |
192 | static Structure* preventExtensionsTransition(VM&, Structure*); |
193 | static Structure* nonPropertyTransition(VM&, Structure*, NonPropertyTransition); |
194 | JS_EXPORT_PRIVATE static Structure* nonPropertyTransitionSlow(VM&, Structure*, NonPropertyTransition); |
195 | |
196 | JS_EXPORT_PRIVATE bool isSealed(VM&); |
197 | JS_EXPORT_PRIVATE bool isFrozen(VM&); |
198 | bool isStructureExtensible() const { return !didPreventExtensions(); } |
199 | |
200 | JS_EXPORT_PRIVATE Structure* flattenDictionaryStructure(VM&, JSObject*); |
201 | |
202 | static const bool needsDestruction = true; |
203 | static void destroy(JSCell*); |
204 | |
205 | // Versions that take a func will call it after making the change but while still holding |
206 | // the lock. The callback is not called if there is no change being made, like if you call |
207 | // removePropertyWithoutTransition() and the property is not found. |
208 | template<typename Func> |
209 | PropertyOffset addPropertyWithoutTransition(VM&, PropertyName, unsigned attributes, const Func&); |
210 | template<typename Func> |
211 | PropertyOffset removePropertyWithoutTransition(VM&, PropertyName, const Func&); |
212 | void setPrototypeWithoutTransition(VM&, JSValue prototype); |
213 | |
214 | bool isDictionary() const { return dictionaryKind() != NoneDictionaryKind; } |
215 | bool isUncacheableDictionary() const { return dictionaryKind() == UncachedDictionaryKind; } |
216 | |
217 | bool prototypeQueriesAreCacheable() |
218 | { |
219 | return !typeInfo().prohibitsPropertyCaching(); |
220 | } |
221 | |
222 | bool propertyAccessesAreCacheable() |
223 | { |
224 | return dictionaryKind() != UncachedDictionaryKind |
225 | && prototypeQueriesAreCacheable() |
226 | && !(typeInfo().getOwnPropertySlotIsImpure() && !typeInfo().newImpurePropertyFiresWatchpoints()); |
227 | } |
228 | |
229 | bool propertyAccessesAreCacheableForAbsence() |
230 | { |
231 | return !typeInfo().getOwnPropertySlotIsImpureForPropertyAbsence(); |
232 | } |
233 | |
234 | bool needImpurePropertyWatchpoint() |
235 | { |
236 | return propertyAccessesAreCacheable() |
237 | && typeInfo().getOwnPropertySlotIsImpure() |
238 | && typeInfo().newImpurePropertyFiresWatchpoints(); |
239 | } |
240 | |
241 | bool isImmutablePrototypeExoticObject() |
242 | { |
243 | return typeInfo().isImmutablePrototypeExoticObject(); |
244 | } |
245 | |
246 | // We use SlowPath in GetByIdStatus for structures that may get new impure properties later to prevent |
247 | // DFG from inlining property accesses since structures don't transition when a new impure property appears. |
248 | bool takesSlowPathInDFGForImpureProperty() |
249 | { |
250 | return typeInfo().getOwnPropertySlotIsImpure(); |
251 | } |
252 | |
253 | // Type accessors. |
254 | TypeInfo typeInfo() const { return m_blob.typeInfo(m_outOfLineTypeFlags); } |
255 | bool isObject() const { return typeInfo().isObject(); } |
256 | |
257 | IndexingType indexingType() const { return m_blob.indexingModeIncludingHistory() & AllWritableArrayTypes; } |
258 | IndexingType indexingMode() const { return m_blob.indexingModeIncludingHistory() & AllArrayTypes; } |
259 | IndexingType indexingModeIncludingHistory() const { return m_blob.indexingModeIncludingHistory(); } |
260 | |
261 | inline bool mayInterceptIndexedAccesses() const; |
262 | |
263 | bool holesMustForwardToPrototype(VM&, JSObject*) const; |
264 | |
265 | JSGlobalObject* globalObject() const { return m_globalObject.get(); } |
266 | |
267 | // NOTE: This method should only be called during the creation of structures, since the global |
268 | // object of a structure is presumed to be immutable in a bunch of places. |
269 | void setGlobalObject(VM&, JSGlobalObject*); |
270 | |
271 | ALWAYS_INLINE bool hasMonoProto() const |
272 | { |
273 | return !m_prototype.get().isEmpty(); |
274 | } |
275 | ALWAYS_INLINE bool hasPolyProto() const |
276 | { |
277 | return !hasMonoProto(); |
278 | } |
279 | ALWAYS_INLINE JSValue storedPrototype() const |
280 | { |
281 | ASSERT(hasMonoProto()); |
282 | return m_prototype.get(); |
283 | } |
284 | JSValue storedPrototype(const JSObject*) const; |
285 | JSObject* storedPrototypeObject(const JSObject*) const; |
286 | Structure* storedPrototypeStructure(const JSObject*) const; |
287 | |
288 | JSObject* storedPrototypeObject() const; |
289 | Structure* storedPrototypeStructure() const; |
290 | JSValue prototypeForLookup(JSGlobalObject*) const; |
291 | JSValue prototypeForLookup(JSGlobalObject*, JSCell* base) const; |
292 | StructureChain* prototypeChain(VM&, JSGlobalObject*, JSObject* base) const; |
293 | StructureChain* prototypeChain(ExecState*, JSObject* base) const; |
294 | static void visitChildren(JSCell*, SlotVisitor&); |
295 | |
296 | // A Structure is cheap to mark during GC if doing so would only add a small and bounded amount |
297 | // to our heap footprint. For example, if the structure refers to a global object that is not |
298 | // yet marked, then as far as we know, the decision to mark this Structure would lead to a large |
299 | // increase in footprint because no other object refers to that global object. This method |
300 | // returns true if all user-controlled (and hence unbounded in size) objects referenced from the |
301 | // Structure are already marked. |
302 | bool isCheapDuringGC(VM&); |
303 | |
304 | // Returns true if this structure is now marked. |
305 | bool markIfCheap(SlotVisitor&); |
306 | |
307 | bool hasRareData() const |
308 | { |
309 | return isRareData(m_previousOrRareData.get()); |
310 | } |
311 | |
312 | StructureRareData* rareData() |
313 | { |
314 | ASSERT(hasRareData()); |
315 | return static_cast<StructureRareData*>(m_previousOrRareData.get()); |
316 | } |
317 | |
318 | const StructureRareData* rareData() const |
319 | { |
320 | ASSERT(hasRareData()); |
321 | return static_cast<const StructureRareData*>(m_previousOrRareData.get()); |
322 | } |
323 | |
324 | const StructureRareData* rareDataConcurrently() const |
325 | { |
326 | JSCell* cell = m_previousOrRareData.get(); |
327 | if (isRareData(cell)) |
328 | return static_cast<StructureRareData*>(cell); |
329 | return nullptr; |
330 | } |
331 | |
332 | StructureRareData* ensureRareData(VM& vm) |
333 | { |
334 | if (!hasRareData()) |
335 | allocateRareData(vm); |
336 | return rareData(); |
337 | } |
338 | |
339 | Structure* previousID() const |
340 | { |
341 | ASSERT(structure()->classInfo() == info()); |
342 | // This is so written because it's used concurrently. We only load from m_previousOrRareData |
343 | // once, and this load is guaranteed atomic. |
344 | JSCell* cell = m_previousOrRareData.get(); |
345 | if (isRareData(cell)) |
346 | return static_cast<StructureRareData*>(cell)->previousID(); |
347 | return static_cast<Structure*>(cell); |
348 | } |
349 | bool transitivelyTransitionedFrom(Structure* structureToFind); |
350 | |
351 | PropertyOffset lastOffset() const { return m_offset; } |
352 | |
353 | void setLastOffset(PropertyOffset offset) { m_offset = offset; } |
354 | |
355 | static unsigned outOfLineCapacity(PropertyOffset lastOffset) |
356 | { |
357 | unsigned outOfLineSize = Structure::outOfLineSize(lastOffset); |
358 | |
359 | // This algorithm completely determines the out-of-line property storage growth algorithm. |
360 | // The JSObject code will only trigger a resize if the value returned by this algorithm |
361 | // changed between the new and old structure. So, it's important to keep this simple because |
362 | // it's on a fast path. |
363 | |
364 | if (!outOfLineSize) |
365 | return 0; |
366 | |
367 | if (outOfLineSize <= initialOutOfLineCapacity) |
368 | return initialOutOfLineCapacity; |
369 | |
370 | ASSERT(outOfLineSize > initialOutOfLineCapacity); |
371 | COMPILE_ASSERT(outOfLineGrowthFactor == 2, outOfLineGrowthFactor_is_two); |
372 | return WTF::roundUpToPowerOfTwo(outOfLineSize); |
373 | } |
374 | |
375 | static unsigned outOfLineSize(PropertyOffset lastOffset) |
376 | { |
377 | return numberOfOutOfLineSlotsForLastOffset(lastOffset); |
378 | } |
379 | |
380 | unsigned outOfLineCapacity() const |
381 | { |
382 | return outOfLineCapacity(m_offset); |
383 | } |
384 | unsigned outOfLineSize() const |
385 | { |
386 | return outOfLineSize(m_offset); |
387 | } |
388 | bool hasInlineStorage() const |
389 | { |
390 | return !!m_inlineCapacity; |
391 | } |
392 | unsigned inlineCapacity() const |
393 | { |
394 | return m_inlineCapacity; |
395 | } |
396 | unsigned inlineSize() const |
397 | { |
398 | return std::min<unsigned>(m_offset + 1, m_inlineCapacity); |
399 | } |
400 | unsigned totalStorageSize() const |
401 | { |
402 | return numberOfSlotsForLastOffset(m_offset, m_inlineCapacity); |
403 | } |
404 | unsigned totalStorageCapacity() const |
405 | { |
406 | ASSERT(structure()->classInfo() == info()); |
407 | return outOfLineCapacity() + inlineCapacity(); |
408 | } |
409 | |
410 | bool isValidOffset(PropertyOffset offset) const |
411 | { |
412 | return JSC::isValidOffset(offset) |
413 | && offset <= m_offset |
414 | && (offset < m_inlineCapacity || offset >= firstOutOfLineOffset); |
415 | } |
416 | |
417 | bool () const |
418 | { |
419 | return isTypedView(m_classInfo->typedArrayStorageType); |
420 | } |
421 | |
422 | bool () const |
423 | { |
424 | return hasIndexedProperties(indexingType()) |
425 | || hijacksIndexingHeader(); |
426 | } |
427 | |
428 | bool (const JSCell*) const; |
429 | |
430 | bool masqueradesAsUndefined(JSGlobalObject* lexicalGlobalObject); |
431 | |
432 | PropertyOffset get(VM&, PropertyName); |
433 | PropertyOffset get(VM&, PropertyName, unsigned& attributes); |
434 | |
435 | // This is a somewhat internalish method. It will call your functor while possibly holding the |
436 | // Structure's lock. There is no guarantee whether the lock is held or not in any particular |
437 | // call. So, you have to assume the worst. Also, the functor returns true if it wishes for you |
438 | // to continue or false if it's done. |
439 | template<typename Functor> |
440 | void forEachPropertyConcurrently(const Functor&); |
441 | |
442 | template<typename Functor> |
443 | void forEachProperty(VM&, const Functor&); |
444 | |
445 | PropertyOffset getConcurrently(UniquedStringImpl* uid); |
446 | PropertyOffset getConcurrently(UniquedStringImpl* uid, unsigned& attributes); |
447 | |
448 | Vector<PropertyMapEntry> getPropertiesConcurrently(); |
449 | |
450 | void setHasGetterSetterPropertiesWithProtoCheck(bool is__proto__) |
451 | { |
452 | setHasGetterSetterProperties(true); |
453 | if (!is__proto__) |
454 | setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true); |
455 | } |
456 | |
457 | void setContainsReadOnlyProperties() { setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true); } |
458 | |
459 | void setHasCustomGetterSetterPropertiesWithProtoCheck(bool is__proto__) |
460 | { |
461 | setHasCustomGetterSetterProperties(true); |
462 | if (!is__proto__) |
463 | setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true); |
464 | } |
465 | |
466 | bool isEmpty() const |
467 | { |
468 | ASSERT(checkOffsetConsistency()); |
469 | return !JSC::isValidOffset(m_offset); |
470 | } |
471 | |
472 | void setCachedPropertyNameEnumerator(VM&, JSPropertyNameEnumerator*); |
473 | JSPropertyNameEnumerator* cachedPropertyNameEnumerator() const; |
474 | bool canCachePropertyNameEnumerator() const; |
475 | bool canAccessPropertiesQuicklyForEnumeration() const; |
476 | |
477 | void setCachedOwnKeys(VM&, JSImmutableButterfly*); |
478 | JSImmutableButterfly* cachedOwnKeys() const; |
479 | JSImmutableButterfly* cachedOwnKeysIgnoringSentinel() const; |
480 | bool canCacheOwnKeys() const; |
481 | |
482 | void getPropertyNamesFromStructure(VM&, PropertyNameArray&, EnumerationMode); |
483 | |
484 | JSString* objectToStringValue() |
485 | { |
486 | if (!hasRareData()) |
487 | return 0; |
488 | return rareData()->objectToStringValue(); |
489 | } |
490 | |
491 | void setObjectToStringValue(ExecState*, VM&, JSString* value, PropertySlot toStringTagSymbolSlot); |
492 | |
493 | const ClassInfo* classInfo() const { return m_classInfo; } |
494 | |
495 | static ptrdiff_t structureIDOffset() |
496 | { |
497 | return OBJECT_OFFSETOF(Structure, m_blob) + StructureIDBlob::structureIDOffset(); |
498 | } |
499 | |
500 | static ptrdiff_t prototypeOffset() |
501 | { |
502 | return OBJECT_OFFSETOF(Structure, m_prototype); |
503 | } |
504 | |
505 | static ptrdiff_t globalObjectOffset() |
506 | { |
507 | return OBJECT_OFFSETOF(Structure, m_globalObject); |
508 | } |
509 | |
510 | static ptrdiff_t classInfoOffset() |
511 | { |
512 | return OBJECT_OFFSETOF(Structure, m_classInfo); |
513 | } |
514 | |
515 | static ptrdiff_t indexingModeIncludingHistoryOffset() |
516 | { |
517 | return OBJECT_OFFSETOF(Structure, m_blob) + StructureIDBlob::indexingModeIncludingHistoryOffset(); |
518 | } |
519 | |
520 | static ptrdiff_t propertyTableUnsafeOffset() |
521 | { |
522 | return OBJECT_OFFSETOF(Structure, m_propertyTableUnsafe); |
523 | } |
524 | |
525 | static ptrdiff_t inlineCapacityOffset() |
526 | { |
527 | return OBJECT_OFFSETOF(Structure, m_inlineCapacity); |
528 | } |
529 | |
530 | static ptrdiff_t previousOrRareDataOffset() |
531 | { |
532 | return OBJECT_OFFSETOF(Structure, m_previousOrRareData); |
533 | } |
534 | |
535 | static Structure* createStructure(VM&); |
536 | |
537 | bool transitionWatchpointSetHasBeenInvalidated() const |
538 | { |
539 | return m_transitionWatchpointSet.hasBeenInvalidated(); |
540 | } |
541 | |
542 | bool transitionWatchpointSetIsStillValid() const |
543 | { |
544 | return m_transitionWatchpointSet.isStillValid(); |
545 | } |
546 | |
547 | bool dfgShouldWatchIfPossible() const |
548 | { |
549 | // FIXME: We would like to not watch things that are unprofitable to watch, like |
550 | // dictionaries. Unfortunately, we can't do such things: a dictionary could get flattened, |
551 | // in which case it will start to appear watchable and so the DFG will think that it is |
552 | // watching it. We should come up with a comprehensive story for not watching things that |
553 | // aren't profitable to watch. |
554 | // https://bugs.webkit.org/show_bug.cgi?id=133625 |
555 | |
556 | // - We don't watch Structures that either decided not to be watched, or whose predecessors |
557 | // decided not to be watched. This happens when a transition is fired while being watched. |
558 | if (transitionWatchpointIsLikelyToBeFired()) |
559 | return false; |
560 | |
561 | // - Don't watch Structures that had been dictionaries. |
562 | if (hasBeenDictionary()) |
563 | return false; |
564 | |
565 | return true; |
566 | } |
567 | |
568 | bool dfgShouldWatch() const |
569 | { |
570 | return dfgShouldWatchIfPossible() && transitionWatchpointSetIsStillValid(); |
571 | } |
572 | |
573 | void addTransitionWatchpoint(Watchpoint* watchpoint) const |
574 | { |
575 | ASSERT(transitionWatchpointSetIsStillValid()); |
576 | m_transitionWatchpointSet.add(watchpoint); |
577 | } |
578 | |
579 | void didTransitionFromThisStructure(DeferredStructureTransitionWatchpointFire* = nullptr) const; |
580 | |
581 | InlineWatchpointSet& transitionWatchpointSet() const |
582 | { |
583 | return m_transitionWatchpointSet; |
584 | } |
585 | |
586 | WatchpointSet* ensurePropertyReplacementWatchpointSet(VM&, PropertyOffset); |
587 | void startWatchingPropertyForReplacements(VM& vm, PropertyOffset offset) |
588 | { |
589 | ensurePropertyReplacementWatchpointSet(vm, offset); |
590 | } |
591 | void startWatchingPropertyForReplacements(VM&, PropertyName); |
592 | WatchpointSet* propertyReplacementWatchpointSet(PropertyOffset); |
593 | void didReplaceProperty(PropertyOffset); |
594 | void didCachePropertyReplacement(VM&, PropertyOffset); |
595 | |
596 | void startWatchingInternalPropertiesIfNecessary(VM& vm) |
597 | { |
598 | if (LIKELY(didWatchInternalProperties())) |
599 | return; |
600 | startWatchingInternalProperties(vm); |
601 | } |
602 | |
603 | Ref<StructureShape> toStructureShape(JSValue, bool& sawPolyProtoStructure); |
604 | |
605 | void dump(PrintStream&) const; |
606 | void dumpInContext(PrintStream&, DumpContext*) const; |
607 | void dumpBrief(PrintStream&, const CString&) const; |
608 | |
609 | static void (PrintStream&); |
610 | |
611 | ConcurrentJSLock& lock() { return m_lock; } |
612 | |
613 | unsigned propertyHash() const { return m_propertyHash; } |
614 | |
615 | static bool shouldConvertToPolyProto(const Structure* a, const Structure* b); |
616 | |
617 | DECLARE_EXPORT_INFO; |
618 | |
619 | private: |
620 | typedef enum { |
621 | NoneDictionaryKind = 0, |
622 | CachedDictionaryKind = 1, |
623 | UncachedDictionaryKind = 2 |
624 | } DictionaryKind; |
625 | |
626 | public: |
627 | #define DEFINE_BITFIELD(type, lowerName, upperName, width, offset) \ |
628 | static const uint32_t s_##lowerName##Shift = offset;\ |
629 | static const uint32_t s_##lowerName##Mask = ((1 << (width - 1)) | ((1 << (width - 1)) - 1));\ |
630 | type lowerName() const { return static_cast<type>((m_bitField >> offset) & s_##lowerName##Mask); }\ |
631 | void set##upperName(type newValue) \ |
632 | {\ |
633 | m_bitField &= ~(s_##lowerName##Mask << offset);\ |
634 | m_bitField |= (newValue & s_##lowerName##Mask) << offset;\ |
635 | } |
636 | |
637 | DEFINE_BITFIELD(DictionaryKind, dictionaryKind, DictionaryKind, 2, 0); |
638 | DEFINE_BITFIELD(bool, isPinnedPropertyTable, IsPinnedPropertyTable, 1, 2); |
639 | DEFINE_BITFIELD(bool, hasGetterSetterProperties, HasGetterSetterProperties, 1, 3); |
640 | DEFINE_BITFIELD(bool, hasReadOnlyOrGetterSetterPropertiesExcludingProto, HasReadOnlyOrGetterSetterPropertiesExcludingProto, 1, 4); |
641 | DEFINE_BITFIELD(bool, isQuickPropertyAccessAllowedForEnumeration, IsQuickPropertyAccessAllowedForEnumeration, 1, 5); |
642 | DEFINE_BITFIELD(unsigned, attributesInPrevious, AttributesInPrevious, 14, 6); |
643 | DEFINE_BITFIELD(bool, didPreventExtensions, DidPreventExtensions, 1, 20); |
644 | DEFINE_BITFIELD(bool, didTransition, DidTransition, 1, 21); |
645 | DEFINE_BITFIELD(bool, staticPropertiesReified, StaticPropertiesReified, 1, 22); |
646 | DEFINE_BITFIELD(bool, hasBeenFlattenedBefore, HasBeenFlattenedBefore, 1, 23); |
647 | DEFINE_BITFIELD(bool, hasCustomGetterSetterProperties, HasCustomGetterSetterProperties, 1, 24); |
648 | DEFINE_BITFIELD(bool, didWatchInternalProperties, DidWatchInternalProperties, 1, 25); |
649 | DEFINE_BITFIELD(bool, transitionWatchpointIsLikelyToBeFired, TransitionWatchpointIsLikelyToBeFired, 1, 26); |
650 | DEFINE_BITFIELD(bool, hasBeenDictionary, HasBeenDictionary, 1, 27); |
651 | DEFINE_BITFIELD(bool, isAddingPropertyForTransition, IsAddingPropertyForTransition, 1, 28); |
652 | DEFINE_BITFIELD(bool, hasUnderscoreProtoPropertyExcludingOriginalProto, HasUnderscoreProtoPropertyExcludingOriginalProto, 1, 29); |
653 | |
654 | private: |
655 | friend class LLIntOffsetsExtractor; |
656 | |
657 | JS_EXPORT_PRIVATE Structure(VM&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType, unsigned inlineCapacity); |
658 | Structure(VM&); |
659 | Structure(VM&, Structure*, DeferredStructureTransitionWatchpointFire*); |
660 | |
661 | static Structure* create(VM&, Structure*, DeferredStructureTransitionWatchpointFire* = nullptr); |
662 | |
663 | static Structure* addPropertyTransitionToExistingStructureImpl(Structure*, UniquedStringImpl* uid, unsigned attributes, PropertyOffset&); |
664 | |
665 | // This will return the structure that has a usable property table, that property table, |
666 | // and the list of structures that we visited before we got to it. If it returns a |
667 | // non-null structure, it will also lock the structure that it returns; it is your job |
668 | // to unlock it. |
669 | void findStructuresAndMapForMaterialization(Vector<Structure*, 8>& structures, Structure*&, PropertyTable*&); |
670 | |
671 | static Structure* toDictionaryTransition(VM&, Structure*, DictionaryKind, DeferredStructureTransitionWatchpointFire* = nullptr); |
672 | |
673 | enum class ShouldPin { No, Yes }; |
674 | template<ShouldPin, typename Func> |
675 | PropertyOffset add(VM&, PropertyName, unsigned attributes, const Func&); |
676 | PropertyOffset add(VM&, PropertyName, unsigned attributes); |
677 | template<typename Func> |
678 | PropertyOffset remove(PropertyName, const Func&); |
679 | PropertyOffset remove(PropertyName); |
680 | |
681 | void checkConsistency(); |
682 | |
683 | // This may grab the lock, or not. Do not call when holding the Structure's lock. |
684 | PropertyTable* ensurePropertyTableIfNotEmpty(VM& vm) |
685 | { |
686 | if (PropertyTable* result = m_propertyTableUnsafe.get()) |
687 | return result; |
688 | if (!previousID()) |
689 | return nullptr; |
690 | return materializePropertyTable(vm); |
691 | } |
692 | |
693 | // This may grab the lock, or not. Do not call when holding the Structure's lock. |
694 | PropertyTable* ensurePropertyTable(VM& vm) |
695 | { |
696 | if (PropertyTable* result = m_propertyTableUnsafe.get()) |
697 | return result; |
698 | return materializePropertyTable(vm); |
699 | } |
700 | |
701 | PropertyTable* propertyTableOrNull() const |
702 | { |
703 | return m_propertyTableUnsafe.get(); |
704 | } |
705 | |
706 | // This will grab the lock. Do not call when holding the Structure's lock. |
707 | JS_EXPORT_PRIVATE PropertyTable* materializePropertyTable(VM&, bool setPropertyTable = true); |
708 | |
709 | void setPropertyTable(VM& vm, PropertyTable* table); |
710 | |
711 | PropertyTable* takePropertyTableOrCloneIfPinned(VM&); |
712 | PropertyTable* copyPropertyTableForPinning(VM&); |
713 | |
714 | void setPreviousID(VM&, Structure*); |
715 | |
716 | void clearPreviousID() |
717 | { |
718 | if (hasRareData()) |
719 | rareData()->clearPreviousID(); |
720 | else |
721 | m_previousOrRareData.clear(); |
722 | } |
723 | |
724 | int transitionCount() const |
725 | { |
726 | // Since the number of transitions is always the same as m_offset, we keep the size of Structure down by not storing both. |
727 | return numberOfSlotsForLastOffset(m_offset, m_inlineCapacity); |
728 | } |
729 | |
730 | bool isValid(JSGlobalObject*, StructureChain* cachedPrototypeChain, JSObject* base) const; |
731 | |
732 | // You have to hold the structure lock to do these. |
733 | JS_EXPORT_PRIVATE void pin(const AbstractLocker&, VM&, PropertyTable*); |
734 | void pinForCaching(const AbstractLocker&, VM&, PropertyTable*); |
735 | |
736 | bool isRareData(JSCell* cell) const |
737 | { |
738 | return cell && cell->structureID() != structureID(); |
739 | } |
740 | |
741 | template<typename DetailsFunc> |
742 | bool checkOffsetConsistency(PropertyTable*, const DetailsFunc&) const; |
743 | bool checkOffsetConsistency() const; |
744 | |
745 | JS_EXPORT_PRIVATE void allocateRareData(VM&); |
746 | |
747 | void startWatchingInternalProperties(VM&); |
748 | |
749 | static const int s_maxTransitionLength = 64; |
750 | static const int s_maxTransitionLengthForNonEvalPutById = 512; |
751 | |
752 | // These need to be properly aligned at the beginning of the 'Structure' |
753 | // part of the object. |
754 | StructureIDBlob m_blob; |
755 | TypeInfo::OutOfLineTypeFlags m_outOfLineTypeFlags; |
756 | |
757 | uint8_t m_inlineCapacity; |
758 | |
759 | ConcurrentJSLock m_lock; |
760 | |
761 | uint32_t m_bitField; |
762 | |
763 | WriteBarrier<JSGlobalObject> m_globalObject; |
764 | WriteBarrier<Unknown> m_prototype; |
765 | mutable WriteBarrier<StructureChain> m_cachedPrototypeChain; |
766 | |
767 | WriteBarrier<JSCell> m_previousOrRareData; |
768 | |
769 | RefPtr<UniquedStringImpl> m_nameInPrevious; |
770 | |
771 | const ClassInfo* m_classInfo; |
772 | |
773 | StructureTransitionTable m_transitionTable; |
774 | |
775 | // Should be accessed through ensurePropertyTable(). During GC, it may be set to 0 by another thread. |
776 | // During a Heap Snapshot GC we avoid clearing the table so it is safe to use. |
777 | WriteBarrier<PropertyTable> m_propertyTableUnsafe; |
778 | |
779 | mutable InlineWatchpointSet m_transitionWatchpointSet; |
780 | |
781 | COMPILE_ASSERT(firstOutOfLineOffset < 256, firstOutOfLineOffset_fits); |
782 | |
783 | // m_offset does not account for anonymous slots |
784 | PropertyOffset m_offset; |
785 | |
786 | uint32_t m_propertyHash; |
787 | }; |
788 | |
789 | // We deliberately put Structure::create here in Structure.h instead of StructureInlines.h, because |
790 | // it is used everywhere. This is so we don't have to hunt down all the places where we would need |
791 | // to #include StructureInlines.h otherwise. |
792 | inline Structure* Structure::create(VM& vm, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo, IndexingType indexingType, unsigned inlineCapacity) |
793 | { |
794 | ASSERT(vm.structureStructure); |
795 | ASSERT(classInfo); |
796 | Structure* structure = new (NotNull, allocateCell<Structure>(vm.heap)) Structure(vm, globalObject, prototype, typeInfo, classInfo, indexingType, inlineCapacity); |
797 | structure->finishCreation(vm); |
798 | return structure; |
799 | } |
800 | |
801 | } // namespace JSC |
802 | |