5c1c5f7f9bf226ccaad40d94dd6ccd9cde97cfc3
[WebKit-https.git] / Source / JavaScriptCore / runtime / StructureRareData.cpp
1 /*
2  * Copyright (C) 2013-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. 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.
24  */
25
26 #include "config.h"
27 #include "StructureRareData.h"
28
29 #include "AdaptiveInferredPropertyValueWatchpointBase.h"
30 #include "JSImmutableButterfly.h"
31 #include "JSPropertyNameEnumerator.h"
32 #include "JSString.h"
33 #include "JSCInlines.h"
34 #include "ObjectPropertyConditionSet.h"
35
36 namespace JSC {
37
38 const ClassInfo StructureRareData::s_info = { "StructureRareData", nullptr, nullptr, nullptr, CREATE_METHOD_TABLE(StructureRareData) };
39
40 Structure* StructureRareData::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
41 {
42     return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info());
43 }
44
45 StructureRareData* StructureRareData::create(VM& vm, Structure* previous)
46 {
47     StructureRareData* rareData = new (NotNull, allocateCell<StructureRareData>(vm.heap)) StructureRareData(vm, previous);
48     rareData->finishCreation(vm);
49     return rareData;
50 }
51
52 void StructureRareData::destroy(JSCell* cell)
53 {
54     static_cast<StructureRareData*>(cell)->StructureRareData::~StructureRareData();
55 }
56
57 StructureRareData::StructureRareData(VM& vm, Structure* previous)
58     : JSCell(vm, vm.structureRareDataStructure.get())
59     , m_giveUpOnObjectToStringValueCache(false)
60 {
61     if (previous)
62         m_previous.set(vm, this, previous);
63 }
64
65 void StructureRareData::visitChildren(JSCell* cell, SlotVisitor& visitor)
66 {
67     StructureRareData* thisObject = jsCast<StructureRareData*>(cell);
68     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
69
70     Base::visitChildren(thisObject, visitor);
71     visitor.append(thisObject->m_previous);
72     visitor.append(thisObject->m_objectToStringValue);
73     visitor.append(thisObject->m_cachedPropertyNameEnumerator);
74     auto* cachedOwnKeys = thisObject->m_cachedOwnKeys.unvalidatedGet();
75     if (cachedOwnKeys != cachedOwnKeysSentinel())
76         visitor.appendUnbarriered(cachedOwnKeys);
77 }
78
79 // ----------- Object.prototype.toString() helper watchpoint classes -----------
80
81 class ObjectToStringAdaptiveInferredPropertyValueWatchpoint final : public AdaptiveInferredPropertyValueWatchpointBase {
82 public:
83     typedef AdaptiveInferredPropertyValueWatchpointBase Base;
84     ObjectToStringAdaptiveInferredPropertyValueWatchpoint(const ObjectPropertyCondition&, StructureRareData*);
85
86 private:
87     bool isValid() const override;
88     void handleFire(VM&, const FireDetail&) override;
89
90     StructureRareData* m_structureRareData;
91 };
92
93 class ObjectToStringAdaptiveStructureWatchpoint final : public Watchpoint {
94 public:
95     ObjectToStringAdaptiveStructureWatchpoint(const ObjectPropertyCondition&, StructureRareData*);
96
97     void install(VM&);
98
99     const ObjectPropertyCondition& key() const { return m_key; }
100
101 protected:
102     void fireInternal(VM&, const FireDetail&) override;
103     
104 private:
105     ObjectPropertyCondition m_key;
106     StructureRareData* m_structureRareData;
107 };
108
109 void StructureRareData::setObjectToStringValue(ExecState* exec, VM& vm, Structure* ownStructure, JSString* value, PropertySlot toStringTagSymbolSlot)
110 {
111     if (m_giveUpOnObjectToStringValueCache)
112         return;
113
114     ObjectPropertyConditionSet conditionSet;
115     if (toStringTagSymbolSlot.isValue()) {
116         // We don't handle the own property case of Symbol.toStringTag because we would never know if a new
117         // object transitioning to the same structure had the same value stored in Symbol.toStringTag.
118         // Additionally, this is a super unlikely case anyway.
119         if (!toStringTagSymbolSlot.isCacheable() || toStringTagSymbolSlot.slotBase()->structure(vm) == ownStructure)
120             return;
121
122
123         // This will not create a condition for the current structure but that is good because we know the Symbol.toStringTag
124         // is not on the ownStructure so we will transisition if one is added and this cache will no longer be used.
125         conditionSet = generateConditionsForPrototypePropertyHit(vm, this, exec, ownStructure, toStringTagSymbolSlot.slotBase(), vm.propertyNames->toStringTagSymbol.impl());
126         ASSERT(!conditionSet.isValid() || conditionSet.hasOneSlotBaseCondition());
127     } else if (toStringTagSymbolSlot.isUnset())
128         conditionSet = generateConditionsForPropertyMiss(vm, this, exec, ownStructure, vm.propertyNames->toStringTagSymbol.impl());
129     else
130         return;
131
132     if (!conditionSet.isValid()) {
133         m_giveUpOnObjectToStringValueCache = true;
134         return;
135     }
136
137     ObjectPropertyCondition equivCondition;
138     for (const ObjectPropertyCondition& condition : conditionSet) {
139         if (condition.condition().kind() == PropertyCondition::Presence) {
140             ASSERT(isValidOffset(condition.offset()));
141             condition.object()->structure(vm)->startWatchingPropertyForReplacements(vm, condition.offset());
142             equivCondition = condition.attemptToMakeEquivalenceWithoutBarrier(vm);
143
144             // The equivalence condition won't be watchable if we have already seen a replacement.
145             if (!equivCondition.isWatchable()) {
146                 m_giveUpOnObjectToStringValueCache = true;
147                 return;
148             }
149         } else if (!condition.isWatchable()) {
150             m_giveUpOnObjectToStringValueCache = true;
151             return;
152         }
153     }
154
155     ASSERT(conditionSet.structuresEnsureValidity());
156     for (ObjectPropertyCondition condition : conditionSet) {
157         if (condition.condition().kind() == PropertyCondition::Presence) {
158             m_objectToStringAdaptiveInferredValueWatchpoint = std::make_unique<ObjectToStringAdaptiveInferredPropertyValueWatchpoint>(equivCondition, this);
159             m_objectToStringAdaptiveInferredValueWatchpoint->install(vm);
160         } else
161             m_objectToStringAdaptiveWatchpointSet.add(condition, this)->install(vm);
162     }
163
164     m_objectToStringValue.set(vm, this, value);
165 }
166
167 inline void StructureRareData::clearObjectToStringValue()
168 {
169     m_objectToStringAdaptiveWatchpointSet.clear();
170     m_objectToStringAdaptiveInferredValueWatchpoint.reset();
171     m_objectToStringValue.clear();
172 }
173
174 void StructureRareData::finalizeUnconditionally(VM& vm)
175 {
176     if (m_objectToStringAdaptiveInferredValueWatchpoint) {
177         if (!m_objectToStringAdaptiveInferredValueWatchpoint->key().isStillLive(vm)) {
178             clearObjectToStringValue();
179             return;
180         }
181     }
182     for (auto* watchpoint : m_objectToStringAdaptiveWatchpointSet) {
183         if (!watchpoint->key().isStillLive(vm)) {
184             clearObjectToStringValue();
185             return;
186         }
187     }
188 }
189
190 // ------------- Methods for Object.prototype.toString() helper watchpoint classes --------------
191
192 ObjectToStringAdaptiveStructureWatchpoint::ObjectToStringAdaptiveStructureWatchpoint(const ObjectPropertyCondition& key, StructureRareData* structureRareData)
193     : m_key(key)
194     , m_structureRareData(structureRareData)
195 {
196     RELEASE_ASSERT(key.watchingRequiresStructureTransitionWatchpoint());
197     RELEASE_ASSERT(!key.watchingRequiresReplacementWatchpoint());
198 }
199
200 void ObjectToStringAdaptiveStructureWatchpoint::install(VM& vm)
201 {
202     RELEASE_ASSERT(m_key.isWatchable());
203
204     m_key.object()->structure(vm)->addTransitionWatchpoint(this);
205 }
206
207 void ObjectToStringAdaptiveStructureWatchpoint::fireInternal(VM& vm, const FireDetail&)
208 {
209     if (!m_structureRareData->isLive())
210         return;
211
212     if (m_key.isWatchable(PropertyCondition::EnsureWatchability)) {
213         install(vm);
214         return;
215     }
216
217     m_structureRareData->clearObjectToStringValue();
218 }
219
220 ObjectToStringAdaptiveInferredPropertyValueWatchpoint::ObjectToStringAdaptiveInferredPropertyValueWatchpoint(const ObjectPropertyCondition& key, StructureRareData* structureRareData)
221     : Base(key)
222     , m_structureRareData(structureRareData)
223 {
224 }
225
226 bool ObjectToStringAdaptiveInferredPropertyValueWatchpoint::isValid() const
227 {
228     return m_structureRareData->isLive();
229 }
230
231 void ObjectToStringAdaptiveInferredPropertyValueWatchpoint::handleFire(VM&, const FireDetail&)
232 {
233     m_structureRareData->clearObjectToStringValue();
234 }
235
236 } // namespace JSC