1 | /* |
2 | * Copyright (C) 2013, 2014, 2016 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 | default: |
104 | return nullptr; |
105 | } |
106 | } |
107 | |
108 | String LazyJSValue::tryGetString(Graph& graph) const |
109 | { |
110 | switch (m_kind) { |
111 | case NewStringImpl: |
112 | return u.stringImpl; |
113 | |
114 | case SingleCharacterString: |
115 | return String(&u.character, 1); |
116 | |
117 | default: |
118 | if (const StringImpl* string = tryGetStringImpl(graph.m_vm)) { |
119 | unsigned ginormousStringLength = 10000; |
120 | if (string->length() > ginormousStringLength) |
121 | return String(); |
122 | |
123 | auto result = graph.m_copiedStrings.add(string, String()); |
124 | if (result.isNewEntry) |
125 | result.iterator->value = string->isolatedCopy(); |
126 | return result.iterator->value; |
127 | } |
128 | |
129 | return String(); |
130 | } |
131 | } |
132 | |
133 | TriState LazyJSValue::strictEqual(const LazyJSValue& other) const |
134 | { |
135 | switch (m_kind) { |
136 | case KnownValue: |
137 | switch (other.m_kind) { |
138 | case KnownValue: |
139 | return JSValue::pureStrictEqual(value()->value(), other.value()->value()); |
140 | case SingleCharacterString: |
141 | return equalToSingleCharacter(value()->value(), other.character()); |
142 | case KnownStringImpl: |
143 | case NewStringImpl: |
144 | return equalToStringImpl(value()->value(), other.stringImpl()); |
145 | } |
146 | break; |
147 | case SingleCharacterString: |
148 | switch (other.m_kind) { |
149 | case SingleCharacterString: |
150 | return triState(character() == other.character()); |
151 | case KnownStringImpl: |
152 | case NewStringImpl: |
153 | if (other.stringImpl()->length() != 1) |
154 | return FalseTriState; |
155 | return triState(other.stringImpl()->at(0) == character()); |
156 | default: |
157 | return other.strictEqual(*this); |
158 | } |
159 | break; |
160 | case KnownStringImpl: |
161 | case NewStringImpl: |
162 | switch (other.m_kind) { |
163 | case KnownStringImpl: |
164 | case NewStringImpl: |
165 | return triState(WTF::equal(stringImpl(), other.stringImpl())); |
166 | default: |
167 | return other.strictEqual(*this); |
168 | } |
169 | break; |
170 | } |
171 | RELEASE_ASSERT_NOT_REACHED(); |
172 | return FalseTriState; |
173 | } |
174 | |
175 | uintptr_t LazyJSValue::switchLookupValue(SwitchKind kind) const |
176 | { |
177 | // NB. Not every kind of JSValue will be able to give you a switch lookup |
178 | // value, and this method will assert, or do bad things, if you use it |
179 | // for a kind of value that can't. |
180 | switch (m_kind) { |
181 | case KnownValue: |
182 | switch (kind) { |
183 | case SwitchImm: |
184 | return value()->value().asInt32(); |
185 | case SwitchCell: |
186 | return bitwise_cast<uintptr_t>(value()->value().asCell()); |
187 | default: |
188 | RELEASE_ASSERT_NOT_REACHED(); |
189 | return 0; |
190 | } |
191 | case SingleCharacterString: |
192 | switch (kind) { |
193 | case SwitchChar: |
194 | return character(); |
195 | default: |
196 | RELEASE_ASSERT_NOT_REACHED(); |
197 | return 0; |
198 | } |
199 | default: |
200 | RELEASE_ASSERT_NOT_REACHED(); |
201 | return 0; |
202 | } |
203 | } |
204 | |
205 | void LazyJSValue::emit(CCallHelpers& jit, JSValueRegs result) const |
206 | { |
207 | if (m_kind == KnownValue) { |
208 | jit.moveValue(value()->value(), result); |
209 | return; |
210 | } |
211 | |
212 | // It must be some kind of cell. |
213 | #if USE(JSVALUE32_64) |
214 | jit.move(CCallHelpers::TrustedImm32(JSValue::CellTag), result.tagGPR()); |
215 | #endif |
216 | CCallHelpers::DataLabelPtr label = jit.moveWithPatch( |
217 | CCallHelpers::TrustedImmPtr(static_cast<size_t>(0xd1e7beeflu)), |
218 | result.payloadGPR()); |
219 | |
220 | LazyJSValue thisValue = *this; |
221 | |
222 | // Once we do this, we're committed. Otherwise we leak memory. Note that we call ref/deref |
223 | // manually to ensure that there is no concurrency shadiness. We are doing something here |
224 | // that might be rather brutal: transfering ownership of this string. |
225 | if (m_kind == NewStringImpl) |
226 | thisValue.u.stringImpl->ref(); |
227 | |
228 | CodeBlock* codeBlock = jit.codeBlock(); |
229 | |
230 | jit.addLinkTask( |
231 | [codeBlock, label, thisValue] (LinkBuffer& linkBuffer) { |
232 | JSValue realValue = thisValue.getValue(*codeBlock->vm()); |
233 | RELEASE_ASSERT(realValue.isCell()); |
234 | |
235 | codeBlock->addConstant(realValue); |
236 | |
237 | if (thisValue.m_kind == NewStringImpl) |
238 | thisValue.u.stringImpl->deref(); |
239 | |
240 | linkBuffer.patch(label, realValue.asCell()); |
241 | }); |
242 | } |
243 | |
244 | void LazyJSValue::dumpInContext(PrintStream& out, DumpContext* context) const |
245 | { |
246 | switch (m_kind) { |
247 | case KnownValue: |
248 | value()->dumpInContext(out, context); |
249 | return; |
250 | case SingleCharacterString: |
251 | out.print("Lazy:SingleCharacterString(" ); |
252 | out.printf("%04X" , static_cast<unsigned>(character())); |
253 | out.print(" / " , StringImpl::utf8ForCharacters(&u.character, 1).value(), ")" ); |
254 | return; |
255 | case KnownStringImpl: |
256 | out.print("Lazy:KnownString(" , stringImpl(), ")" ); |
257 | return; |
258 | case NewStringImpl: |
259 | out.print("Lazy:NewString(" , stringImpl(), ")" ); |
260 | return; |
261 | } |
262 | RELEASE_ASSERT_NOT_REACHED(); |
263 | } |
264 | |
265 | void LazyJSValue::dump(PrintStream& out) const |
266 | { |
267 | dumpInContext(out, 0); |
268 | } |
269 | |
270 | } } // namespace JSC::DFG |
271 | |
272 | #endif // ENABLE(DFG_JIT) |
273 | |
274 | |