Use asString instead of toWTFString, toString, or getString when we already checked...
[WebKit-https.git] / Source / JavaScriptCore / runtime / JSString.cpp
1 /*
2  *  Copyright (C) 1999-2002 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
4  *  Copyright (C) 2004, 2007-2008, 2015-2016 Apple Inc. All rights reserved.
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Library General Public
8  *  License as published by the Free Software Foundation; either
9  *  version 2 of the License, or (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Library General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Library General Public License
17  *  along with this library; see the file COPYING.LIB.  If not, write to
18  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  *  Boston, MA 02110-1301, USA.
20  *
21  */
22
23 #include "config.h"
24 #include "JSString.h"
25
26 #include "JSGlobalObject.h"
27 #include "JSGlobalObjectFunctions.h"
28 #include "JSObject.h"
29 #include "JSCInlines.h"
30 #include "StringObject.h"
31 #include "StringPrototype.h"
32 #include "StrongInlines.h"
33
34 namespace JSC {
35     
36 const ClassInfo JSString::s_info = { "string", 0, 0, CREATE_METHOD_TABLE(JSString) };
37
38 Structure* JSString::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto)
39 {
40     return Structure::create(vm, globalObject, proto, TypeInfo(StringType, StructureFlags), info());
41 }
42
43 void JSRopeString::RopeBuilder::expand()
44 {
45     ASSERT(m_index == JSRopeString::s_maxInternalRopeLength);
46     JSString* jsString = m_jsString;
47     RELEASE_ASSERT(jsString);
48     m_jsString = jsStringBuilder(&m_vm);
49     m_index = 0;
50     append(jsString);
51 }
52
53 void JSString::destroy(JSCell* cell)
54 {
55     static_cast<JSString*>(cell)->JSString::~JSString();
56 }
57
58 void JSString::dumpToStream(const JSCell* cell, PrintStream& out)
59 {
60     const JSString* thisObject = jsCast<const JSString*>(cell);
61     out.printf("<%p, %s, [%u], ", thisObject, thisObject->className(), thisObject->length());
62     if (thisObject->isRope())
63         out.printf("[rope]");
64     else {
65         WTF::StringImpl* ourImpl = thisObject->m_value.impl();
66         if (ourImpl->is8Bit())
67             out.printf("[8 %p]", ourImpl->characters8());
68         else
69             out.printf("[16 %p]", ourImpl->characters16());
70     }
71     out.printf(">");
72 }
73
74 bool JSString::equalSlowCase(ExecState* exec, JSString* other) const
75 {
76     VM& vm = exec->vm();
77     auto scope = DECLARE_THROW_SCOPE(vm);
78     String str1 = value(exec);
79     String str2 = other->value(exec);
80     RETURN_IF_EXCEPTION(scope, false);
81     return WTF::equal(*str1.impl(), *str2.impl());
82 }
83
84 size_t JSString::estimatedSize(JSCell* cell)
85 {
86     JSString* thisObject = asString(cell);
87     if (thisObject->isRope())
88         return Base::estimatedSize(cell);
89     return Base::estimatedSize(cell) + thisObject->m_value.impl()->costDuringGC();
90 }
91
92 void JSString::visitChildren(JSCell* cell, SlotVisitor& visitor)
93 {
94     JSString* thisObject = asString(cell);
95     Base::visitChildren(thisObject, visitor);
96     
97     if (thisObject->isRope())
98         static_cast<JSRopeString*>(thisObject)->visitFibers(visitor);
99     if (StringImpl* impl = thisObject->m_value.impl())
100         visitor.reportExtraMemoryVisited(impl->costDuringGC());
101 }
102
103 void JSRopeString::visitFibers(SlotVisitor& visitor)
104 {
105     if (isSubstring()) {
106         visitor.append(substringBase());
107         return;
108     }
109     for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i)
110         visitor.append(fiber(i));
111 }
112
113 static const unsigned maxLengthForOnStackResolve = 2048;
114
115 void JSRopeString::resolveRopeInternal8(LChar* buffer) const
116 {
117     if (isSubstring()) {
118         StringImpl::copyChars(
119             buffer, substringBase()->m_value.characters8() + substringOffset(), length());
120         return;
121     }
122     
123     resolveRopeInternal8NoSubstring(buffer);
124 }
125
126 void JSRopeString::resolveRopeInternal8NoSubstring(LChar* buffer) const
127 {
128     for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i) {
129         if (fiber(i)->isRope()) {
130             resolveRopeSlowCase8(buffer);
131             return;
132         }
133     }
134
135     LChar* position = buffer;
136     for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i) {
137         const StringImpl& fiberString = *fiber(i)->m_value.impl();
138         unsigned length = fiberString.length();
139         StringImpl::copyChars(position, fiberString.characters8(), length);
140         position += length;
141     }
142     ASSERT((buffer + length()) == position);
143 }
144
145 void JSRopeString::resolveRopeInternal16(UChar* buffer) const
146 {
147     if (isSubstring()) {
148         StringImpl::copyChars(
149             buffer, substringBase()->m_value.characters16() + substringOffset(), length());
150         return;
151     }
152     
153     resolveRopeInternal16NoSubstring(buffer);
154 }
155
156 void JSRopeString::resolveRopeInternal16NoSubstring(UChar* buffer) const
157 {
158     for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i) {
159         if (fiber(i)->isRope()) {
160             resolveRopeSlowCase(buffer);
161             return;
162         }
163     }
164
165     UChar* position = buffer;
166     for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i) {
167         const StringImpl& fiberString = *fiber(i)->m_value.impl();
168         unsigned length = fiberString.length();
169         if (fiberString.is8Bit())
170             StringImpl::copyChars(position, fiberString.characters8(), length);
171         else
172             StringImpl::copyChars(position, fiberString.characters16(), length);
173         position += length;
174     }
175     ASSERT((buffer + length()) == position);
176 }
177
178 void JSRopeString::resolveRopeToAtomicString(ExecState* exec) const
179 {
180     if (length() > maxLengthForOnStackResolve) {
181         resolveRope(exec);
182         m_value = AtomicString(m_value);
183         setIs8Bit(m_value.impl()->is8Bit());
184         return;
185     }
186
187     if (is8Bit()) {
188         LChar buffer[maxLengthForOnStackResolve];
189         resolveRopeInternal8(buffer);
190         m_value = AtomicString(buffer, length());
191         setIs8Bit(m_value.impl()->is8Bit());
192     } else {
193         UChar buffer[maxLengthForOnStackResolve];
194         resolveRopeInternal16(buffer);
195         m_value = AtomicString(buffer, length());
196         setIs8Bit(m_value.impl()->is8Bit());
197     }
198
199     clearFibers();
200
201     // If we resolved a string that didn't previously exist, notify the heap that we've grown.
202     if (m_value.impl()->hasOneRef())
203         Heap::heap(this)->reportExtraMemoryAllocated(m_value.impl()->cost());
204 }
205
206 void JSRopeString::clearFibers() const
207 {
208     for (size_t i = 0; i < s_maxInternalRopeLength; ++i)
209         u[i].number = 0;
210 }
211
212 RefPtr<AtomicStringImpl> JSRopeString::resolveRopeToExistingAtomicString(ExecState* exec) const
213 {
214     if (length() > maxLengthForOnStackResolve) {
215         resolveRope(exec);
216         if (RefPtr<AtomicStringImpl> existingAtomicString = AtomicStringImpl::lookUp(m_value.impl())) {
217             m_value = *existingAtomicString;
218             setIs8Bit(m_value.impl()->is8Bit());
219             clearFibers();
220             return existingAtomicString;
221         }
222         return nullptr;
223     }
224     
225     if (is8Bit()) {
226         LChar buffer[maxLengthForOnStackResolve];
227         resolveRopeInternal8(buffer);
228         if (RefPtr<AtomicStringImpl> existingAtomicString = AtomicStringImpl::lookUp(buffer, length())) {
229             m_value = *existingAtomicString;
230             setIs8Bit(m_value.impl()->is8Bit());
231             clearFibers();
232             return existingAtomicString;
233         }
234     } else {
235         UChar buffer[maxLengthForOnStackResolve];
236         resolveRopeInternal16(buffer);
237         if (RefPtr<AtomicStringImpl> existingAtomicString = AtomicStringImpl::lookUp(buffer, length())) {
238             m_value = *existingAtomicString;
239             setIs8Bit(m_value.impl()->is8Bit());
240             clearFibers();
241             return existingAtomicString;
242         }
243     }
244
245     return nullptr;
246 }
247
248 void JSRopeString::resolveRope(ExecState* exec) const
249 {
250     ASSERT(isRope());
251     
252     if (isSubstring()) {
253         ASSERT(!substringBase()->isRope());
254         m_value = substringBase()->m_value.substringSharingImpl(substringOffset(), length());
255         substringBase().clear();
256         return;
257     }
258     
259     if (is8Bit()) {
260         LChar* buffer;
261         if (auto newImpl = StringImpl::tryCreateUninitialized(length(), buffer)) {
262             Heap::heap(this)->reportExtraMemoryAllocated(newImpl->cost());
263             m_value = WTFMove(newImpl);
264         } else {
265             outOfMemory(exec);
266             return;
267         }
268         resolveRopeInternal8NoSubstring(buffer);
269         clearFibers();
270         ASSERT(!isRope());
271         return;
272     }
273
274     UChar* buffer;
275     if (auto newImpl = StringImpl::tryCreateUninitialized(length(), buffer)) {
276         Heap::heap(this)->reportExtraMemoryAllocated(newImpl->cost());
277         m_value = WTFMove(newImpl);
278     } else {
279         outOfMemory(exec);
280         return;
281     }
282
283     resolveRopeInternal16NoSubstring(buffer);
284     clearFibers();
285     ASSERT(!isRope());
286 }
287
288 // Overview: These functions convert a JSString from holding a string in rope form
289 // down to a simple String representation. It does so by building up the string
290 // backwards, since we want to avoid recursion, we expect that the tree structure
291 // representing the rope is likely imbalanced with more nodes down the left side
292 // (since appending to the string is likely more common) - and as such resolving
293 // in this fashion should minimize work queue size.  (If we built the queue forwards
294 // we would likely have to place all of the constituent StringImpls into the
295 // Vector before performing any concatenation, but by working backwards we likely
296 // only fill the queue with the number of substrings at any given level in a
297 // rope-of-ropes.)    
298 void JSRopeString::resolveRopeSlowCase8(LChar* buffer) const
299 {
300     LChar* position = buffer + length(); // We will be working backwards over the rope.
301     Vector<JSString*, 32, UnsafeVectorOverflow> workQueue; // Putting strings into a Vector is only OK because there are no GC points in this method.
302     
303     for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i)
304         workQueue.append(fiber(i).get());
305
306     while (!workQueue.isEmpty()) {
307         JSString* currentFiber = workQueue.last();
308         workQueue.removeLast();
309
310         const LChar* characters;
311         
312         if (currentFiber->isRope()) {
313             JSRopeString* currentFiberAsRope = static_cast<JSRopeString*>(currentFiber);
314             if (!currentFiberAsRope->isSubstring()) {
315                 for (size_t i = 0; i < s_maxInternalRopeLength && currentFiberAsRope->fiber(i); ++i)
316                     workQueue.append(currentFiberAsRope->fiber(i).get());
317                 continue;
318             }
319             ASSERT(!currentFiberAsRope->substringBase()->isRope());
320             characters =
321                 currentFiberAsRope->substringBase()->m_value.characters8() +
322                 currentFiberAsRope->substringOffset();
323         } else
324             characters = currentFiber->m_value.characters8();
325         
326         unsigned length = currentFiber->length();
327         position -= length;
328         StringImpl::copyChars(position, characters, length);
329     }
330
331     ASSERT(buffer == position);
332 }
333
334 void JSRopeString::resolveRopeSlowCase(UChar* buffer) const
335 {
336     UChar* position = buffer + length(); // We will be working backwards over the rope.
337     Vector<JSString*, 32, UnsafeVectorOverflow> workQueue; // These strings are kept alive by the parent rope, so using a Vector is OK.
338
339     for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i)
340         workQueue.append(fiber(i).get());
341
342     while (!workQueue.isEmpty()) {
343         JSString* currentFiber = workQueue.last();
344         workQueue.removeLast();
345
346         if (currentFiber->isRope()) {
347             JSRopeString* currentFiberAsRope = static_cast<JSRopeString*>(currentFiber);
348             if (currentFiberAsRope->isSubstring()) {
349                 ASSERT(!currentFiberAsRope->substringBase()->isRope());
350                 StringImpl* string = static_cast<StringImpl*>(
351                     currentFiberAsRope->substringBase()->m_value.impl());
352                 unsigned offset = currentFiberAsRope->substringOffset();
353                 unsigned length = currentFiberAsRope->length();
354                 position -= length;
355                 if (string->is8Bit())
356                     StringImpl::copyChars(position, string->characters8() + offset, length);
357                 else
358                     StringImpl::copyChars(position, string->characters16() + offset, length);
359                 continue;
360             }
361             for (size_t i = 0; i < s_maxInternalRopeLength && currentFiberAsRope->fiber(i); ++i)
362                 workQueue.append(currentFiberAsRope->fiber(i).get());
363             continue;
364         }
365
366         StringImpl* string = static_cast<StringImpl*>(currentFiber->m_value.impl());
367         unsigned length = string->length();
368         position -= length;
369         if (string->is8Bit())
370             StringImpl::copyChars(position, string->characters8(), length);
371         else
372             StringImpl::copyChars(position, string->characters16(), length);
373     }
374
375     ASSERT(buffer == position);
376 }
377
378 void JSRopeString::outOfMemory(ExecState* exec) const
379 {
380     VM& vm = exec->vm();
381     auto scope = DECLARE_THROW_SCOPE(vm);
382
383     clearFibers();
384     ASSERT(isRope());
385     ASSERT(m_value.isNull());
386     if (exec)
387         throwOutOfMemoryError(exec, scope);
388 }
389
390 JSValue JSString::toPrimitive(ExecState*, PreferredPrimitiveType) const
391 {
392     return const_cast<JSString*>(this);
393 }
394
395 bool JSString::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result) const
396 {
397     result = this;
398     number = jsToNumber(unsafeView(*exec));
399     return false;
400 }
401
402 double JSString::toNumber(ExecState* exec) const
403 {
404     return jsToNumber(unsafeView(*exec));
405 }
406
407 inline StringObject* StringObject::create(VM& vm, JSGlobalObject* globalObject, JSString* string)
408 {
409     StringObject* object = new (NotNull, allocateCell<StringObject>(vm.heap)) StringObject(vm, globalObject->stringObjectStructure());
410     object->finishCreation(vm, string);
411     return object;
412 }
413
414 JSObject* JSString::toObject(ExecState* exec, JSGlobalObject* globalObject) const
415 {
416     return StringObject::create(exec->vm(), globalObject, const_cast<JSString*>(this));
417 }
418
419 JSValue JSString::toThis(JSCell* cell, ExecState* exec, ECMAMode ecmaMode)
420 {
421     if (ecmaMode == StrictMode)
422         return cell;
423     return StringObject::create(exec->vm(), exec->lexicalGlobalObject(), asString(cell));
424 }
425
426 bool JSString::getStringPropertyDescriptor(ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor)
427 {
428     if (propertyName == exec->propertyNames().length) {
429         descriptor.setDescriptor(jsNumber(length()), DontEnum | DontDelete | ReadOnly);
430         return true;
431     }
432     
433     std::optional<uint32_t> index = parseIndex(propertyName);
434     if (index && index.value() < length()) {
435         descriptor.setDescriptor(getIndex(exec, index.value()), DontDelete | ReadOnly);
436         return true;
437     }
438     
439     return false;
440 }
441
442 JSString* jsStringWithCacheSlowCase(VM& vm, StringImpl& stringImpl)
443 {
444     if (JSString* string = vm.stringCache.get(&stringImpl))
445         return string;
446
447     JSString* string = jsString(&vm, String(stringImpl));
448     vm.lastCachedString.set(vm, string);
449     return string;
450 }
451
452 } // namespace JSC