|
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 #include "RopeImpl.h" |
|
33 |
|
34 namespace JSC { |
|
35 |
|
36 class JSString; |
|
37 |
|
38 JSString* jsEmptyString(JSGlobalData*); |
|
39 JSString* jsEmptyString(ExecState*); |
|
40 JSString* jsString(JSGlobalData*, const UString&); // returns empty string if passed null string |
|
41 JSString* jsString(ExecState*, const UString&); // returns empty string if passed null string |
|
42 |
|
43 JSString* jsSingleCharacterString(JSGlobalData*, UChar); |
|
44 JSString* jsSingleCharacterString(ExecState*, UChar); |
|
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 friend class SpecializedThunkJIT; |
|
70 friend struct ThunkHelpers; |
|
71 |
|
72 class RopeBuilder { |
|
73 public: |
|
74 RopeBuilder(unsigned fiberCount) |
|
75 : m_index(0) |
|
76 , m_rope(RopeImpl::tryCreateUninitialized(fiberCount)) |
|
77 { |
|
78 } |
|
79 |
|
80 bool isOutOfMemory() { return !m_rope; } |
|
81 |
|
82 void append(RopeImpl::Fiber& fiber) |
|
83 { |
|
84 ASSERT(m_rope); |
|
85 m_rope->initializeFiber(m_index, fiber); |
|
86 } |
|
87 void append(const UString& string) |
|
88 { |
|
89 ASSERT(m_rope); |
|
90 m_rope->initializeFiber(m_index, string.rep()); |
|
91 } |
|
92 void append(JSString* jsString) |
|
93 { |
|
94 if (jsString->isRope()) { |
|
95 for (unsigned i = 0; i < jsString->m_fiberCount; ++i) |
|
96 append(jsString->m_other.m_fibers[i]); |
|
97 } else |
|
98 append(jsString->string()); |
|
99 } |
|
100 |
|
101 PassRefPtr<RopeImpl> release() |
|
102 { |
|
103 ASSERT(m_index == m_rope->fiberCount()); |
|
104 return m_rope.release(); |
|
105 } |
|
106 |
|
107 unsigned length() { return m_rope->length(); } |
|
108 |
|
109 private: |
|
110 unsigned m_index; |
|
111 RefPtr<RopeImpl> m_rope; |
|
112 }; |
|
113 |
|
114 class RopeIterator { |
|
115 public: |
|
116 RopeIterator() { } |
|
117 |
|
118 RopeIterator(RopeImpl::Fiber* fibers, size_t fiberCount) |
|
119 { |
|
120 ASSERT(fiberCount); |
|
121 m_workQueue.append(WorkItem(fibers, fiberCount)); |
|
122 skipRopes(); |
|
123 } |
|
124 |
|
125 RopeIterator& operator++() |
|
126 { |
|
127 WorkItem& item = m_workQueue.last(); |
|
128 ASSERT(!RopeImpl::isRope(item.fibers[item.i])); |
|
129 if (++item.i == item.fiberCount) |
|
130 m_workQueue.removeLast(); |
|
131 skipRopes(); |
|
132 return *this; |
|
133 } |
|
134 |
|
135 UStringImpl* operator*() |
|
136 { |
|
137 WorkItem& item = m_workQueue.last(); |
|
138 RopeImpl::Fiber fiber = item.fibers[item.i]; |
|
139 ASSERT(!RopeImpl::isRope(fiber)); |
|
140 return static_cast<UStringImpl*>(fiber); |
|
141 } |
|
142 |
|
143 bool operator!=(const RopeIterator& other) const |
|
144 { |
|
145 return m_workQueue != other.m_workQueue; |
|
146 } |
|
147 |
|
148 private: |
|
149 struct WorkItem { |
|
150 WorkItem(RopeImpl::Fiber* fibers, size_t fiberCount) |
|
151 : fibers(fibers) |
|
152 , fiberCount(fiberCount) |
|
153 , i(0) |
|
154 { |
|
155 } |
|
156 |
|
157 bool operator!=(const WorkItem& other) const |
|
158 { |
|
159 return fibers != other.fibers || fiberCount != other.fiberCount || i != other.i; |
|
160 } |
|
161 |
|
162 RopeImpl::Fiber* fibers; |
|
163 size_t fiberCount; |
|
164 size_t i; |
|
165 }; |
|
166 |
|
167 void skipRopes() |
|
168 { |
|
169 if (m_workQueue.isEmpty()) |
|
170 return; |
|
171 |
|
172 while (1) { |
|
173 WorkItem& item = m_workQueue.last(); |
|
174 RopeImpl::Fiber fiber = item.fibers[item.i]; |
|
175 if (!RopeImpl::isRope(fiber)) |
|
176 break; |
|
177 RopeImpl* rope = static_cast<RopeImpl*>(fiber); |
|
178 if (++item.i == item.fiberCount) |
|
179 m_workQueue.removeLast(); |
|
180 m_workQueue.append(WorkItem(rope->fibers(), rope->fiberCount())); |
|
181 } |
|
182 } |
|
183 |
|
184 Vector<WorkItem, 16> m_workQueue; |
|
185 }; |
|
186 |
|
187 ALWAYS_INLINE JSString(JSGlobalData* globalData, const UString& value) |
|
188 : JSCell(globalData->stringStructure.get()) |
|
189 , m_length(value.size()) |
|
190 , m_value(value) |
|
191 , m_fiberCount(0) |
|
192 { |
|
193 ASSERT(!m_value.isNull()); |
|
194 Heap::heap(this)->reportExtraMemoryCost(value.cost()); |
|
195 } |
|
196 |
|
197 enum HasOtherOwnerType { HasOtherOwner }; |
|
198 JSString(JSGlobalData* globalData, const UString& value, HasOtherOwnerType) |
|
199 : JSCell(globalData->stringStructure.get()) |
|
200 , m_length(value.size()) |
|
201 , m_value(value) |
|
202 , m_fiberCount(0) |
|
203 { |
|
204 ASSERT(!m_value.isNull()); |
|
205 } |
|
206 JSString(JSGlobalData* globalData, PassRefPtr<UStringImpl> value, HasOtherOwnerType) |
|
207 : JSCell(globalData->stringStructure.get()) |
|
208 , m_length(value->length()) |
|
209 , m_value(value) |
|
210 , m_fiberCount(0) |
|
211 { |
|
212 ASSERT(!m_value.isNull()); |
|
213 } |
|
214 JSString(JSGlobalData* globalData, PassRefPtr<RopeImpl> rope) |
|
215 : JSCell(globalData->stringStructure.get()) |
|
216 , m_length(rope->length()) |
|
217 , m_fiberCount(1) |
|
218 { |
|
219 m_other.m_fibers[0] = rope.releaseRef(); |
|
220 } |
|
221 // This constructor constructs a new string by concatenating s1 & s2. |
|
222 // This should only be called with fiberCount <= 3. |
|
223 JSString(JSGlobalData* globalData, unsigned fiberCount, JSString* s1, JSString* s2) |
|
224 : JSCell(globalData->stringStructure.get()) |
|
225 , m_length(s1->length() + s2->length()) |
|
226 , m_fiberCount(fiberCount) |
|
227 { |
|
228 ASSERT(fiberCount <= s_maxInternalRopeLength); |
|
229 unsigned index = 0; |
|
230 appendStringInConstruct(index, s1); |
|
231 appendStringInConstruct(index, s2); |
|
232 ASSERT(fiberCount == index); |
|
233 } |
|
234 // This constructor constructs a new string by concatenating s1 & s2. |
|
235 // This should only be called with fiberCount <= 3. |
|
236 JSString(JSGlobalData* globalData, unsigned fiberCount, JSString* s1, const UString& u2) |
|
237 : JSCell(globalData->stringStructure.get()) |
|
238 , m_length(s1->length() + u2.size()) |
|
239 , m_fiberCount(fiberCount) |
|
240 { |
|
241 ASSERT(fiberCount <= s_maxInternalRopeLength); |
|
242 unsigned index = 0; |
|
243 appendStringInConstruct(index, s1); |
|
244 appendStringInConstruct(index, u2); |
|
245 ASSERT(fiberCount == index); |
|
246 } |
|
247 // This constructor constructs a new string by concatenating s1 & s2. |
|
248 // This should only be called with fiberCount <= 3. |
|
249 JSString(JSGlobalData* globalData, unsigned fiberCount, const UString& u1, JSString* s2) |
|
250 : JSCell(globalData->stringStructure.get()) |
|
251 , m_length(u1.size() + s2->length()) |
|
252 , m_fiberCount(fiberCount) |
|
253 { |
|
254 ASSERT(fiberCount <= s_maxInternalRopeLength); |
|
255 unsigned index = 0; |
|
256 appendStringInConstruct(index, u1); |
|
257 appendStringInConstruct(index, s2); |
|
258 ASSERT(fiberCount == index); |
|
259 } |
|
260 // This constructor constructs a new string by concatenating v1, v2 & v3. |
|
261 // This should only be called with fiberCount <= 3 ... which since every |
|
262 // value must require a fiberCount of at least one implies that the length |
|
263 // for each value must be exactly 1! |
|
264 JSString(ExecState* exec, JSValue v1, JSValue v2, JSValue v3) |
|
265 : JSCell(exec->globalData().stringStructure.get()) |
|
266 , m_length(0) |
|
267 , m_fiberCount(s_maxInternalRopeLength) |
|
268 { |
|
269 unsigned index = 0; |
|
270 appendValueInConstructAndIncrementLength(exec, index, v1); |
|
271 appendValueInConstructAndIncrementLength(exec, index, v2); |
|
272 appendValueInConstructAndIncrementLength(exec, index, v3); |
|
273 ASSERT(index == s_maxInternalRopeLength); |
|
274 } |
|
275 |
|
276 // This constructor constructs a new string by concatenating u1 & u2. |
|
277 JSString(JSGlobalData* globalData, const UString& u1, const UString& u2) |
|
278 : JSCell(globalData->stringStructure.get()) |
|
279 , m_length(u1.size() + u2.size()) |
|
280 , m_fiberCount(2) |
|
281 { |
|
282 unsigned index = 0; |
|
283 appendStringInConstruct(index, u1); |
|
284 appendStringInConstruct(index, u2); |
|
285 ASSERT(index <= s_maxInternalRopeLength); |
|
286 } |
|
287 |
|
288 // This constructor constructs a new string by concatenating u1, u2 & u3. |
|
289 JSString(JSGlobalData* globalData, const UString& u1, const UString& u2, const UString& u3) |
|
290 : JSCell(globalData->stringStructure.get()) |
|
291 , m_length(u1.size() + u2.size() + u3.size()) |
|
292 , m_fiberCount(s_maxInternalRopeLength) |
|
293 { |
|
294 unsigned index = 0; |
|
295 appendStringInConstruct(index, u1); |
|
296 appendStringInConstruct(index, u2); |
|
297 appendStringInConstruct(index, u3); |
|
298 ASSERT(index <= s_maxInternalRopeLength); |
|
299 } |
|
300 |
|
301 JSString(JSGlobalData* globalData, const UString& value, JSStringFinalizerCallback finalizer, void* context) |
|
302 : JSCell(globalData->stringStructure.get()) |
|
303 , m_length(value.size()) |
|
304 , m_value(value) |
|
305 , m_fiberCount(0) |
|
306 { |
|
307 ASSERT(!m_value.isNull()); |
|
308 // nasty hack because we can't union non-POD types |
|
309 m_other.m_finalizerCallback = finalizer; |
|
310 m_other.m_finalizerContext = context; |
|
311 Heap::heap(this)->reportExtraMemoryCost(value.cost()); |
|
312 } |
|
313 |
|
314 ~JSString() |
|
315 { |
|
316 ASSERT(vptr() == JSGlobalData::jsStringVPtr); |
|
317 for (unsigned i = 0; i < m_fiberCount; ++i) |
|
318 RopeImpl::deref(m_other.m_fibers[i]); |
|
319 |
|
320 if (!m_fiberCount && m_other.m_finalizerCallback) |
|
321 m_other.m_finalizerCallback(this, m_other.m_finalizerContext); |
|
322 } |
|
323 |
|
324 const UString& value(ExecState* exec) const |
|
325 { |
|
326 if (isRope()) |
|
327 resolveRope(exec); |
|
328 return m_value; |
|
329 } |
|
330 const UString& tryGetValue() const |
|
331 { |
|
332 if (isRope()) |
|
333 resolveRope(0); |
|
334 return m_value; |
|
335 } |
|
336 unsigned length() { return m_length; } |
|
337 |
|
338 bool getStringPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); |
|
339 bool getStringPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); |
|
340 bool getStringPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor&); |
|
341 |
|
342 bool canGetIndex(unsigned i) { return i < m_length; } |
|
343 JSString* getIndex(ExecState*, unsigned); |
|
344 JSString* getIndexSlowCase(ExecState*, unsigned); |
|
345 |
|
346 JSValue replaceCharacter(ExecState*, UChar, const UString& replacement); |
|
347 |
|
348 static PassRefPtr<Structure> createStructure(JSValue proto) { return Structure::create(proto, TypeInfo(StringType, OverridesGetOwnPropertySlot | NeedsThisConversion), AnonymousSlotCount); } |
|
349 |
|
350 private: |
|
351 enum VPtrStealingHackType { VPtrStealingHack }; |
|
352 JSString(VPtrStealingHackType) |
|
353 : JSCell(0) |
|
354 , m_fiberCount(0) |
|
355 { |
|
356 } |
|
357 |
|
358 void resolveRope(ExecState*) const; |
|
359 |
|
360 void appendStringInConstruct(unsigned& index, const UString& string) |
|
361 { |
|
362 UStringImpl* impl = string.rep(); |
|
363 impl->ref(); |
|
364 m_other.m_fibers[index++] = impl; |
|
365 } |
|
366 |
|
367 void appendStringInConstruct(unsigned& index, JSString* jsString) |
|
368 { |
|
369 if (jsString->isRope()) { |
|
370 for (unsigned i = 0; i < jsString->m_fiberCount; ++i) { |
|
371 RopeImpl::Fiber fiber = jsString->m_other.m_fibers[i]; |
|
372 fiber->ref(); |
|
373 m_other.m_fibers[index++] = fiber; |
|
374 } |
|
375 } else |
|
376 appendStringInConstruct(index, jsString->string()); |
|
377 } |
|
378 |
|
379 void appendValueInConstructAndIncrementLength(ExecState* exec, unsigned& index, JSValue v) |
|
380 { |
|
381 if (v.isString()) { |
|
382 ASSERT(asCell(v)->isString()); |
|
383 JSString* s = static_cast<JSString*>(asCell(v)); |
|
384 ASSERT(s->size() == 1); |
|
385 appendStringInConstruct(index, s); |
|
386 m_length += s->length(); |
|
387 } else { |
|
388 UString u(v.toString(exec)); |
|
389 UStringImpl* impl = u.rep(); |
|
390 impl->ref(); |
|
391 m_other.m_fibers[index++] = impl; |
|
392 m_length += u.size(); |
|
393 } |
|
394 } |
|
395 |
|
396 virtual JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const; |
|
397 virtual bool getPrimitiveNumber(ExecState*, double& number, JSValue& value); |
|
398 virtual bool toBoolean(ExecState*) const; |
|
399 virtual double toNumber(ExecState*) const; |
|
400 virtual JSObject* toObject(ExecState*) const; |
|
401 virtual UString toString(ExecState*) const; |
|
402 |
|
403 virtual JSObject* toThisObject(ExecState*) const; |
|
404 |
|
405 // Actually getPropertySlot, not getOwnPropertySlot (see JSCell). |
|
406 virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); |
|
407 virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); |
|
408 virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); |
|
409 |
|
410 static const unsigned s_maxInternalRopeLength = 3; |
|
411 |
|
412 // A string is represented either by a UString or a RopeImpl. |
|
413 unsigned m_length; |
|
414 mutable UString m_value; |
|
415 mutable unsigned m_fiberCount; |
|
416 // This structure exists to support a temporary workaround for a GC issue. |
|
417 struct JSStringFinalizerStruct { |
|
418 JSStringFinalizerStruct() : m_finalizerCallback(0) {} |
|
419 union { |
|
420 mutable FixedArray<RopeImpl::Fiber, s_maxInternalRopeLength> m_fibers; |
|
421 struct { |
|
422 JSStringFinalizerCallback m_finalizerCallback; |
|
423 void* m_finalizerContext; |
|
424 }; |
|
425 }; |
|
426 } m_other; |
|
427 |
|
428 bool isRope() const { return m_fiberCount; } |
|
429 UString& string() { ASSERT(!isRope()); return m_value; } |
|
430 unsigned size() { return m_fiberCount ? m_fiberCount : 1; } |
|
431 |
|
432 friend JSValue jsString(ExecState* exec, JSString* s1, JSString* s2); |
|
433 friend JSValue jsString(ExecState* exec, const UString& u1, JSString* s2); |
|
434 friend JSValue jsString(ExecState* exec, JSString* s1, const UString& u2); |
|
435 friend JSValue jsString(ExecState* exec, Register* strings, unsigned count); |
|
436 friend JSValue jsString(ExecState* exec, JSValue thisValue); |
|
437 friend JSString* jsStringWithFinalizer(ExecState*, const UString&, JSStringFinalizerCallback callback, void* context); |
|
438 }; |
|
439 |
|
440 JSString* asString(JSValue); |
|
441 |
|
442 // When an object is created from a different DLL, MSVC changes vptr to a "local" one right after invoking a constructor, |
|
443 // see <http://groups.google.com/group/microsoft.public.vc.language/msg/55cdcefeaf770212>. |
|
444 // This breaks isJSString(), and we don't need that hack anyway, so we change vptr back to primary one. |
|
445 // The below function must be called by any inline function that invokes a JSString constructor. |
|
446 #if COMPILER(MSVC) && !defined(BUILDING_JavaScriptCore) |
|
447 inline JSString* fixupVPtr(JSGlobalData* globalData, JSString* string) { string->setVPtr(globalData->jsStringVPtr); return string; } |
|
448 #else |
|
449 inline JSString* fixupVPtr(JSGlobalData*, JSString* string) { return string; } |
|
450 #endif |
|
451 |
|
452 inline JSString* asString(JSValue value) |
|
453 { |
|
454 ASSERT(asCell(value)->isString()); |
|
455 return static_cast<JSString*>(asCell(value)); |
|
456 } |
|
457 |
|
458 inline JSString* jsEmptyString(JSGlobalData* globalData) |
|
459 { |
|
460 return globalData->smallStrings.emptyString(globalData); |
|
461 } |
|
462 |
|
463 inline JSString* jsSingleCharacterString(JSGlobalData* globalData, UChar c) |
|
464 { |
|
465 if (c <= 0xFF) |
|
466 return globalData->smallStrings.singleCharacterString(globalData, c); |
|
467 return fixupVPtr(globalData, new (globalData) JSString(globalData, UString(&c, 1))); |
|
468 } |
|
469 |
|
470 inline JSString* jsSingleCharacterSubstring(ExecState* exec, const UString& s, unsigned offset) |
|
471 { |
|
472 JSGlobalData* globalData = &exec->globalData(); |
|
473 ASSERT(offset < static_cast<unsigned>(s.size())); |
|
474 UChar c = s.data()[offset]; |
|
475 if (c <= 0xFF) |
|
476 return globalData->smallStrings.singleCharacterString(globalData, c); |
|
477 return fixupVPtr(globalData, new (globalData) JSString(globalData, UString(UStringImpl::create(s.rep(), offset, 1)))); |
|
478 } |
|
479 |
|
480 inline JSString* jsNontrivialString(JSGlobalData* globalData, const char* s) |
|
481 { |
|
482 ASSERT(s); |
|
483 ASSERT(s[0]); |
|
484 ASSERT(s[1]); |
|
485 return fixupVPtr(globalData, new (globalData) JSString(globalData, s)); |
|
486 } |
|
487 |
|
488 inline JSString* jsNontrivialString(JSGlobalData* globalData, const UString& s) |
|
489 { |
|
490 ASSERT(s.size() > 1); |
|
491 return fixupVPtr(globalData, new (globalData) JSString(globalData, s)); |
|
492 } |
|
493 |
|
494 inline JSString* JSString::getIndex(ExecState* exec, unsigned i) |
|
495 { |
|
496 ASSERT(canGetIndex(i)); |
|
497 if (isRope()) |
|
498 return getIndexSlowCase(exec, i); |
|
499 ASSERT(i < m_value.size()); |
|
500 return jsSingleCharacterSubstring(exec, m_value, i); |
|
501 } |
|
502 |
|
503 inline JSString* jsString(JSGlobalData* globalData, const UString& s) |
|
504 { |
|
505 int size = s.size(); |
|
506 if (!size) |
|
507 return globalData->smallStrings.emptyString(globalData); |
|
508 if (size == 1) { |
|
509 UChar c = s.data()[0]; |
|
510 if (c <= 0xFF) |
|
511 return globalData->smallStrings.singleCharacterString(globalData, c); |
|
512 } |
|
513 return fixupVPtr(globalData, new (globalData) JSString(globalData, s)); |
|
514 } |
|
515 |
|
516 inline JSString* jsStringWithFinalizer(ExecState* exec, const UString& s, JSStringFinalizerCallback callback, void* context) |
|
517 { |
|
518 ASSERT(s.size() && (s.size() > 1 || s.data()[0] > 0xFF)); |
|
519 JSGlobalData* globalData = &exec->globalData(); |
|
520 return fixupVPtr(globalData, new (globalData) JSString(globalData, s, callback, context)); |
|
521 } |
|
522 |
|
523 inline JSString* jsSubstring(JSGlobalData* globalData, const UString& s, unsigned offset, unsigned length) |
|
524 { |
|
525 ASSERT(offset <= static_cast<unsigned>(s.size())); |
|
526 ASSERT(length <= static_cast<unsigned>(s.size())); |
|
527 ASSERT(offset + length <= static_cast<unsigned>(s.size())); |
|
528 if (!length) |
|
529 return globalData->smallStrings.emptyString(globalData); |
|
530 if (length == 1) { |
|
531 UChar c = s.data()[offset]; |
|
532 if (c <= 0xFF) |
|
533 return globalData->smallStrings.singleCharacterString(globalData, c); |
|
534 } |
|
535 return fixupVPtr(globalData, new (globalData) JSString(globalData, UString(UStringImpl::create(s.rep(), offset, length)), JSString::HasOtherOwner)); |
|
536 } |
|
537 |
|
538 inline JSString* jsOwnedString(JSGlobalData* globalData, const UString& s) |
|
539 { |
|
540 int size = s.size(); |
|
541 if (!size) |
|
542 return globalData->smallStrings.emptyString(globalData); |
|
543 if (size == 1) { |
|
544 UChar c = s.data()[0]; |
|
545 if (c <= 0xFF) |
|
546 return globalData->smallStrings.singleCharacterString(globalData, c); |
|
547 } |
|
548 return fixupVPtr(globalData, new (globalData) JSString(globalData, s, JSString::HasOtherOwner)); |
|
549 } |
|
550 |
|
551 inline JSString* jsEmptyString(ExecState* exec) { return jsEmptyString(&exec->globalData()); } |
|
552 inline JSString* jsString(ExecState* exec, const UString& s) { return jsString(&exec->globalData(), s); } |
|
553 inline JSString* jsSingleCharacterString(ExecState* exec, UChar c) { return jsSingleCharacterString(&exec->globalData(), c); } |
|
554 inline JSString* jsSubstring(ExecState* exec, const UString& s, unsigned offset, unsigned length) { return jsSubstring(&exec->globalData(), s, offset, length); } |
|
555 inline JSString* jsNontrivialString(ExecState* exec, const UString& s) { return jsNontrivialString(&exec->globalData(), s); } |
|
556 inline JSString* jsNontrivialString(ExecState* exec, const char* s) { return jsNontrivialString(&exec->globalData(), s); } |
|
557 inline JSString* jsOwnedString(ExecState* exec, const UString& s) { return jsOwnedString(&exec->globalData(), s); } |
|
558 |
|
559 ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) |
|
560 { |
|
561 if (propertyName == exec->propertyNames().length) { |
|
562 slot.setValue(jsNumber(exec, m_length)); |
|
563 return true; |
|
564 } |
|
565 |
|
566 bool isStrictUInt32; |
|
567 unsigned i = propertyName.toStrictUInt32(&isStrictUInt32); |
|
568 if (isStrictUInt32 && i < m_length) { |
|
569 slot.setValue(getIndex(exec, i)); |
|
570 return true; |
|
571 } |
|
572 |
|
573 return false; |
|
574 } |
|
575 |
|
576 ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) |
|
577 { |
|
578 if (propertyName < m_length) { |
|
579 slot.setValue(getIndex(exec, propertyName)); |
|
580 return true; |
|
581 } |
|
582 |
|
583 return false; |
|
584 } |
|
585 |
|
586 inline bool isJSString(JSGlobalData* globalData, JSValue v) { return v.isCell() && v.asCell()->vptr() == globalData->jsStringVPtr; } |
|
587 |
|
588 // --- JSValue inlines ---------------------------- |
|
589 |
|
590 inline UString JSValue::toString(ExecState* exec) const |
|
591 { |
|
592 if (isString()) |
|
593 return static_cast<JSString*>(asCell())->value(exec); |
|
594 if (isInt32()) |
|
595 return exec->globalData().numericStrings.add(asInt32()); |
|
596 if (isDouble()) |
|
597 return exec->globalData().numericStrings.add(asDouble()); |
|
598 if (isTrue()) |
|
599 return "true"; |
|
600 if (isFalse()) |
|
601 return "false"; |
|
602 if (isNull()) |
|
603 return "null"; |
|
604 if (isUndefined()) |
|
605 return "undefined"; |
|
606 ASSERT(isCell()); |
|
607 return asCell()->toString(exec); |
|
608 } |
|
609 |
|
610 inline UString JSValue::toPrimitiveString(ExecState* exec) const |
|
611 { |
|
612 if (isString()) |
|
613 return static_cast<JSString*>(asCell())->value(exec); |
|
614 if (isInt32()) |
|
615 return exec->globalData().numericStrings.add(asInt32()); |
|
616 if (isDouble()) |
|
617 return exec->globalData().numericStrings.add(asDouble()); |
|
618 if (isTrue()) |
|
619 return "true"; |
|
620 if (isFalse()) |
|
621 return "false"; |
|
622 if (isNull()) |
|
623 return "null"; |
|
624 if (isUndefined()) |
|
625 return "undefined"; |
|
626 ASSERT(isCell()); |
|
627 return asCell()->toPrimitive(exec, NoPreference).toString(exec); |
|
628 } |
|
629 |
|
630 } // namespace JSC |
|
631 |
|
632 #endif // JSString_h |