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