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, 2014 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 "PropertyDescriptor.h" 30#include "PropertySlot.h" 31#include "Structure.h" 32#include <array> 33 34namespace JSC { 35 36 class JSString; 37 class JSRopeString; 38 class LLIntOffsetsExtractor; 39 40 JSString* jsEmptyString(VM*); 41 JSString* jsEmptyString(ExecState*); 42 JSString* jsString(VM*, const String&); // returns empty string if passed null string 43 JSString* jsString(ExecState*, const String&); // returns empty string if passed null string 44 45 JSString* jsSingleCharacterString(VM*, UChar); 46 JSString* jsSingleCharacterString(ExecState*, UChar); 47 JSString* jsSingleCharacterSubstring(ExecState*, const String&, unsigned offset); 48 JSString* jsSubstring(VM*, const String&, unsigned offset, unsigned length); 49 JSString* jsSubstring(ExecState*, const String&, unsigned offset, unsigned length); 50 51 // Non-trivial strings are two or more characters long. 52 // These functions are faster than just calling jsString. 53 JSString* jsNontrivialString(VM*, const String&); 54 JSString* jsNontrivialString(ExecState*, const String&); 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 String 59 JSString* jsOwnedString(VM*, const String&); 60 JSString* jsOwnedString(ExecState*, const String&); 61 62 JSRopeString* jsStringBuilder(VM*); 63 64 class JSString : public JSCell { 65 public: 66 friend class JIT; 67 friend class VM; 68 friend class SpecializedThunkJIT; 69 friend class JSRopeString; 70 friend class MarkStack; 71 friend class SlotVisitor; 72 friend struct ThunkHelpers; 73 74 typedef JSCell Base; 75 76 static const bool needsDestruction = true; 77 static const bool hasImmortalStructure = true; 78 static void destroy(JSCell*); 79 80 private: 81 JSString(VM& vm, PassRefPtr<StringImpl> value) 82 : JSCell(vm, vm.stringStructure.get()) 83 , m_flags(0) 84 , m_value(value) 85 { 86 } 87 88 JSString(VM& vm) 89 : JSCell(vm, vm.stringStructure.get()) 90 , m_flags(0) 91 { 92 } 93 94 void finishCreation(VM& vm, size_t length) 95 { 96 ASSERT(!m_value.isNull()); 97 Base::finishCreation(vm); 98 m_length = length; 99 setIs8Bit(m_value.impl()->is8Bit()); 100 vm.m_newStringsSinceLastHashCons++; 101 } 102 103 void finishCreation(VM& vm, size_t length, size_t cost) 104 { 105 ASSERT(!m_value.isNull()); 106 Base::finishCreation(vm); 107 m_length = length; 108 setIs8Bit(m_value.impl()->is8Bit()); 109 Heap::heap(this)->reportExtraMemoryCost(cost); 110 vm.m_newStringsSinceLastHashCons++; 111 } 112 113 protected: 114 void finishCreation(VM& vm) 115 { 116 Base::finishCreation(vm); 117 m_length = 0; 118 setIs8Bit(true); 119 vm.m_newStringsSinceLastHashCons++; 120 } 121 122 public: 123 static JSString* create(VM& vm, PassRefPtr<StringImpl> value) 124 { 125 ASSERT(value); 126 int32_t length = value->length(); 127 RELEASE_ASSERT(length >= 0); 128 size_t cost = value->cost(); 129 JSString* newString = new (NotNull, allocateCell<JSString>(vm.heap)) JSString(vm, value); 130 newString->finishCreation(vm, length, cost); 131 return newString; 132 } 133 static JSString* createHasOtherOwner(VM& vm, PassRefPtr<StringImpl> value) 134 { 135 ASSERT(value); 136 size_t length = value->length(); 137 JSString* newString = new (NotNull, allocateCell<JSString>(vm.heap)) JSString(vm, value); 138 newString->finishCreation(vm, length); 139 return newString; 140 } 141 142 Identifier toIdentifier(ExecState*) const; 143 AtomicString toAtomicString(ExecState*) const; 144 AtomicStringImpl* toExistingAtomicString(ExecState*) const; 145 const String& value(ExecState*) const; 146 const String& tryGetValue() const; 147 const StringImpl* tryGetValueImpl() const; 148 unsigned length() const { return m_length; } 149 150 JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const; 151 JS_EXPORT_PRIVATE bool toBoolean() const; 152 bool getPrimitiveNumber(ExecState*, double& number, JSValue&) const; 153 JSObject* toObject(ExecState*, JSGlobalObject*) const; 154 double toNumber(ExecState*) const; 155 156 bool getStringPropertySlot(ExecState*, PropertyName, PropertySlot&); 157 bool getStringPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); 158 bool getStringPropertyDescriptor(ExecState*, PropertyName, PropertyDescriptor&); 159 160 bool canGetIndex(unsigned i) { return i < m_length; } 161 JSString* getIndex(ExecState*, unsigned); 162 163 static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto) 164 { 165 return Structure::create(vm, globalObject, proto, TypeInfo(StringType, StructureFlags), info()); 166 } 167 168 static size_t offsetOfLength() { return OBJECT_OFFSETOF(JSString, m_length); } 169 static size_t offsetOfFlags() { return OBJECT_OFFSETOF(JSString, m_flags); } 170 static size_t offsetOfValue() { return OBJECT_OFFSETOF(JSString, m_value); } 171 172 DECLARE_EXPORT_INFO; 173 174 static void dumpToStream(const JSCell*, PrintStream&); 175 static void visitChildren(JSCell*, SlotVisitor&); 176 177 enum { 178 HashConsLock = 1u << 2, 179 IsHashConsSingleton = 1u << 1, 180 Is8Bit = 1u 181 }; 182 183 protected: 184 static const unsigned StructureFlags = OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | StructureIsImmortal; 185 186 friend class JSValue; 187 188 bool isRope() const { return m_value.isNull(); } 189 bool is8Bit() const { return m_flags & Is8Bit; } 190 void setIs8Bit(bool flag) const 191 { 192 if (flag) 193 m_flags |= Is8Bit; 194 else 195 m_flags &= ~Is8Bit; 196 } 197 bool shouldTryHashCons(); 198 bool isHashConsSingleton() const { return m_flags & IsHashConsSingleton; } 199 void clearHashConsSingleton() { m_flags &= ~IsHashConsSingleton; } 200 void setHashConsSingleton() { m_flags |= IsHashConsSingleton; } 201 bool tryHashConsLock(); 202 void releaseHashConsLock(); 203 204 mutable unsigned m_flags; 205 206 // A string is represented either by a String or a rope of fibers. 207 unsigned m_length; 208 mutable String m_value; 209 210 private: 211 friend class LLIntOffsetsExtractor; 212 213 static JSValue toThis(JSCell*, ExecState*, ECMAMode); 214 215 String& string() { ASSERT(!isRope()); return m_value; } 216 217 friend JSValue jsString(ExecState*, JSString*, JSString*); 218 friend JSString* jsSubstring(ExecState*, JSString*, unsigned offset, unsigned length); 219 }; 220 221 class JSRopeString : public JSString { 222 friend class JSString; 223 224 friend JSRopeString* jsStringBuilder(VM*); 225 226 class RopeBuilder { 227 public: 228 RopeBuilder(VM& vm) 229 : m_vm(vm) 230 , m_jsString(jsStringBuilder(&vm)) 231 , m_index(0) 232 { 233 } 234 235 bool append(JSString* jsString) 236 { 237 if (m_index == JSRopeString::s_maxInternalRopeLength) 238 expand(); 239 if (static_cast<int32_t>(m_jsString->length() + jsString->length()) < 0) { 240 m_jsString = nullptr; 241 return false; 242 } 243 m_jsString->append(m_vm, m_index++, jsString); 244 return true; 245 } 246 247 JSRopeString* release() 248 { 249 RELEASE_ASSERT(m_jsString); 250 JSRopeString* tmp = m_jsString; 251 m_jsString = 0; 252 return tmp; 253 } 254 255 unsigned length() const { return m_jsString->m_length; } 256 257 private: 258 void expand(); 259 260 VM& m_vm; 261 JSRopeString* m_jsString; 262 size_t m_index; 263 }; 264 265 private: 266 JSRopeString(VM& vm) 267 : JSString(vm) 268 { 269 } 270 271 void finishCreation(VM& vm, JSString* s1, JSString* s2) 272 { 273 Base::finishCreation(vm); 274 m_length = s1->length() + s2->length(); 275 setIs8Bit(s1->is8Bit() && s2->is8Bit()); 276 m_fibers[0].set(vm, this, s1); 277 m_fibers[1].set(vm, this, s2); 278 } 279 280 void finishCreation(VM& vm, JSString* s1, JSString* s2, JSString* s3) 281 { 282 Base::finishCreation(vm); 283 m_length = s1->length() + s2->length() + s3->length(); 284 setIs8Bit(s1->is8Bit() && s2->is8Bit() && s3->is8Bit()); 285 m_fibers[0].set(vm, this, s1); 286 m_fibers[1].set(vm, this, s2); 287 m_fibers[2].set(vm, this, s3); 288 } 289 290 void finishCreation(VM& vm) 291 { 292 JSString::finishCreation(vm); 293 } 294 295 void append(VM& vm, size_t index, JSString* jsString) 296 { 297 m_fibers[index].set(vm, this, jsString); 298 m_length += jsString->m_length; 299 RELEASE_ASSERT(static_cast<int32_t>(m_length) >= 0); 300 setIs8Bit(is8Bit() && jsString->is8Bit()); 301 } 302 303 static JSRopeString* createNull(VM& vm) 304 { 305 JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm); 306 newString->finishCreation(vm); 307 return newString; 308 } 309 310 public: 311 static JSString* create(VM& vm, JSString* s1, JSString* s2) 312 { 313 JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm); 314 newString->finishCreation(vm, s1, s2); 315 return newString; 316 } 317 static JSString* create(VM& vm, JSString* s1, JSString* s2, JSString* s3) 318 { 319 JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm); 320 newString->finishCreation(vm, s1, s2, s3); 321 return newString; 322 } 323 324 void visitFibers(SlotVisitor&); 325 326 static ptrdiff_t offsetOfFibers() { return OBJECT_OFFSETOF(JSRopeString, m_fibers); } 327 328 static const unsigned s_maxInternalRopeLength = 3; 329 330 private: 331 friend JSValue jsStringFromRegisterArray(ExecState*, Register*, unsigned); 332 friend JSValue jsStringFromArguments(ExecState*, JSValue); 333 334 JS_EXPORT_PRIVATE void resolveRope(ExecState*) const; 335 JS_EXPORT_PRIVATE void resolveRopeToAtomicString(ExecState*) const; 336 JS_EXPORT_PRIVATE AtomicStringImpl* resolveRopeToExistingAtomicString(ExecState*) const; 337 void resolveRopeSlowCase8(LChar*) const; 338 void resolveRopeSlowCase(UChar*) const; 339 void outOfMemory(ExecState*) const; 340 void resolveRopeInternal8(LChar*) const; 341 void resolveRopeInternal16(UChar*) const; 342 void clearFibers() const; 343 344 JS_EXPORT_PRIVATE JSString* getIndexSlowCase(ExecState*, unsigned); 345 346 mutable std::array<WriteBarrier<JSString>, s_maxInternalRopeLength> m_fibers; 347 }; 348 349 350 inline const StringImpl* JSString::tryGetValueImpl() const 351 { 352 return m_value.impl(); 353 } 354 355 JSString* asString(JSValue); 356 357 inline JSString* asString(JSValue value) 358 { 359 ASSERT(value.asCell()->isString()); 360 return jsCast<JSString*>(value.asCell()); 361 } 362 363 inline JSString* jsEmptyString(VM* vm) 364 { 365 return vm->smallStrings.emptyString(); 366 } 367 368 ALWAYS_INLINE JSString* jsSingleCharacterString(VM* vm, UChar c) 369 { 370 if (c <= maxSingleCharacterString) 371 return vm->smallStrings.singleCharacterString(c); 372 return JSString::create(*vm, String(&c, 1).impl()); 373 } 374 375 ALWAYS_INLINE JSString* jsSingleCharacterSubstring(ExecState* exec, const String& s, unsigned offset) 376 { 377 VM* vm = &exec->vm(); 378 ASSERT(offset < static_cast<unsigned>(s.length())); 379 UChar c = s.characterAt(offset); 380 if (c <= maxSingleCharacterString) 381 return vm->smallStrings.singleCharacterString(c); 382 return JSString::create(*vm, StringImpl::createSubstringSharingImpl(s.impl(), offset, 1)); 383 } 384 385 inline JSString* jsNontrivialString(VM* vm, const String& s) 386 { 387 ASSERT(s.length() > 1); 388 return JSString::create(*vm, s.impl()); 389 } 390 391 ALWAYS_INLINE Identifier JSString::toIdentifier(ExecState* exec) const 392 { 393 return Identifier(exec, toAtomicString(exec)); 394 } 395 396 ALWAYS_INLINE AtomicString JSString::toAtomicString(ExecState* exec) const 397 { 398 if (isRope()) 399 static_cast<const JSRopeString*>(this)->resolveRopeToAtomicString(exec); 400 return AtomicString(m_value); 401 } 402 403 ALWAYS_INLINE AtomicStringImpl* JSString::toExistingAtomicString(ExecState* exec) const 404 { 405 if (isRope()) 406 return static_cast<const JSRopeString*>(this)->resolveRopeToExistingAtomicString(exec); 407 if (m_value.impl()->isAtomic()) 408 return static_cast<AtomicStringImpl*>(m_value.impl()); 409 if (AtomicStringImpl* existingAtomicString = AtomicString::find(m_value.impl())) { 410 m_value = *existingAtomicString; 411 setIs8Bit(m_value.impl()->is8Bit()); 412 return existingAtomicString; 413 } 414 return nullptr; 415 } 416 417 inline const String& JSString::value(ExecState* exec) const 418 { 419 if (isRope()) 420 static_cast<const JSRopeString*>(this)->resolveRope(exec); 421 return m_value; 422 } 423 424 inline const String& JSString::tryGetValue() const 425 { 426 if (isRope()) 427 static_cast<const JSRopeString*>(this)->resolveRope(0); 428 return m_value; 429 } 430 431 inline JSString* JSString::getIndex(ExecState* exec, unsigned i) 432 { 433 ASSERT(canGetIndex(i)); 434 if (isRope()) 435 return static_cast<JSRopeString*>(this)->getIndexSlowCase(exec, i); 436 ASSERT(i < m_value.length()); 437 return jsSingleCharacterSubstring(exec, m_value, i); 438 } 439 440 inline JSString* jsString(VM* vm, const String& s) 441 { 442 int size = s.length(); 443 if (!size) 444 return vm->smallStrings.emptyString(); 445 if (size == 1) { 446 UChar c = s.characterAt(0); 447 if (c <= maxSingleCharacterString) 448 return vm->smallStrings.singleCharacterString(c); 449 } 450 return JSString::create(*vm, s.impl()); 451 } 452 453 inline JSString* jsSubstring(ExecState* exec, JSString* s, unsigned offset, unsigned length) 454 { 455 ASSERT(offset <= static_cast<unsigned>(s->length())); 456 ASSERT(length <= static_cast<unsigned>(s->length())); 457 ASSERT(offset + length <= static_cast<unsigned>(s->length())); 458 VM* vm = &exec->vm(); 459 if (!length) 460 return vm->smallStrings.emptyString(); 461 return jsSubstring(vm, s->value(exec), offset, length); 462 } 463 464 inline JSString* jsSubstring8(VM* vm, const String& s, unsigned offset, unsigned length) 465 { 466 ASSERT(offset <= static_cast<unsigned>(s.length())); 467 ASSERT(length <= static_cast<unsigned>(s.length())); 468 ASSERT(offset + length <= static_cast<unsigned>(s.length())); 469 if (!length) 470 return vm->smallStrings.emptyString(); 471 if (length == 1) { 472 UChar c = s.characterAt(offset); 473 if (c <= maxSingleCharacterString) 474 return vm->smallStrings.singleCharacterString(c); 475 } 476 return JSString::createHasOtherOwner(*vm, StringImpl::createSubstringSharingImpl8(s.impl(), offset, length)); 477 } 478 479 inline JSString* jsSubstring(VM* vm, const String& s, unsigned offset, unsigned length) 480 { 481 ASSERT(offset <= static_cast<unsigned>(s.length())); 482 ASSERT(length <= static_cast<unsigned>(s.length())); 483 ASSERT(offset + length <= static_cast<unsigned>(s.length())); 484 if (!length) 485 return vm->smallStrings.emptyString(); 486 if (length == 1) { 487 UChar c = s.characterAt(offset); 488 if (c <= maxSingleCharacterString) 489 return vm->smallStrings.singleCharacterString(c); 490 } 491 return JSString::createHasOtherOwner(*vm, StringImpl::createSubstringSharingImpl(s.impl(), offset, length)); 492 } 493 494 inline JSString* jsOwnedString(VM* vm, const String& s) 495 { 496 int size = s.length(); 497 if (!size) 498 return vm->smallStrings.emptyString(); 499 if (size == 1) { 500 UChar c = s.characterAt(0); 501 if (c <= maxSingleCharacterString) 502 return vm->smallStrings.singleCharacterString(c); 503 } 504 return JSString::createHasOtherOwner(*vm, s.impl()); 505 } 506 507 inline JSRopeString* jsStringBuilder(VM* vm) 508 { 509 return JSRopeString::createNull(*vm); 510 } 511 512 inline JSString* jsEmptyString(ExecState* exec) { return jsEmptyString(&exec->vm()); } 513 inline JSString* jsString(ExecState* exec, const String& s) { return jsString(&exec->vm(), s); } 514 inline JSString* jsSingleCharacterString(ExecState* exec, UChar c) { return jsSingleCharacterString(&exec->vm(), c); } 515 inline JSString* jsSubstring8(ExecState* exec, const String& s, unsigned offset, unsigned length) { return jsSubstring8(&exec->vm(), s, offset, length); } 516 inline JSString* jsSubstring(ExecState* exec, const String& s, unsigned offset, unsigned length) { return jsSubstring(&exec->vm(), s, offset, length); } 517 inline JSString* jsNontrivialString(ExecState* exec, const String& s) { return jsNontrivialString(&exec->vm(), s); } 518 inline JSString* jsOwnedString(ExecState* exec, const String& s) { return jsOwnedString(&exec->vm(), s); } 519 520 JS_EXPORT_PRIVATE JSString* jsStringWithCacheSlowCase(VM&, StringImpl&); 521 522 ALWAYS_INLINE JSString* jsStringWithCache(ExecState* exec, const String& s) 523 { 524 VM& vm = exec->vm(); 525 StringImpl* stringImpl = s.impl(); 526 if (!stringImpl || !stringImpl->length()) 527 return jsEmptyString(&vm); 528 529 if (stringImpl->length() == 1) { 530 UChar singleCharacter = (*stringImpl)[0u]; 531 if (singleCharacter <= maxSingleCharacterString) 532 return vm.smallStrings.singleCharacterString(static_cast<unsigned char>(singleCharacter)); 533 } 534 535 if (JSString* lastCachedString = vm.lastCachedString.get()) { 536 if (lastCachedString->tryGetValueImpl() == stringImpl) 537 return lastCachedString; 538 } 539 540 return jsStringWithCacheSlowCase(vm, *stringImpl); 541 } 542 543 ALWAYS_INLINE JSString* jsStringWithCache(ExecState* exec, const AtomicString& s) 544 { 545 return jsStringWithCache(exec, s.string()); 546 } 547 548 ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot) 549 { 550 if (propertyName == exec->propertyNames().length) { 551 slot.setValue(this, DontEnum | DontDelete | ReadOnly, jsNumber(m_length)); 552 return true; 553 } 554 555 unsigned i = propertyName.asIndex(); 556 if (i < m_length) { 557 ASSERT(i != PropertyName::NotAnIndex); // No need for an explicit check, the above test would always fail! 558 slot.setValue(this, DontDelete | ReadOnly, getIndex(exec, i)); 559 return true; 560 } 561 562 return false; 563 } 564 565 ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) 566 { 567 if (propertyName < m_length) { 568 slot.setValue(this, DontDelete | ReadOnly, getIndex(exec, propertyName)); 569 return true; 570 } 571 572 return false; 573 } 574 575 inline bool isJSString(JSValue v) { return v.isCell() && v.asCell()->type() == StringType; } 576 577 // --- JSValue inlines ---------------------------- 578 579 inline bool JSValue::toBoolean(ExecState* exec) const 580 { 581 if (isInt32()) 582 return asInt32(); 583 if (isDouble()) 584 return asDouble() > 0.0 || asDouble() < 0.0; // false for NaN 585 if (isCell()) 586 return asCell()->toBoolean(exec); 587 return isTrue(); // false, null, and undefined all convert to false. 588 } 589 590 inline JSString* JSValue::toString(ExecState* exec) const 591 { 592 if (isString()) 593 return jsCast<JSString*>(asCell()); 594 return toStringSlowCase(exec); 595 } 596 597 inline String JSValue::toWTFString(ExecState* exec) const 598 { 599 if (isString()) 600 return static_cast<JSString*>(asCell())->value(exec); 601 return toWTFStringSlowCase(exec); 602 } 603 604 ALWAYS_INLINE String inlineJSValueNotStringtoString(const JSValue& value, ExecState* exec) 605 { 606 VM& vm = exec->vm(); 607 if (value.isInt32()) 608 return vm.numericStrings.add(value.asInt32()); 609 if (value.isDouble()) 610 return vm.numericStrings.add(value.asDouble()); 611 if (value.isTrue()) 612 return vm.propertyNames->trueKeyword.string(); 613 if (value.isFalse()) 614 return vm.propertyNames->falseKeyword.string(); 615 if (value.isNull()) 616 return vm.propertyNames->nullKeyword.string(); 617 if (value.isUndefined()) 618 return vm.propertyNames->undefinedKeyword.string(); 619 return value.toString(exec)->value(exec); 620 } 621 622 ALWAYS_INLINE String JSValue::toWTFStringInline(ExecState* exec) const 623 { 624 if (isString()) 625 return static_cast<JSString*>(asCell())->value(exec); 626 627 return inlineJSValueNotStringtoString(*this, exec); 628 } 629 630} // namespace JSC 631 632#endif // JSString_h 633