1/*
2 * Copyright (C) 2014-2018 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#include "config.h"
27#include "PutByIdVariant.h"
28
29#include "CallLinkStatus.h"
30#include "JSCInlines.h"
31#include <wtf/ListDump.h>
32
33namespace JSC {
34
35PutByIdVariant::PutByIdVariant(const PutByIdVariant& other)
36 : PutByIdVariant()
37{
38 *this = other;
39}
40
41PutByIdVariant& PutByIdVariant::operator=(const PutByIdVariant& other)
42{
43 m_kind = other.m_kind;
44 m_oldStructure = other.m_oldStructure;
45 m_newStructure = other.m_newStructure;
46 m_conditionSet = other.m_conditionSet;
47 m_offset = other.m_offset;
48 if (other.m_callLinkStatus)
49 m_callLinkStatus = std::make_unique<CallLinkStatus>(*other.m_callLinkStatus);
50 else
51 m_callLinkStatus = nullptr;
52 return *this;
53}
54
55PutByIdVariant PutByIdVariant::replace(
56 const StructureSet& structure, PropertyOffset offset)
57{
58 PutByIdVariant result;
59 result.m_kind = Replace;
60 result.m_oldStructure = structure;
61 result.m_offset = offset;
62 return result;
63}
64
65PutByIdVariant PutByIdVariant::transition(
66 const StructureSet& oldStructure, Structure* newStructure,
67 const ObjectPropertyConditionSet& conditionSet, PropertyOffset offset)
68{
69 PutByIdVariant result;
70 result.m_kind = Transition;
71 result.m_oldStructure = oldStructure;
72 result.m_newStructure = newStructure;
73 result.m_conditionSet = conditionSet;
74 result.m_offset = offset;
75 return result;
76}
77
78PutByIdVariant PutByIdVariant::setter(
79 const StructureSet& structure, PropertyOffset offset,
80 const ObjectPropertyConditionSet& conditionSet,
81 std::unique_ptr<CallLinkStatus> callLinkStatus)
82{
83 PutByIdVariant result;
84 result.m_kind = Setter;
85 result.m_oldStructure = structure;
86 result.m_conditionSet = conditionSet;
87 result.m_offset = offset;
88 result.m_callLinkStatus = WTFMove(callLinkStatus);
89 return result;
90}
91
92Structure* PutByIdVariant::oldStructureForTransition() const
93{
94 RELEASE_ASSERT(kind() == Transition);
95 RELEASE_ASSERT(m_oldStructure.size() <= 2);
96 for (unsigned i = m_oldStructure.size(); i--;) {
97 Structure* structure = m_oldStructure[i];
98 if (structure != m_newStructure)
99 return structure;
100 }
101 RELEASE_ASSERT_NOT_REACHED();
102
103 return nullptr;
104}
105
106void PutByIdVariant::fixTransitionToReplaceIfNecessary()
107{
108 if (kind() != Transition)
109 return;
110
111 RELEASE_ASSERT(m_oldStructure.size() <= 2);
112 for (unsigned i = m_oldStructure.size(); i--;) {
113 Structure* structure = m_oldStructure[i];
114 if (structure != m_newStructure)
115 return;
116 }
117
118 m_newStructure = nullptr;
119 m_kind = Replace;
120 m_conditionSet = ObjectPropertyConditionSet();
121 RELEASE_ASSERT(!m_callLinkStatus);
122}
123
124bool PutByIdVariant::writesStructures() const
125{
126 switch (kind()) {
127 case Transition:
128 case Setter:
129 return true;
130 default:
131 return false;
132 }
133}
134
135bool PutByIdVariant::reallocatesStorage() const
136{
137 switch (kind()) {
138 case Transition:
139 return oldStructureForTransition()->outOfLineCapacity() != newStructure()->outOfLineCapacity();
140 case Setter:
141 return true;
142 default:
143 return false;
144 }
145}
146
147bool PutByIdVariant::makesCalls() const
148{
149 return kind() == Setter;
150}
151
152bool PutByIdVariant::attemptToMerge(const PutByIdVariant& other)
153{
154 if (m_offset != other.m_offset)
155 return false;
156
157 switch (m_kind) {
158 case NotSet:
159 RELEASE_ASSERT_NOT_REACHED();
160 return false;
161
162 case Replace: {
163 switch (other.m_kind) {
164 case Replace: {
165 ASSERT(m_conditionSet.isEmpty());
166 ASSERT(other.m_conditionSet.isEmpty());
167
168 m_oldStructure.merge(other.m_oldStructure);
169 return true;
170 }
171
172 case Transition: {
173 PutByIdVariant newVariant = other;
174 if (newVariant.attemptToMergeTransitionWithReplace(*this)) {
175 *this = newVariant;
176 return true;
177 }
178 return false;
179 }
180
181 default:
182 return false;
183 }
184 }
185
186 case Transition:
187 switch (other.m_kind) {
188 case Replace:
189 return attemptToMergeTransitionWithReplace(other);
190
191 case Transition: {
192 if (m_oldStructure != other.m_oldStructure)
193 return false;
194
195 if (m_newStructure != other.m_newStructure)
196 return false;
197
198 ObjectPropertyConditionSet mergedConditionSet;
199 if (!m_conditionSet.isEmpty()) {
200 mergedConditionSet = m_conditionSet.mergedWith(other.m_conditionSet);
201 if (!mergedConditionSet.isValid())
202 return false;
203 }
204 m_conditionSet = mergedConditionSet;
205 return true;
206 }
207
208 default:
209 return false;
210 }
211
212 case Setter: {
213 if (other.m_kind != Setter)
214 return false;
215
216 if (m_callLinkStatus || other.m_callLinkStatus) {
217 if (!(m_callLinkStatus && other.m_callLinkStatus))
218 return false;
219 }
220
221 if (m_conditionSet.isEmpty() != other.m_conditionSet.isEmpty())
222 return false;
223
224 ObjectPropertyConditionSet mergedConditionSet;
225 if (!m_conditionSet.isEmpty()) {
226 mergedConditionSet = m_conditionSet.mergedWith(other.m_conditionSet);
227 if (!mergedConditionSet.isValid() || !mergedConditionSet.hasOneSlotBaseCondition())
228 return false;
229 }
230 m_conditionSet = mergedConditionSet;
231
232 if (m_callLinkStatus)
233 m_callLinkStatus->merge(*other.m_callLinkStatus);
234
235 m_oldStructure.merge(other.m_oldStructure);
236 return true;
237 } }
238
239 RELEASE_ASSERT_NOT_REACHED();
240 return false;
241}
242
243bool PutByIdVariant::attemptToMergeTransitionWithReplace(const PutByIdVariant& replace)
244{
245 ASSERT(m_kind == Transition);
246 ASSERT(replace.m_kind == Replace);
247 ASSERT(m_offset == replace.m_offset);
248 ASSERT(!replace.writesStructures());
249 ASSERT(!replace.reallocatesStorage());
250 ASSERT(replace.conditionSet().isEmpty());
251
252 // This sort of merging only works when we have one path along which we add a new field which
253 // transitions to structure S while the other path was already on structure S. This doesn't
254 // work if we need to reallocate anything or if the replace path is polymorphic.
255
256 if (reallocatesStorage())
257 return false;
258
259 if (replace.m_oldStructure.onlyStructure() != m_newStructure)
260 return false;
261
262 m_oldStructure.merge(m_newStructure);
263 return true;
264}
265
266void PutByIdVariant::markIfCheap(SlotVisitor& visitor)
267{
268 m_oldStructure.markIfCheap(visitor);
269 if (m_newStructure)
270 m_newStructure->markIfCheap(visitor);
271}
272
273bool PutByIdVariant::finalize(VM& vm)
274{
275 if (!m_oldStructure.isStillAlive(vm))
276 return false;
277 if (m_newStructure && !vm.heap.isMarked(m_newStructure))
278 return false;
279 if (!m_conditionSet.areStillLive(vm))
280 return false;
281 if (m_callLinkStatus && !m_callLinkStatus->finalize(vm))
282 return false;
283 return true;
284}
285
286void PutByIdVariant::dump(PrintStream& out) const
287{
288 dumpInContext(out, 0);
289}
290
291void PutByIdVariant::dumpInContext(PrintStream& out, DumpContext* context) const
292{
293 switch (kind()) {
294 case NotSet:
295 out.print("<empty>");
296 return;
297
298 case Replace:
299 out.print(
300 "<Replace: ", inContext(structure(), context), ", offset = ", offset(), ", ", ">");
301 return;
302
303 case Transition:
304 out.print(
305 "<Transition: ", inContext(oldStructure(), context), " to ",
306 pointerDumpInContext(newStructure(), context), ", [",
307 inContext(m_conditionSet, context), "], offset = ", offset(), ", ", ">");
308 return;
309
310 case Setter:
311 out.print(
312 "<Setter: ", inContext(structure(), context), ", [",
313 inContext(m_conditionSet, context), "]");
314 out.print(", offset = ", m_offset);
315 out.print(", call = ", *m_callLinkStatus);
316 out.print(">");
317 return;
318 }
319
320 RELEASE_ASSERT_NOT_REACHED();
321}
322
323} // namespace JSC
324
325