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 | |
33 | namespace 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 | |