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
36namespace JSC {
37
38const ClassInfo StructureRareData::s_info = { "StructureRareData", nullptr, nullptr, nullptr, CREATE_METHOD_TABLE(StructureRareData) };
39
40Structure* StructureRareData::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
41{
42 return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info());
43}
44
45StructureRareData* 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
52void StructureRareData::destroy(JSCell* cell)
53{
54 static_cast<StructureRareData*>(cell)->StructureRareData::~StructureRareData();
55}
56
57StructureRareData::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
65void 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
81class ObjectToStringAdaptiveInferredPropertyValueWatchpoint final : public AdaptiveInferredPropertyValueWatchpointBase {
82public:
83 typedef AdaptiveInferredPropertyValueWatchpointBase Base;
84 ObjectToStringAdaptiveInferredPropertyValueWatchpoint(const ObjectPropertyCondition&, StructureRareData*);
85
86private:
87 bool isValid() const override;
88 void handleFire(VM&, const FireDetail&) override;
89
90 StructureRareData* m_structureRareData;
91};
92
93class ObjectToStringAdaptiveStructureWatchpoint final : public Watchpoint {
94public:
95 ObjectToStringAdaptiveStructureWatchpoint(const ObjectPropertyCondition&, StructureRareData*);
96
97 void install(VM&);
98
99 const ObjectPropertyCondition& key() const { return m_key; }
100
101protected:
102 void fireInternal(VM&, const FireDetail&) override;
103
104private:
105 ObjectPropertyCondition m_key;
106 StructureRareData* m_structureRareData;
107};
108
109void 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
167inline void StructureRareData::clearObjectToStringValue()
168{
169 m_objectToStringAdaptiveWatchpointSet.clear();
170 m_objectToStringAdaptiveInferredValueWatchpoint.reset();
171 m_objectToStringValue.clear();
172}
173
174void 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
192ObjectToStringAdaptiveStructureWatchpoint::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
200void ObjectToStringAdaptiveStructureWatchpoint::install(VM& vm)
201{
202 RELEASE_ASSERT(m_key.isWatchable());
203
204 m_key.object()->structure(vm)->addTransitionWatchpoint(this);
205}
206
207void 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
220ObjectToStringAdaptiveInferredPropertyValueWatchpoint::ObjectToStringAdaptiveInferredPropertyValueWatchpoint(const ObjectPropertyCondition& key, StructureRareData* structureRareData)
221 : Base(key)
222 , m_structureRareData(structureRareData)
223{
224}
225
226bool ObjectToStringAdaptiveInferredPropertyValueWatchpoint::isValid() const
227{
228 return m_structureRareData->isLive();
229}
230
231void ObjectToStringAdaptiveInferredPropertyValueWatchpoint::handleFire(VM&, const FireDetail&)
232{
233 m_structureRareData->clearObjectToStringValue();
234}
235
236} // namespace JSC
237