1 /* 2 * Copyright (C) 1999-2001 Harri Porten (porten (at) kde.org) 3 * Copyright (C) 2001 Peter Kelly (pmk (at) 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(sizeof(Rope) + (ropeLength - 1) * sizeof(Fiber)).getValue(allocation)) 128 return adoptRef(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, jsString->m_fibers[i]); 151 } else 152 append(index, 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(this)->reportExtraMemoryCost(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, s1); 209 appendStringInConstruct(index, 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, s1); 222 appendStringInConstruct(index, 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, u1); 235 appendStringInConstruct(index, 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, v1); 249 appendValueInConstructAndIncrementLength(exec, index, v2); 250 appendValueInConstructAndIncrementLength(exec, index, 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(this)->reportExtraMemoryCost(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 = reinterpret_cast<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 this is a rope, m_value should be null - 287 // if this is not a rope, m_value should be non-null. 288 ASSERT(isRope() == m_value.isNull()); 289 return m_value; 290 } 291 unsigned length() { return m_stringLength; } 292 293 bool getStringPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); 294 bool getStringPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); 295 bool getStringPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor&); 296 297 bool canGetIndex(unsigned i) { return i < m_stringLength; } 298 JSString* getIndex(ExecState*, unsigned); 299 300 static PassRefPtr<Structure> createStructure(JSValue proto) { return Structure::create(proto, TypeInfo(StringType, OverridesGetOwnPropertySlot | NeedsThisConversion), AnonymousSlotCount); } 301 302 private: 303 enum VPtrStealingHackType { VPtrStealingHack }; 304 JSString(VPtrStealingHackType) 305 : JSCell(0) 306 , m_ropeLength(0) 307 { 308 } 309 310 void resolveRope(ExecState*) const; 311 312 void appendStringInConstruct(unsigned& index, const UString& string) 313 { 314 m_fibers[index++] = Rope::Fiber(string.rep()->ref()); 315 } 316 317 void appendStringInConstruct(unsigned& index, JSString* jsString) 318 { 319 if (jsString->isRope()) { 320 for (unsigned i = 0; i < jsString->m_ropeLength; ++i) 321 m_fibers[index++] = jsString->m_fibers[i].ref(); 322 } else 323 appendStringInConstruct(index, jsString->string()); 324 } 325 326 void appendValueInConstructAndIncrementLength(ExecState* exec, unsigned& index, JSValue v) 327 { 328 if (v.isString()) { 329 ASSERT(asCell(v)->isString()); 330 JSString* s = static_cast<JSString*>(asCell(v)); 331 ASSERT(s->ropeLength() == 1); 332 appendStringInConstruct(index, s); 333 m_stringLength += s->length(); 334 } else { 335 UString u(v.toString(exec)); 336 m_fibers[index++] = Rope::Fiber(u.rep()->ref()); 337 m_stringLength += u.size(); 338 } 339 } 340 341 virtual JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const; 342 virtual bool getPrimitiveNumber(ExecState*, double& number, JSValue& value); 343 virtual bool toBoolean(ExecState*) const; 344 virtual double toNumber(ExecState*) const; 345 virtual JSObject* toObject(ExecState*) const; 346 virtual UString toString(ExecState*) const; 347 348 virtual JSObject* toThisObject(ExecState*) const; 349 virtual UString toThisString(ExecState*) const; 350 virtual JSString* toThisJSString(ExecState*); 351 352 // Actually getPropertySlot, not getOwnPropertySlot (see JSCell). 353 virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); 354 virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); 355 virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); 356 357 static const unsigned s_maxInternalRopeLength = 3; 358 359 // A string is represented either by a UString or a Rope. 360 unsigned m_stringLength; 361 mutable UString m_value; 362 mutable unsigned m_ropeLength; 363 mutable Rope::Fiber m_fibers[s_maxInternalRopeLength]; 364 365 bool isRope() const { return m_ropeLength; } 366 UString& string() { ASSERT(!isRope()); return m_value; } 367 unsigned ropeLength() { return m_ropeLength ? m_ropeLength : 1; } 368 369 friend JSValue jsString(ExecState* exec, JSString* s1, JSString* s2); 370 friend JSValue jsString(ExecState* exec, const UString& u1, JSString* s2); 371 friend JSValue jsString(ExecState* exec, JSString* s1, const UString& u2); 372 friend JSValue jsString(ExecState* exec, Register* strings, unsigned count); 373 friend JSValue jsString(ExecState* exec, JSValue thisValue, const ArgList& args); 374 friend JSString* jsStringWithFinalizer(ExecState*, const UString&, JSStringFinalizerCallback callback, void* context); 375 }; 376 377 JSString* asString(JSValue); 378 379 // When an object is created from a different DLL, MSVC changes vptr to a "local" one right after invoking a constructor, 380 // see <http://groups.google.com/group/microsoft.public.vc.language/msg/55cdcefeaf770212>. 381 // This breaks isJSString(), and we don't need that hack anyway, so we change vptr back to primary one. 382 // The below function must be called by any inline function that invokes a JSString constructor. 383 #if COMPILER(MSVC) && !defined(BUILDING_JavaScriptCore) 384 inline JSString* fixupVPtr(JSGlobalData* globalData, JSString* string) { string->setVPtr(globalData->jsStringVPtr); return string; } 385 #else 386 inline JSString* fixupVPtr(JSGlobalData*, JSString* string) { return string; } 387 #endif 388 389 inline JSString* asString(JSValue value) 390 { 391 ASSERT(asCell(value)->isString()); 392 return static_cast<JSString*>(asCell(value)); 393 } 394 395 inline JSString* jsEmptyString(JSGlobalData* globalData) 396 { 397 return globalData->smallStrings.emptyString(globalData); 398 } 399 400 inline JSString* jsSingleCharacterString(JSGlobalData* globalData, UChar c) 401 { 402 if (c <= 0xFF) 403 return globalData->smallStrings.singleCharacterString(globalData, c); 404 return fixupVPtr(globalData, new (globalData) JSString(globalData, UString(&c, 1))); 405 } 406 407 inline JSString* jsSingleCharacterSubstring(JSGlobalData* globalData, const UString& s, unsigned offset) 408 { 409 ASSERT(offset < static_cast<unsigned>(s.size())); 410 UChar c = s.data()[offset]; 411 if (c <= 0xFF) 412 return globalData->smallStrings.singleCharacterString(globalData, c); 413 return fixupVPtr(globalData, new (globalData) JSString(globalData, UString(UString::Rep::create(s.rep(), offset, 1)))); 414 } 415 416 inline JSString* jsNontrivialString(JSGlobalData* globalData, const char* s) 417 { 418 ASSERT(s); 419 ASSERT(s[0]); 420 ASSERT(s[1]); 421 return fixupVPtr(globalData, new (globalData) JSString(globalData, s)); 422 } 423 424 inline JSString* jsNontrivialString(JSGlobalData* globalData, const UString& s) 425 { 426 ASSERT(s.size() > 1); 427 return fixupVPtr(globalData, new (globalData) JSString(globalData, s)); 428 } 429 430 inline JSString* JSString::getIndex(ExecState* exec, unsigned i) 431 { 432 ASSERT(canGetIndex(i)); 433 return jsSingleCharacterSubstring(&exec->globalData(), value(exec), i); 434 } 435 436 inline JSString* jsString(JSGlobalData* globalData, const UString& s) 437 { 438 int size = s.size(); 439 if (!size) 440 return globalData->smallStrings.emptyString(globalData); 441 if (size == 1) { 442 UChar c = s.data()[0]; 443 if (c <= 0xFF) 444 return globalData->smallStrings.singleCharacterString(globalData, c); 445 } 446 return fixupVPtr(globalData, new (globalData) JSString(globalData, s)); 447 } 448 449 inline JSString* jsStringWithFinalizer(ExecState* exec, const UString& s, JSStringFinalizerCallback callback, void* context) 450 { 451 ASSERT(s.size() && (s.size() > 1 || s.data()[0] > 0xFF)); 452 JSGlobalData* globalData = &exec->globalData(); 453 return fixupVPtr(globalData, new (globalData) JSString(globalData, s, callback, context)); 454 } 455 456 inline JSString* jsSubstring(JSGlobalData* globalData, const UString& s, unsigned offset, unsigned length) 457 { 458 ASSERT(offset <= static_cast<unsigned>(s.size())); 459 ASSERT(length <= static_cast<unsigned>(s.size())); 460 ASSERT(offset + length <= static_cast<unsigned>(s.size())); 461 if (!length) 462 return globalData->smallStrings.emptyString(globalData); 463 if (length == 1) { 464 UChar c = s.data()[offset]; 465 if (c <= 0xFF) 466 return globalData->smallStrings.singleCharacterString(globalData, c); 467 } 468 return fixupVPtr(globalData, new (globalData) JSString(globalData, UString(UString::Rep::create(s.rep(), offset, length)), JSString::HasOtherOwner)); 469 } 470 471 inline JSString* jsOwnedString(JSGlobalData* globalData, const UString& s) 472 { 473 int size = s.size(); 474 if (!size) 475 return globalData->smallStrings.emptyString(globalData); 476 if (size == 1) { 477 UChar c = s.data()[0]; 478 if (c <= 0xFF) 479 return globalData->smallStrings.singleCharacterString(globalData, c); 480 } 481 return fixupVPtr(globalData, new (globalData) JSString(globalData, s, JSString::HasOtherOwner)); 482 } 483 484 inline JSString* jsEmptyString(ExecState* exec) { return jsEmptyString(&exec->globalData()); } 485 inline JSString* jsString(ExecState* exec, const UString& s) { return jsString(&exec->globalData(), s); } 486 inline JSString* jsSingleCharacterString(ExecState* exec, UChar c) { return jsSingleCharacterString(&exec->globalData(), c); } 487 inline JSString* jsSingleCharacterSubstring(ExecState* exec, const UString& s, unsigned offset) { return jsSingleCharacterSubstring(&exec->globalData(), s, offset); } 488 inline JSString* jsSubstring(ExecState* exec, const UString& s, unsigned offset, unsigned length) { return jsSubstring(&exec->globalData(), s, offset, length); } 489 inline JSString* jsNontrivialString(ExecState* exec, const UString& s) { return jsNontrivialString(&exec->globalData(), s); } 490 inline JSString* jsNontrivialString(ExecState* exec, const char* s) { return jsNontrivialString(&exec->globalData(), s); } 491 inline JSString* jsOwnedString(ExecState* exec, const UString& s) { return jsOwnedString(&exec->globalData(), s); } 492 493 ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) 494 { 495 if (propertyName == exec->propertyNames().length) { 496 slot.setValue(jsNumber(exec, m_stringLength)); 497 return true; 498 } 499 500 bool isStrictUInt32; 501 unsigned i = propertyName.toStrictUInt32(&isStrictUInt32); 502 if (isStrictUInt32 && i < m_stringLength) { 503 slot.setValue(jsSingleCharacterSubstring(exec, value(exec), i)); 504 return true; 505 } 506 507 return false; 508 } 509 510 ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) 511 { 512 if (propertyName < m_stringLength) { 513 slot.setValue(jsSingleCharacterSubstring(exec, value(exec), propertyName)); 514 return true; 515 } 516 517 return false; 518 } 519 520 inline bool isJSString(JSGlobalData* globalData, JSValue v) { return v.isCell() && v.asCell()->vptr() == globalData->jsStringVPtr; } 521 522 // --- JSValue inlines ---------------------------- 523 524 inline JSString* JSValue::toThisJSString(ExecState* exec) 525 { 526 return isCell() ? asCell()->toThisJSString(exec) : jsString(exec, toString(exec)); 527 } 528 529 inline UString JSValue::toString(ExecState* exec) const 530 { 531 if (isString()) 532 return static_cast<JSString*>(asCell())->value(exec); 533 if (isInt32()) 534 return exec->globalData().numericStrings.add(asInt32()); 535 if (isDouble()) 536 return exec->globalData().numericStrings.add(asDouble()); 537 if (isTrue()) 538 return "true"; 539 if (isFalse()) 540 return "false"; 541 if (isNull()) 542 return "null"; 543 if (isUndefined()) 544 return "undefined"; 545 ASSERT(isCell()); 546 return asCell()->toString(exec); 547 } 548 549 inline UString JSValue::toPrimitiveString(ExecState* exec) const 550 { 551 if (isString()) 552 return static_cast<JSString*>(asCell())->value(exec); 553 if (isInt32()) 554 return exec->globalData().numericStrings.add(asInt32()); 555 if (isDouble()) 556 return exec->globalData().numericStrings.add(asDouble()); 557 if (isTrue()) 558 return "true"; 559 if (isFalse()) 560 return "false"; 561 if (isNull()) 562 return "null"; 563 if (isUndefined()) 564 return "undefined"; 565 ASSERT(isCell()); 566 return asCell()->toPrimitive(exec, NoPreference).toString(exec); 567 } 568 569 } // namespace JSC 570 571 #endif // JSString_h 572