1 | /* |
2 | * Copyright (C) 2013-2019 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 "DFGLazyJSValue.h" |
28 | |
29 | #if ENABLE(DFG_JIT) |
30 | |
31 | #include "CCallHelpers.h" |
32 | #include "DFGGraph.h" |
33 | #include "JSCInlines.h" |
34 | #include "LinkBuffer.h" |
35 | |
36 | namespace JSC { namespace DFG { |
37 | |
38 | LazyJSValue LazyJSValue::newString(Graph& graph, const String& string) |
39 | { |
40 | LazyJSValue result; |
41 | result.m_kind = NewStringImpl; |
42 | result.u.stringImpl = graph.m_localStrings.add(string).iterator->impl(); |
43 | return result; |
44 | } |
45 | |
46 | JSValue LazyJSValue::getValue(VM& vm) const |
47 | { |
48 | switch (m_kind) { |
49 | case KnownValue: |
50 | return value()->value(); |
51 | case SingleCharacterString: |
52 | return jsSingleCharacterString(vm, u.character); |
53 | case KnownStringImpl: |
54 | return jsString(vm, u.stringImpl); |
55 | case NewStringImpl: |
56 | return jsString(vm, AtomStringImpl::add(u.stringImpl)); |
57 | } |
58 | RELEASE_ASSERT_NOT_REACHED(); |
59 | return JSValue(); |
60 | } |
61 | |
62 | static TriState equalToSingleCharacter(JSValue value, UChar character) |
63 | { |
64 | if (!value.isString()) |
65 | return FalseTriState; |
66 | |
67 | JSString* jsString = asString(value); |
68 | if (jsString->length() != 1) |
69 | return FalseTriState; |
70 | |
71 | const StringImpl* string = jsString->tryGetValueImpl(); |
72 | if (!string) |
73 | return MixedTriState; |
74 | |
75 | return triState(string->at(0) == character); |
76 | } |
77 | |
78 | static TriState equalToStringImpl(JSValue value, StringImpl* stringImpl) |
79 | { |
80 | if (!value.isString()) |
81 | return FalseTriState; |
82 | |
83 | JSString* jsString = asString(value); |
84 | const StringImpl* string = jsString->tryGetValueImpl(); |
85 | if (!string) |
86 | return MixedTriState; |
87 | |
88 | return triState(WTF::equal(stringImpl, string)); |
89 | } |
90 | |
91 | const StringImpl* LazyJSValue::tryGetStringImpl(VM& vm) const |
92 | { |
93 | switch (m_kind) { |
94 | case KnownStringImpl: |
95 | case NewStringImpl: |
96 | return u.stringImpl; |
97 | |
98 | case KnownValue: |
99 | if (JSString* string = value()->dynamicCast<JSString*>(vm)) |
100 | return string->tryGetValueImpl(); |
101 | return nullptr; |
102 | |
103 | case SingleCharacterString: |
104 | return nullptr; |
105 | } |
106 | RELEASE_ASSERT_NOT_REACHED(); |
107 | return nullptr; |
108 | } |
109 | |
110 | String LazyJSValue::tryGetString(Graph& graph) const |
111 | { |
112 | switch (m_kind) { |
113 | case NewStringImpl: |
114 | return u.stringImpl; |
115 | |
116 | case SingleCharacterString: |
117 | return String(&u.character, 1); |
118 | |
119 | case KnownValue: |
120 | case KnownStringImpl: |
121 | if (const StringImpl* string = tryGetStringImpl(graph.m_vm)) { |
122 | unsigned ginormousStringLength = 10000; |
123 | if (string->length() > ginormousStringLength) |
124 | return String(); |
125 | |
126 | auto result = graph.m_copiedStrings.add(string, String()); |
127 | if (result.isNewEntry) |
128 | result.iterator->value = string->isolatedCopy(); |
129 | return result.iterator->value; |
130 | } |
131 | |
132 | return String(); |
133 | } |
134 | RELEASE_ASSERT_NOT_REACHED(); |
135 | return String(); |
136 | } |
137 | |
138 | TriState LazyJSValue::strictEqual(const LazyJSValue& other) const |
139 | { |
140 | switch (m_kind) { |
141 | case KnownValue: |
142 | switch (other.m_kind) { |
143 | case KnownValue: { |
144 | if (!value()->value() || !other.value()->value()) |
145 | return value()->value() == other.value()->value() ? TrueTriState : FalseTriState; |
146 | return JSValue::pureStrictEqual(value()->value(), other.value()->value()); |
147 | } |
148 | case SingleCharacterString: { |
149 | if (!value()->value()) |
150 | return FalseTriState; |
151 | return equalToSingleCharacter(value()->value(), other.character()); |
152 | } |
153 | case KnownStringImpl: |
154 | case NewStringImpl: { |
155 | if (!value()->value()) |
156 | return FalseTriState; |
157 | return equalToStringImpl(value()->value(), other.stringImpl()); |
158 | } |
159 | } |
160 | break; |
161 | case SingleCharacterString: |
162 | switch (other.m_kind) { |
163 | case SingleCharacterString: |
164 | return triState(character() == other.character()); |
165 | case KnownStringImpl: |
166 | case NewStringImpl: |
167 | if (other.stringImpl()->length() != 1) |
168 | return FalseTriState; |
169 | return triState(other.stringImpl()->at(0) == character()); |
170 | case KnownValue: |
171 | return other.strictEqual(*this); |
172 | } |
173 | break; |
174 | case KnownStringImpl: |
175 | case NewStringImpl: |
176 | switch (other.m_kind) { |
177 | case KnownStringImpl: |
178 | case NewStringImpl: |
179 | return triState(WTF::equal(stringImpl(), other.stringImpl())); |
180 | case SingleCharacterString: |
181 | case KnownValue: |
182 | return other.strictEqual(*this); |
183 | } |
184 | break; |
185 | } |
186 | RELEASE_ASSERT_NOT_REACHED(); |
187 | return FalseTriState; |
188 | } |
189 | |
190 | uintptr_t LazyJSValue::switchLookupValue(SwitchKind kind) const |
191 | { |
192 | // NB. Not every kind of JSValue will be able to give you a switch lookup |
193 | // value, and this method will assert, or do bad things, if you use it |
194 | // for a kind of value that can't. |
195 | switch (m_kind) { |
196 | case KnownValue: |
197 | switch (kind) { |
198 | case SwitchImm: |
199 | if (value()->value()) |
200 | return value()->value().asInt32(); |
201 | return 0; |
202 | case SwitchCell: |
203 | if (value()->value()) |
204 | return bitwise_cast<uintptr_t>(value()->value().asCell()); |
205 | return 0; |
206 | default: |
207 | RELEASE_ASSERT_NOT_REACHED(); |
208 | return 0; |
209 | } |
210 | case SingleCharacterString: |
211 | switch (kind) { |
212 | case SwitchChar: |
213 | return character(); |
214 | default: |
215 | RELEASE_ASSERT_NOT_REACHED(); |
216 | return 0; |
217 | } |
218 | case KnownStringImpl: |
219 | case NewStringImpl: |
220 | RELEASE_ASSERT_NOT_REACHED(); |
221 | return 0; |
222 | } |
223 | RELEASE_ASSERT_NOT_REACHED(); |
224 | return 0; |
225 | } |
226 | |
227 | void LazyJSValue::emit(CCallHelpers& jit, JSValueRegs result) const |
228 | { |
229 | if (m_kind == KnownValue) { |
230 | jit.moveValue(value()->value(), result); |
231 | return; |
232 | } |
233 | |
234 | // It must be some kind of cell. |
235 | #if USE(JSVALUE32_64) |
236 | jit.move(CCallHelpers::TrustedImm32(JSValue::CellTag), result.tagGPR()); |
237 | #endif |
238 | CCallHelpers::DataLabelPtr label = jit.moveWithPatch( |
239 | CCallHelpers::TrustedImmPtr(static_cast<size_t>(0xd1e7beeflu)), |
240 | result.payloadGPR()); |
241 | |
242 | LazyJSValue thisValue = *this; |
243 | |
244 | // Once we do this, we're committed. Otherwise we leak memory. Note that we call ref/deref |
245 | // manually to ensure that there is no concurrency shadiness. We are doing something here |
246 | // that might be rather brutal: transfering ownership of this string. |
247 | if (m_kind == NewStringImpl) |
248 | thisValue.u.stringImpl->ref(); |
249 | |
250 | CodeBlock* codeBlock = jit.codeBlock(); |
251 | |
252 | jit.addLinkTask( |
253 | [codeBlock, label, thisValue] (LinkBuffer& linkBuffer) { |
254 | JSValue realValue = thisValue.getValue(codeBlock->vm()); |
255 | RELEASE_ASSERT(realValue.isCell()); |
256 | |
257 | codeBlock->addConstant(ConcurrentJSLocker(codeBlock->m_lock), realValue); |
258 | |
259 | if (thisValue.m_kind == NewStringImpl) |
260 | thisValue.u.stringImpl->deref(); |
261 | |
262 | linkBuffer.patch(label, realValue.asCell()); |
263 | }); |
264 | } |
265 | |
266 | void LazyJSValue::dumpInContext(PrintStream& out, DumpContext* context) const |
267 | { |
268 | switch (m_kind) { |
269 | case KnownValue: |
270 | value()->dumpInContext(out, context); |
271 | return; |
272 | case SingleCharacterString: |
273 | out.print("Lazy:SingleCharacterString(" ); |
274 | out.printf("%04X" , static_cast<unsigned>(character())); |
275 | out.print(" / " , StringImpl::utf8ForCharacters(&u.character, 1).value(), ")" ); |
276 | return; |
277 | case KnownStringImpl: |
278 | out.print("Lazy:KnownString(" , stringImpl(), ")" ); |
279 | return; |
280 | case NewStringImpl: |
281 | out.print("Lazy:NewString(" , stringImpl(), ")" ); |
282 | return; |
283 | } |
284 | RELEASE_ASSERT_NOT_REACHED(); |
285 | } |
286 | |
287 | void LazyJSValue::dump(PrintStream& out) const |
288 | { |
289 | dumpInContext(out, 0); |
290 | } |
291 | |
292 | } } // namespace JSC::DFG |
293 | |
294 | #endif // ENABLE(DFG_JIT) |
295 | |
296 | |