1/*
2 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3 * Copyright (C) 2001 Peter Kelly (pmk@post.com)
4 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 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#ifndef JSString_h
24#define JSString_h
25
26#include "CallFrame.h"
27#include "CommonIdentifiers.h"
28#include "Identifier.h"
29#include "JSNumberCell.h"
30#include "PropertyDescriptor.h"
31#include "PropertySlot.h"
32
33namespace JSC {
34
35 class JSString;
36
37 JSString* jsEmptyString(JSGlobalData*);
38 JSString* jsEmptyString(ExecState*);
39 JSString* jsString(JSGlobalData*, const UString&); // returns empty string if passed null string
40 JSString* jsString(ExecState*, const UString&); // returns empty string if passed null string
41
42 JSString* jsSingleCharacterString(JSGlobalData*, UChar);
43 JSString* jsSingleCharacterString(ExecState*, UChar);
44 JSString* jsSingleCharacterSubstring(JSGlobalData*, const UString&, unsigned offset);
45 JSString* jsSingleCharacterSubstring(ExecState*, const UString&, unsigned offset);
46 JSString* jsSubstring(JSGlobalData*, const UString&, unsigned offset, unsigned length);
47 JSString* jsSubstring(ExecState*, const UString&, unsigned offset, unsigned length);
48
49 // Non-trivial strings are two or more characters long.
50 // These functions are faster than just calling jsString.
51 JSString* jsNontrivialString(JSGlobalData*, const UString&);
52 JSString* jsNontrivialString(ExecState*, const UString&);
53 JSString* jsNontrivialString(JSGlobalData*, const char*);
54 JSString* jsNontrivialString(ExecState*, const char*);
55
56 // Should be used for strings that are owned by an object that will
57 // likely outlive the JSValue this makes, such as the parse tree or a
58 // DOM object that contains a UString
59 JSString* jsOwnedString(JSGlobalData*, const UString&);
60 JSString* jsOwnedString(ExecState*, const UString&);
61
62 typedef void (*JSStringFinalizerCallback)(JSString*, void* context);
63 JSString* jsStringWithFinalizer(ExecState*, const UString&, JSStringFinalizerCallback callback, void* context);
64
65 class JS_EXPORTCLASS JSString : public JSCell {
66 public:
67 friend class JIT;
68 friend class JSGlobalData;
69
70 // A Rope is a string composed of a set of substrings.
71 class Rope : public RefCounted<Rope> {
72 public:
73 // A Rope is composed from a set of smaller strings called Fibers.
74 // Each Fiber in a rope is either UString::Rep or another Rope.
75 class Fiber {
76 public:
77 Fiber() : m_value(0) {}
78 Fiber(UString::Rep* string) : m_value(reinterpret_cast<intptr_t>(string)) {}
79 Fiber(Rope* rope) : m_value(reinterpret_cast<intptr_t>(rope) | 1) {}
80
81 Fiber(void* nonFiber) : m_value(reinterpret_cast<intptr_t>(nonFiber)) {}
82
83 void deref()
84 {
85 if (isRope())
86 rope()->deref();
87 else
88 string()->deref();
89 }
90
91 Fiber& ref()
92 {
93 if (isString())
94 string()->ref();
95 else
96 rope()->ref();
97 return *this;
98 }
99
100 unsigned refAndGetLength()
101 {
102 if (isString()) {
103 UString::Rep* rep = string();
104 return rep->ref()->size();
105 } else {
106 Rope* r = rope();
107 r->ref();
108 return r->stringLength();
109 }
110 }
111
112 bool isRope() { return m_value & 1; }
113 Rope* rope() { return reinterpret_cast<Rope*>(m_value & ~1); }
114 bool isString() { return !isRope(); }
115 UString::Rep* string() { return reinterpret_cast<UString::Rep*>(m_value); }
116
117 void* nonFiber() { return reinterpret_cast<void*>(m_value); }
118 private:
119 intptr_t m_value;
120 };
121
122 // Creates a Rope comprising of 'ropeLength' Fibers.
123 // The Rope is constructed in an uninitialized state - initialize must be called for each Fiber in the Rope.
124 static PassRefPtr<Rope> createOrNull(unsigned ropeLength)
125 {
126 void* allocation;
127 if (tryFastMalloc(n: sizeof(Rope) + (ropeLength - 1) * sizeof(Fiber)).getValue(data&: allocation))
128 return adoptRef(p: new (allocation) Rope(ropeLength));
129 return 0;
130 }
131
132 ~Rope();
133 void destructNonRecursive();
134
135 void append(unsigned &index, Fiber& fiber)
136 {
137 m_fibers[index++] = fiber;
138 m_stringLength += fiber.refAndGetLength();
139 }
140 void append(unsigned &index, const UString& string)
141 {
142 UString::Rep* rep = string.rep();
143 m_fibers[index++] = Fiber(rep);
144 m_stringLength += rep->ref()->size();
145 }
146 void append(unsigned& index, JSString* jsString)
147 {
148 if (jsString->isRope()) {
149 for (unsigned i = 0; i < jsString->m_ropeLength; ++i)
150 append(index, fiber&: jsString->m_fibers[i]);
151 } else
152 append(index, string: jsString->string());
153 }
154
155 unsigned ropeLength() { return m_ropeLength; }
156 unsigned stringLength() { return m_stringLength; }
157 Fiber& fibers(unsigned index) { return m_fibers[index]; }
158
159 private:
160 Rope(unsigned ropeLength) : m_ropeLength(ropeLength), m_stringLength(0) {}
161 void* operator new(size_t, void* inPlace) { return inPlace; }
162
163 unsigned m_ropeLength;
164 unsigned m_stringLength;
165 Fiber m_fibers[1];
166 };
167
168 ALWAYS_INLINE JSString(JSGlobalData* globalData, const UString& value)
169 : JSCell(globalData->stringStructure.get())
170 , m_stringLength(value.size())
171 , m_value(value)
172 , m_ropeLength(0)
173 {
174 Heap::heap(c: this)->reportExtraMemoryCost(cost: value.cost());
175 }
176
177 enum HasOtherOwnerType { HasOtherOwner };
178 JSString(JSGlobalData* globalData, const UString& value, HasOtherOwnerType)
179 : JSCell(globalData->stringStructure.get())
180 , m_stringLength(value.size())
181 , m_value(value)
182 , m_ropeLength(0)
183 {
184 }
185 JSString(JSGlobalData* globalData, PassRefPtr<UString::Rep> value, HasOtherOwnerType)
186 : JSCell(globalData->stringStructure.get())
187 , m_stringLength(value->size())
188 , m_value(value)
189 , m_ropeLength(0)
190 {
191 }
192 JSString(JSGlobalData* globalData, PassRefPtr<JSString::Rope> rope)
193 : JSCell(globalData->stringStructure.get())
194 , m_stringLength(rope->stringLength())
195 , m_ropeLength(1)
196 {
197 m_fibers[0] = rope.releaseRef();
198 }
199 // This constructor constructs a new string by concatenating s1 & s2.
200 // This should only be called with ropeLength <= 3.
201 JSString(JSGlobalData* globalData, unsigned ropeLength, JSString* s1, JSString* s2)
202 : JSCell(globalData->stringStructure.get())
203 , m_stringLength(s1->length() + s2->length())
204 , m_ropeLength(ropeLength)
205 {
206 ASSERT(ropeLength <= s_maxInternalRopeLength);
207 unsigned index = 0;
208 appendStringInConstruct(index, jsString: s1);
209 appendStringInConstruct(index, jsString: s2);
210 ASSERT(ropeLength == index);
211 }
212 // This constructor constructs a new string by concatenating s1 & s2.
213 // This should only be called with ropeLength <= 3.
214 JSString(JSGlobalData* globalData, unsigned ropeLength, JSString* s1, const UString& u2)
215 : JSCell(globalData->stringStructure.get())
216 , m_stringLength(s1->length() + u2.size())
217 , m_ropeLength(ropeLength)
218 {
219 ASSERT(ropeLength <= s_maxInternalRopeLength);
220 unsigned index = 0;
221 appendStringInConstruct(index, jsString: s1);
222 appendStringInConstruct(index, string: u2);
223 ASSERT(ropeLength == index);
224 }
225 // This constructor constructs a new string by concatenating s1 & s2.
226 // This should only be called with ropeLength <= 3.
227 JSString(JSGlobalData* globalData, unsigned ropeLength, const UString& u1, JSString* s2)
228 : JSCell(globalData->stringStructure.get())
229 , m_stringLength(u1.size() + s2->length())
230 , m_ropeLength(ropeLength)
231 {
232 ASSERT(ropeLength <= s_maxInternalRopeLength);
233 unsigned index = 0;
234 appendStringInConstruct(index, string: u1);
235 appendStringInConstruct(index, jsString: s2);
236 ASSERT(ropeLength == index);
237 }
238 // This constructor constructs a new string by concatenating v1, v2 & v3.
239 // This should only be called with ropeLength <= 3 ... which since every
240 // value must require a ropeLength of at least one implies that the length
241 // for each value must be exactly 1!
242 JSString(ExecState* exec, JSValue v1, JSValue v2, JSValue v3)
243 : JSCell(exec->globalData().stringStructure.get())
244 , m_stringLength(0)
245 , m_ropeLength(s_maxInternalRopeLength)
246 {
247 unsigned index = 0;
248 appendValueInConstructAndIncrementLength(exec, index, v: v1);
249 appendValueInConstructAndIncrementLength(exec, index, v: v2);
250 appendValueInConstructAndIncrementLength(exec, index, v: v3);
251 ASSERT(index == s_maxInternalRopeLength);
252 }
253
254 JSString(JSGlobalData* globalData, const UString& value, JSStringFinalizerCallback finalizer, void* context)
255 : JSCell(globalData->stringStructure.get())
256 , m_stringLength(value.size())
257 , m_value(value)
258 , m_ropeLength(0)
259 {
260 // nasty hack because we can't union non-POD types
261 m_fibers[0] = reinterpret_cast<void*>(reinterpret_cast<ptrdiff_t>(finalizer));
262 m_fibers[1] = context;
263 Heap::heap(c: this)->reportExtraMemoryCost(cost: value.cost());
264 }
265
266 ~JSString()
267 {
268 ASSERT(vptr() == JSGlobalData::jsStringVPtr);
269 for (unsigned i = 0; i < m_ropeLength; ++i)
270 m_fibers[i].deref();
271
272 if (!m_ropeLength && m_fibers[0].nonFiber()) {
273 JSStringFinalizerCallback finalizer = (JSStringFinalizerCallback)(m_fibers[0].nonFiber());
274 finalizer(this, m_fibers[1].nonFiber());
275 }
276 }
277
278 const UString& value(ExecState* exec) const
279 {
280 if (isRope())
281 resolveRope(exec);
282 return m_value;
283 }
284 const UString tryGetValue() const
285 {
286 if (isRope())
287 UString();
288 return m_value;
289 }
290 unsigned length() { return m_stringLength; }
291
292 bool getStringPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
293 bool getStringPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
294 bool getStringPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor&);
295
296 bool canGetIndex(unsigned i) { return i < m_stringLength; }
297 JSString* getIndex(ExecState*, unsigned);
298
299 static PassRefPtr<Structure> createStructure(JSValue proto) { return Structure::create(prototype: proto, typeInfo: TypeInfo(StringType, OverridesGetOwnPropertySlot | NeedsThisConversion)); }
300
301 private:
302 enum VPtrStealingHackType { VPtrStealingHack };
303 JSString(VPtrStealingHackType)
304 : JSCell(0)
305 , m_ropeLength(0)
306 {
307 }
308
309 void resolveRope(ExecState*) const;
310
311 void appendStringInConstruct(unsigned& index, const UString& string)
312 {
313 m_fibers[index++] = Rope::Fiber(string.rep()->ref());
314 }
315
316 void appendStringInConstruct(unsigned& index, JSString* jsString)
317 {
318 if (jsString->isRope()) {
319 for (unsigned i = 0; i < jsString->m_ropeLength; ++i)
320 m_fibers[index++] = jsString->m_fibers[i].ref();
321 } else
322 appendStringInConstruct(index, string: jsString->string());
323 }
324
325 void appendValueInConstructAndIncrementLength(ExecState* exec, unsigned& index, JSValue v)
326 {
327 if (v.isString()) {
328 ASSERT(asCell(v)->isString());
329 JSString* s = static_cast<JSString*>(asCell(value: v));
330 ASSERT(s->ropeLength() == 1);
331 appendStringInConstruct(index, jsString: s);
332 m_stringLength += s->length();
333 } else {
334 UString u(v.toString(exec));
335 m_fibers[index++] = Rope::Fiber(u.rep()->ref());
336 m_stringLength += u.size();
337 }
338 }
339
340 virtual JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const;
341 virtual bool getPrimitiveNumber(ExecState*, double& number, JSValue& value);
342 virtual bool toBoolean(ExecState*) const;
343 virtual double toNumber(ExecState*) const;
344 virtual JSObject* toObject(ExecState*) const;
345 virtual UString toString(ExecState*) const;
346
347 virtual JSObject* toThisObject(ExecState*) const;
348 virtual UString toThisString(ExecState*) const;
349 virtual JSString* toThisJSString(ExecState*);
350
351 // Actually getPropertySlot, not getOwnPropertySlot (see JSCell).
352 virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
353 virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
354 virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&);
355
356 static const unsigned s_maxInternalRopeLength = 3;
357
358 // A string is represented either by a UString or a Rope.
359 unsigned m_stringLength;
360 mutable UString m_value;
361 mutable unsigned m_ropeLength;
362 mutable Rope::Fiber m_fibers[s_maxInternalRopeLength];
363
364 bool isRope() const { return m_ropeLength; }
365 UString& string() { ASSERT(!isRope()); return m_value; }
366 unsigned ropeLength() { return m_ropeLength ? m_ropeLength : 1; }
367
368 friend JSValue jsString(ExecState* exec, JSString* s1, JSString* s2);
369 friend JSValue jsString(ExecState* exec, const UString& u1, JSString* s2);
370 friend JSValue jsString(ExecState* exec, JSString* s1, const UString& u2);
371 friend JSValue jsString(ExecState* exec, Register* strings, unsigned count);
372 friend JSValue jsString(ExecState* exec, JSValue thisValue, const ArgList& args);
373 friend JSString* jsStringWithFinalizer(ExecState*, const UString&, JSStringFinalizerCallback callback, void* context);
374 };
375
376 JSString* asString(JSValue);
377
378 // When an object is created from a different DLL, MSVC changes vptr to a "local" one right after invoking a constructor,
379 // see <http://groups.google.com/group/microsoft.public.vc.language/msg/55cdcefeaf770212>.
380 // This breaks isJSString(), and we don't need that hack anyway, so we change vptr back to primary one.
381 // The below function must be called by any inline function that invokes a JSString constructor.
382#if COMPILER(MSVC) && !defined(BUILDING_JavaScriptCore)
383 inline JSString* fixupVPtr(JSGlobalData* globalData, JSString* string) { string->setVPtr(globalData->jsStringVPtr); return string; }
384#else
385 inline JSString* fixupVPtr(JSGlobalData*, JSString* string) { return string; }
386#endif
387
388 inline JSString* asString(JSValue value)
389 {
390 ASSERT(asCell(value)->isString());
391 return static_cast<JSString*>(asCell(value));
392 }
393
394 inline JSString* jsEmptyString(JSGlobalData* globalData)
395 {
396 return globalData->smallStrings.emptyString(globalData);
397 }
398
399 inline JSString* jsSingleCharacterString(JSGlobalData* globalData, UChar c)
400 {
401 if (c <= 0xFF)
402 return globalData->smallStrings.singleCharacterString(globalData, character: c);
403 return fixupVPtr(globalData, string: new (globalData) JSString(globalData, UString(&c, 1)));
404 }
405
406 inline JSString* jsSingleCharacterSubstring(JSGlobalData* globalData, const UString& s, unsigned offset)
407 {
408 ASSERT(offset < static_cast<unsigned>(s.size()));
409 UChar c = s.data()[offset];
410 if (c <= 0xFF)
411 return globalData->smallStrings.singleCharacterString(globalData, character: c);
412 return fixupVPtr(globalData, string: new (globalData) JSString(globalData, UString(UString::Rep::create(rep: s.rep(), offset, length: 1))));
413 }
414
415 inline JSString* jsNontrivialString(JSGlobalData* globalData, const char* s)
416 {
417 ASSERT(s);
418 ASSERT(s[0]);
419 ASSERT(s[1]);
420 return fixupVPtr(globalData, string: new (globalData) JSString(globalData, s));
421 }
422
423 inline JSString* jsNontrivialString(JSGlobalData* globalData, const UString& s)
424 {
425 ASSERT(s.size() > 1);
426 return fixupVPtr(globalData, string: new (globalData) JSString(globalData, s));
427 }
428
429 inline JSString* JSString::getIndex(ExecState* exec, unsigned i)
430 {
431 ASSERT(canGetIndex(i));
432 return jsSingleCharacterSubstring(globalData: &exec->globalData(), s: value(exec), offset: i);
433 }
434
435 inline JSString* jsString(JSGlobalData* globalData, const UString& s)
436 {
437 int size = s.size();
438 if (!size)
439 return globalData->smallStrings.emptyString(globalData);
440 if (size == 1) {
441 UChar c = s.data()[0];
442 if (c <= 0xFF)
443 return globalData->smallStrings.singleCharacterString(globalData, character: c);
444 }
445 return fixupVPtr(globalData, string: new (globalData) JSString(globalData, s));
446 }
447
448 inline JSString* jsStringWithFinalizer(ExecState* exec, const UString& s, JSStringFinalizerCallback callback, void* context)
449 {
450 ASSERT(s.size() && (s.size() > 1 || s.data()[0] > 0xFF));
451 JSGlobalData* globalData = &exec->globalData();
452 return fixupVPtr(globalData, string: new (globalData) JSString(globalData, s, callback, context));
453 }
454
455 inline JSString* jsSubstring(JSGlobalData* globalData, const UString& s, unsigned offset, unsigned length)
456 {
457 ASSERT(offset <= static_cast<unsigned>(s.size()));
458 ASSERT(length <= static_cast<unsigned>(s.size()));
459 ASSERT(offset + length <= static_cast<unsigned>(s.size()));
460 if (!length)
461 return globalData->smallStrings.emptyString(globalData);
462 if (length == 1) {
463 UChar c = s.data()[offset];
464 if (c <= 0xFF)
465 return globalData->smallStrings.singleCharacterString(globalData, character: c);
466 }
467 return fixupVPtr(globalData, string: new (globalData) JSString(globalData, UString(UString::Rep::create(rep: s.rep(), offset, length)), JSString::HasOtherOwner));
468 }
469
470 inline JSString* jsOwnedString(JSGlobalData* globalData, const UString& s)
471 {
472 int size = s.size();
473 if (!size)
474 return globalData->smallStrings.emptyString(globalData);
475 if (size == 1) {
476 UChar c = s.data()[0];
477 if (c <= 0xFF)
478 return globalData->smallStrings.singleCharacterString(globalData, character: c);
479 }
480 return fixupVPtr(globalData, string: new (globalData) JSString(globalData, s, JSString::HasOtherOwner));
481 }
482
483 inline JSString* jsEmptyString(ExecState* exec) { return jsEmptyString(globalData: &exec->globalData()); }
484 inline JSString* jsString(ExecState* exec, const UString& s) { return jsString(globalData: &exec->globalData(), s); }
485 inline JSString* jsSingleCharacterString(ExecState* exec, UChar c) { return jsSingleCharacterString(globalData: &exec->globalData(), c); }
486 inline JSString* jsSingleCharacterSubstring(ExecState* exec, const UString& s, unsigned offset) { return jsSingleCharacterSubstring(globalData: &exec->globalData(), s, offset); }
487 inline JSString* jsSubstring(ExecState* exec, const UString& s, unsigned offset, unsigned length) { return jsSubstring(globalData: &exec->globalData(), s, offset, length); }
488 inline JSString* jsNontrivialString(ExecState* exec, const UString& s) { return jsNontrivialString(globalData: &exec->globalData(), s); }
489 inline JSString* jsNontrivialString(ExecState* exec, const char* s) { return jsNontrivialString(globalData: &exec->globalData(), s); }
490 inline JSString* jsOwnedString(ExecState* exec, const UString& s) { return jsOwnedString(globalData: &exec->globalData(), s); }
491
492 ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
493 {
494 if (propertyName == exec->propertyNames().length) {
495 slot.setValue(jsNumber(exec, i: m_stringLength));
496 return true;
497 }
498
499 bool isStrictUInt32;
500 unsigned i = propertyName.toStrictUInt32(ok: &isStrictUInt32);
501 if (isStrictUInt32 && i < m_stringLength) {
502 slot.setValue(jsSingleCharacterSubstring(exec, s: value(exec), offset: i));
503 return true;
504 }
505
506 return false;
507 }
508
509 ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
510 {
511 if (propertyName < m_stringLength) {
512 slot.setValue(jsSingleCharacterSubstring(exec, s: value(exec), offset: propertyName));
513 return true;
514 }
515
516 return false;
517 }
518
519 inline bool isJSString(JSGlobalData* globalData, JSValue v) { return v.isCell() && v.asCell()->vptr() == globalData->jsStringVPtr; }
520
521 // --- JSValue inlines ----------------------------
522
523 inline JSString* JSValue::toThisJSString(ExecState* exec)
524 {
525 return isCell() ? asCell()->toThisJSString(exec) : jsString(exec, s: toString(exec));
526 }
527
528 inline UString JSValue::toString(ExecState* exec) const
529 {
530 if (isString())
531 return static_cast<JSString*>(asCell())->value(exec);
532 if (isInt32())
533 return exec->globalData().numericStrings.add(i: asInt32());
534 if (isDouble())
535 return exec->globalData().numericStrings.add(d: asDouble());
536 if (isTrue())
537 return "true";
538 if (isFalse())
539 return "false";
540 if (isNull())
541 return "null";
542 if (isUndefined())
543 return "undefined";
544 ASSERT(isCell());
545 return asCell()->toString(exec);
546 }
547
548 inline UString JSValue::toPrimitiveString(ExecState* exec) const
549 {
550 if (isString())
551 return static_cast<JSString*>(asCell())->value(exec);
552 if (isInt32())
553 return exec->globalData().numericStrings.add(i: asInt32());
554 if (isDouble())
555 return exec->globalData().numericStrings.add(d: asDouble());
556 if (isTrue())
557 return "true";
558 if (isFalse())
559 return "false";
560 if (isNull())
561 return "null";
562 if (isUndefined())
563 return "undefined";
564 ASSERT(isCell());
565 return asCell()->toPrimitive(exec, NoPreference).toString(exec);
566 }
567
568} // namespace JSC
569
570#endif // JSString_h
571

source code of qtscript/src/3rdparty/javascriptcore/JavaScriptCore/runtime/JSString.h