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