]>
Commit | Line | Data |
---|---|---|
b37bf2e1 | 1 | /* |
81345200 | 2 | * Copyright (C) 2014 Apple Inc. All rights reserved. |
b37bf2e1 A |
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 | * | |
9dae56ea | 13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
b37bf2e1 A |
14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
9dae56ea | 16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
b37bf2e1 A |
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 | |
6fe7ccc8 | 23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
b37bf2e1 A |
24 | */ |
25 | ||
81345200 A |
26 | #include "config.h" |
27 | #include "PutByIdVariant.h" | |
b37bf2e1 | 28 | |
ed1e77d3 A |
29 | #include "CallLinkStatus.h" |
30 | #include "JSCInlines.h" | |
31 | #include <wtf/ListDump.h> | |
32 | ||
9dae56ea | 33 | namespace JSC { |
b37bf2e1 | 34 | |
ed1e77d3 A |
35 | PutByIdVariant::PutByIdVariant(const PutByIdVariant& other) |
36 | : PutByIdVariant() | |
37 | { | |
38 | *this = other; | |
39 | } | |
40 | ||
41 | PutByIdVariant& 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_constantChecks = other.m_constantChecks; | |
47 | m_alternateBase = other.m_alternateBase; | |
48 | m_offset = other.m_offset; | |
49 | if (other.m_callLinkStatus) | |
50 | m_callLinkStatus = std::make_unique<CallLinkStatus>(*other.m_callLinkStatus); | |
51 | else | |
52 | m_callLinkStatus = nullptr; | |
53 | return *this; | |
54 | } | |
55 | ||
56 | PutByIdVariant PutByIdVariant::replace(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 | ||
65 | PutByIdVariant PutByIdVariant::transition( | |
66 | const StructureSet& oldStructure, Structure* newStructure, | |
67 | const IntendedStructureChain* structureChain, PropertyOffset offset) | |
68 | { | |
69 | PutByIdVariant result; | |
70 | result.m_kind = Transition; | |
71 | result.m_oldStructure = oldStructure; | |
72 | result.m_newStructure = newStructure; | |
73 | if (structureChain) | |
74 | structureChain->gatherChecks(result.m_constantChecks); | |
75 | result.m_offset = offset; | |
76 | return result; | |
77 | } | |
78 | ||
79 | PutByIdVariant PutByIdVariant::setter( | |
80 | const StructureSet& structure, PropertyOffset offset, | |
81 | IntendedStructureChain* chain, std::unique_ptr<CallLinkStatus> callLinkStatus) | |
82 | { | |
83 | PutByIdVariant result; | |
84 | result.m_kind = Setter; | |
85 | result.m_oldStructure = structure; | |
86 | if (chain) { | |
87 | chain->gatherChecks(result.m_constantChecks); | |
88 | result.m_alternateBase = chain->terminalPrototype(); | |
89 | } | |
90 | result.m_offset = offset; | |
91 | result.m_callLinkStatus = WTF::move(callLinkStatus); | |
92 | return result; | |
93 | } | |
94 | ||
95 | Structure* PutByIdVariant::oldStructureForTransition() const | |
96 | { | |
97 | ASSERT(kind() == Transition); | |
98 | ASSERT(m_oldStructure.size() <= 2); | |
99 | for (unsigned i = m_oldStructure.size(); i--;) { | |
100 | Structure* structure = m_oldStructure[i]; | |
101 | if (structure != m_newStructure) | |
102 | return structure; | |
103 | } | |
104 | RELEASE_ASSERT_NOT_REACHED(); | |
105 | ||
106 | return nullptr; | |
107 | } | |
108 | ||
109 | bool PutByIdVariant::writesStructures() const | |
110 | { | |
111 | switch (kind()) { | |
112 | case Transition: | |
113 | case Setter: | |
114 | return true; | |
115 | default: | |
116 | return false; | |
117 | } | |
118 | } | |
119 | ||
120 | bool PutByIdVariant::reallocatesStorage() const | |
121 | { | |
122 | switch (kind()) { | |
123 | case Transition: | |
124 | return oldStructureForTransition()->outOfLineCapacity() != newStructure()->outOfLineCapacity(); | |
125 | case Setter: | |
126 | return true; | |
127 | default: | |
128 | return false; | |
129 | } | |
130 | } | |
131 | ||
132 | bool PutByIdVariant::makesCalls() const | |
133 | { | |
134 | return kind() == Setter; | |
135 | } | |
136 | ||
137 | StructureSet PutByIdVariant::baseStructure() const | |
138 | { | |
139 | ASSERT(kind() == Setter); | |
140 | ||
141 | if (!m_alternateBase) | |
142 | return structure(); | |
143 | ||
144 | Structure* structure = structureFor(m_constantChecks, m_alternateBase); | |
145 | RELEASE_ASSERT(structure); | |
146 | return structure; | |
147 | } | |
148 | ||
149 | bool PutByIdVariant::attemptToMerge(const PutByIdVariant& other) | |
150 | { | |
151 | if (m_offset != other.m_offset) | |
152 | return false; | |
153 | ||
154 | switch (m_kind) { | |
155 | case Replace: | |
156 | switch (other.m_kind) { | |
157 | case Replace: { | |
158 | ASSERT(m_constantChecks.isEmpty()); | |
159 | ASSERT(other.m_constantChecks.isEmpty()); | |
160 | ||
161 | m_oldStructure.merge(other.m_oldStructure); | |
162 | return true; | |
163 | } | |
164 | ||
165 | case Transition: { | |
166 | PutByIdVariant newVariant = other; | |
167 | if (newVariant.attemptToMergeTransitionWithReplace(*this)) { | |
168 | *this = newVariant; | |
169 | return true; | |
170 | } | |
171 | return false; | |
172 | } | |
173 | ||
174 | default: | |
175 | return false; | |
176 | } | |
177 | ||
178 | case Transition: | |
179 | switch (other.m_kind) { | |
180 | case Replace: | |
181 | return attemptToMergeTransitionWithReplace(other); | |
182 | ||
183 | default: | |
184 | return false; | |
185 | } | |
186 | ||
187 | default: | |
188 | return false; | |
189 | } | |
190 | } | |
191 | ||
192 | bool PutByIdVariant::attemptToMergeTransitionWithReplace(const PutByIdVariant& replace) | |
193 | { | |
194 | ASSERT(m_kind == Transition); | |
195 | ASSERT(replace.m_kind == Replace); | |
196 | ASSERT(m_offset == replace.m_offset); | |
197 | ASSERT(!replace.writesStructures()); | |
198 | ASSERT(!replace.reallocatesStorage()); | |
199 | ||
200 | // This sort of merging only works when we have one path along which we add a new field which | |
201 | // transitions to structure S while the other path was already on structure S. This doesn't | |
202 | // work if we need to reallocate anything or if the replace path is polymorphic. | |
203 | ||
204 | if (reallocatesStorage()) | |
205 | return false; | |
206 | ||
207 | if (replace.m_oldStructure.onlyStructure() != m_newStructure) | |
208 | return false; | |
209 | ||
210 | m_oldStructure.merge(m_newStructure); | |
211 | return true; | |
212 | } | |
213 | ||
81345200 A |
214 | void PutByIdVariant::dump(PrintStream& out) const |
215 | { | |
216 | dumpInContext(out, 0); | |
217 | } | |
b37bf2e1 | 218 | |
81345200 | 219 | void PutByIdVariant::dumpInContext(PrintStream& out, DumpContext* context) const |
93a37866 | 220 | { |
81345200 A |
221 | switch (kind()) { |
222 | case NotSet: | |
223 | out.print("<empty>"); | |
224 | return; | |
225 | ||
226 | case Replace: | |
227 | out.print( | |
ed1e77d3 | 228 | "<Replace: ", inContext(structure(), context), ", offset = ", offset(), ">"); |
81345200 A |
229 | return; |
230 | ||
231 | case Transition: | |
232 | out.print( | |
ed1e77d3 A |
233 | "<Transition: ", inContext(oldStructure(), context), " -> ", |
234 | pointerDumpInContext(newStructure(), context), ", [", | |
235 | listDumpInContext(constantChecks(), context), "], offset = ", offset(), ">"); | |
236 | return; | |
237 | ||
238 | case Setter: | |
239 | out.print( | |
240 | "<Setter: ", inContext(structure(), context), ", [", | |
241 | listDumpInContext(constantChecks(), context), "]"); | |
242 | if (m_alternateBase) | |
243 | out.print(", alternateBase = ", inContext(JSValue(m_alternateBase), context)); | |
244 | out.print(", offset = ", m_offset); | |
245 | out.print(", call = ", *m_callLinkStatus); | |
246 | out.print(">"); | |
81345200 A |
247 | return; |
248 | } | |
249 | ||
250 | RELEASE_ASSERT_NOT_REACHED(); | |
93a37866 A |
251 | } |
252 | ||
9dae56ea A |
253 | } // namespace JSC |
254 |