PageRenderTime 44ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/src/qt/qtwebkit/Source/JavaScriptCore/runtime/StringPrototype.cpp

https://gitlab.com/x33n/phantomjs
C++ | 1209 lines | 895 code | 161 blank | 153 comment | 239 complexity | 3048f7d07a87d0a3a50eef9a24ecb009 MD5 | raw file
  1. /*
  2. * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
  3. * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2013 Apple Inc. All rights reserved.
  4. * Copyright (C) 2009 Torch Mobile, Inc.
  5. *
  6. * This library is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser 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. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public
  17. * License along with this library; if not, write to the Free Software
  18. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  19. *
  20. */
  21. #include "config.h"
  22. #include "StringPrototype.h"
  23. #include "ButterflyInlines.h"
  24. #include "CachedCall.h"
  25. #include "CopiedSpaceInlines.h"
  26. #include "Error.h"
  27. #include "Executable.h"
  28. #include "JSGlobalObjectFunctions.h"
  29. #include "JSArray.h"
  30. #include "JSFunction.h"
  31. #include "JSStringBuilder.h"
  32. #include "Lookup.h"
  33. #include "ObjectPrototype.h"
  34. #include "Operations.h"
  35. #include "PropertyNameArray.h"
  36. #include "RegExpCache.h"
  37. #include "RegExpConstructor.h"
  38. #include "RegExpMatchesArray.h"
  39. #include "RegExpObject.h"
  40. #include <wtf/ASCIICType.h>
  41. #include <wtf/MathExtras.h>
  42. #include <wtf/unicode/Collator.h>
  43. using namespace WTF;
  44. namespace JSC {
  45. ASSERT_HAS_TRIVIAL_DESTRUCTOR(StringPrototype);
  46. static EncodedJSValue JSC_HOST_CALL stringProtoFuncToString(ExecState*);
  47. static EncodedJSValue JSC_HOST_CALL stringProtoFuncCharAt(ExecState*);
  48. static EncodedJSValue JSC_HOST_CALL stringProtoFuncCharCodeAt(ExecState*);
  49. static EncodedJSValue JSC_HOST_CALL stringProtoFuncConcat(ExecState*);
  50. static EncodedJSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState*);
  51. static EncodedJSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState*);
  52. static EncodedJSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState*);
  53. static EncodedJSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState*);
  54. static EncodedJSValue JSC_HOST_CALL stringProtoFuncSearch(ExecState*);
  55. static EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState*);
  56. static EncodedJSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState*);
  57. static EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState*);
  58. static EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState*);
  59. static EncodedJSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState*);
  60. static EncodedJSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState*);
  61. static EncodedJSValue JSC_HOST_CALL stringProtoFuncLocaleCompare(ExecState*);
  62. static EncodedJSValue JSC_HOST_CALL stringProtoFuncBig(ExecState*);
  63. static EncodedJSValue JSC_HOST_CALL stringProtoFuncSmall(ExecState*);
  64. static EncodedJSValue JSC_HOST_CALL stringProtoFuncBlink(ExecState*);
  65. static EncodedJSValue JSC_HOST_CALL stringProtoFuncBold(ExecState*);
  66. static EncodedJSValue JSC_HOST_CALL stringProtoFuncFixed(ExecState*);
  67. static EncodedJSValue JSC_HOST_CALL stringProtoFuncItalics(ExecState*);
  68. static EncodedJSValue JSC_HOST_CALL stringProtoFuncStrike(ExecState*);
  69. static EncodedJSValue JSC_HOST_CALL stringProtoFuncSub(ExecState*);
  70. static EncodedJSValue JSC_HOST_CALL stringProtoFuncSup(ExecState*);
  71. static EncodedJSValue JSC_HOST_CALL stringProtoFuncFontcolor(ExecState*);
  72. static EncodedJSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState*);
  73. static EncodedJSValue JSC_HOST_CALL stringProtoFuncAnchor(ExecState*);
  74. static EncodedJSValue JSC_HOST_CALL stringProtoFuncLink(ExecState*);
  75. static EncodedJSValue JSC_HOST_CALL stringProtoFuncTrim(ExecState*);
  76. static EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimLeft(ExecState*);
  77. static EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimRight(ExecState*);
  78. const ClassInfo StringPrototype::s_info = { "String", &StringObject::s_info, 0, 0, CREATE_METHOD_TABLE(StringPrototype) };
  79. // ECMA 15.5.4
  80. StringPrototype::StringPrototype(ExecState* exec, Structure* structure)
  81. : StringObject(exec->vm(), structure)
  82. {
  83. }
  84. void StringPrototype::finishCreation(ExecState* exec, JSGlobalObject* globalObject, JSString* nameAndMessage)
  85. {
  86. VM& vm = exec->vm();
  87. Base::finishCreation(vm, nameAndMessage);
  88. ASSERT(inherits(&s_info));
  89. JSC_NATIVE_INTRINSIC_FUNCTION(vm.propertyNames->toString, stringProtoFuncToString, DontEnum, 0, StringPrototypeValueOfIntrinsic);
  90. JSC_NATIVE_INTRINSIC_FUNCTION(vm.propertyNames->valueOf, stringProtoFuncToString, DontEnum, 0, StringPrototypeValueOfIntrinsic);
  91. JSC_NATIVE_INTRINSIC_FUNCTION("charAt", stringProtoFuncCharAt, DontEnum, 1, CharAtIntrinsic);
  92. JSC_NATIVE_INTRINSIC_FUNCTION("charCodeAt", stringProtoFuncCharCodeAt, DontEnum, 1, CharCodeAtIntrinsic);
  93. JSC_NATIVE_FUNCTION("concat", stringProtoFuncConcat, DontEnum, 1);
  94. JSC_NATIVE_FUNCTION("indexOf", stringProtoFuncIndexOf, DontEnum, 1);
  95. JSC_NATIVE_FUNCTION("lastIndexOf", stringProtoFuncLastIndexOf, DontEnum, 1);
  96. JSC_NATIVE_FUNCTION("match", stringProtoFuncMatch, DontEnum, 1);
  97. JSC_NATIVE_FUNCTION("replace", stringProtoFuncReplace, DontEnum, 2);
  98. JSC_NATIVE_FUNCTION("search", stringProtoFuncSearch, DontEnum, 1);
  99. JSC_NATIVE_FUNCTION("slice", stringProtoFuncSlice, DontEnum, 2);
  100. JSC_NATIVE_FUNCTION("split", stringProtoFuncSplit, DontEnum, 2);
  101. JSC_NATIVE_FUNCTION("substr", stringProtoFuncSubstr, DontEnum, 2);
  102. JSC_NATIVE_FUNCTION("substring", stringProtoFuncSubstring, DontEnum, 2);
  103. JSC_NATIVE_FUNCTION("toLowerCase", stringProtoFuncToLowerCase, DontEnum, 0);
  104. JSC_NATIVE_FUNCTION("toUpperCase", stringProtoFuncToUpperCase, DontEnum, 0);
  105. JSC_NATIVE_FUNCTION("localeCompare", stringProtoFuncLocaleCompare, DontEnum, 1);
  106. JSC_NATIVE_FUNCTION("toLocaleLowerCase", stringProtoFuncToLowerCase, DontEnum, 0);
  107. JSC_NATIVE_FUNCTION("toLocaleUpperCase", stringProtoFuncToUpperCase, DontEnum, 0);
  108. JSC_NATIVE_FUNCTION("big", stringProtoFuncBig, DontEnum, 0);
  109. JSC_NATIVE_FUNCTION("small", stringProtoFuncSmall, DontEnum, 0);
  110. JSC_NATIVE_FUNCTION("blink", stringProtoFuncBlink, DontEnum, 0);
  111. JSC_NATIVE_FUNCTION("bold", stringProtoFuncBold, DontEnum, 0);
  112. JSC_NATIVE_FUNCTION("fixed", stringProtoFuncFixed, DontEnum, 0);
  113. JSC_NATIVE_FUNCTION("italics", stringProtoFuncItalics, DontEnum, 0);
  114. JSC_NATIVE_FUNCTION("strike", stringProtoFuncStrike, DontEnum, 0);
  115. JSC_NATIVE_FUNCTION("sub", stringProtoFuncSub, DontEnum, 0);
  116. JSC_NATIVE_FUNCTION("sup", stringProtoFuncSup, DontEnum, 0);
  117. JSC_NATIVE_FUNCTION("fontcolor", stringProtoFuncFontcolor, DontEnum, 1);
  118. JSC_NATIVE_FUNCTION("fontsize", stringProtoFuncFontsize, DontEnum, 1);
  119. JSC_NATIVE_FUNCTION("anchor", stringProtoFuncAnchor, DontEnum, 1);
  120. JSC_NATIVE_FUNCTION("link", stringProtoFuncLink, DontEnum, 1);
  121. JSC_NATIVE_FUNCTION("trim", stringProtoFuncTrim, DontEnum, 0);
  122. JSC_NATIVE_FUNCTION("trimLeft", stringProtoFuncTrimLeft, DontEnum, 0);
  123. JSC_NATIVE_FUNCTION("trimRight", stringProtoFuncTrimRight, DontEnum, 0);
  124. // The constructor will be added later, after StringConstructor has been built
  125. putDirectWithoutTransition(exec->vm(), exec->propertyNames().length, jsNumber(0), DontDelete | ReadOnly | DontEnum);
  126. }
  127. StringPrototype* StringPrototype::create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure)
  128. {
  129. JSString* empty = jsEmptyString(exec);
  130. StringPrototype* prototype = new (NotNull, allocateCell<StringPrototype>(*exec->heap())) StringPrototype(exec, structure);
  131. prototype->finishCreation(exec, globalObject, empty);
  132. return prototype;
  133. }
  134. // ------------------------------ Functions --------------------------
  135. // Helper for producing a JSString for 'string', where 'string' was been produced by
  136. // calling ToString on 'originalValue'. In cases where 'originalValue' already was a
  137. // string primitive we can just use this, otherwise we need to allocate a new JSString.
  138. static inline JSString* jsStringWithReuse(ExecState* exec, JSValue originalValue, const String& string)
  139. {
  140. if (originalValue.isString()) {
  141. ASSERT(asString(originalValue)->value(exec) == string);
  142. return asString(originalValue);
  143. }
  144. return jsString(exec, string);
  145. }
  146. template <typename CharType>
  147. static NEVER_INLINE String substituteBackreferencesSlow(const String& replacement, const String& source, const int* ovector, RegExp* reg, size_t i)
  148. {
  149. Vector<CharType> substitutedReplacement;
  150. int offset = 0;
  151. do {
  152. if (i + 1 == replacement.length())
  153. break;
  154. UChar ref = replacement[i + 1];
  155. if (ref == '$') {
  156. // "$$" -> "$"
  157. ++i;
  158. substitutedReplacement.append(replacement.getCharactersWithUpconvert<CharType>() + offset, i - offset);
  159. offset = i + 1;
  160. continue;
  161. }
  162. int backrefStart;
  163. int backrefLength;
  164. int advance = 0;
  165. if (ref == '&') {
  166. backrefStart = ovector[0];
  167. backrefLength = ovector[1] - backrefStart;
  168. } else if (ref == '`') {
  169. backrefStart = 0;
  170. backrefLength = ovector[0];
  171. } else if (ref == '\'') {
  172. backrefStart = ovector[1];
  173. backrefLength = source.length() - backrefStart;
  174. } else if (reg && ref >= '0' && ref <= '9') {
  175. // 1- and 2-digit back references are allowed
  176. unsigned backrefIndex = ref - '0';
  177. if (backrefIndex > reg->numSubpatterns())
  178. continue;
  179. if (replacement.length() > i + 2) {
  180. ref = replacement[i + 2];
  181. if (ref >= '0' && ref <= '9') {
  182. backrefIndex = 10 * backrefIndex + ref - '0';
  183. if (backrefIndex > reg->numSubpatterns())
  184. backrefIndex = backrefIndex / 10; // Fall back to the 1-digit reference
  185. else
  186. advance = 1;
  187. }
  188. }
  189. if (!backrefIndex)
  190. continue;
  191. backrefStart = ovector[2 * backrefIndex];
  192. backrefLength = ovector[2 * backrefIndex + 1] - backrefStart;
  193. } else
  194. continue;
  195. if (i - offset)
  196. substitutedReplacement.append(replacement.getCharactersWithUpconvert<CharType>() + offset, i - offset);
  197. i += 1 + advance;
  198. offset = i + 1;
  199. if (backrefStart >= 0)
  200. substitutedReplacement.append(source.getCharactersWithUpconvert<CharType>() + backrefStart, backrefLength);
  201. } while ((i = replacement.find('$', i + 1)) != notFound);
  202. if (replacement.length() - offset)
  203. substitutedReplacement.append(replacement.getCharactersWithUpconvert<CharType>() + offset, replacement.length() - offset);
  204. substitutedReplacement.shrinkToFit();
  205. return String::adopt(substitutedReplacement);
  206. }
  207. static inline String substituteBackreferences(const String& replacement, const String& source, const int* ovector, RegExp* reg)
  208. {
  209. size_t i = replacement.find('$');
  210. if (UNLIKELY(i != notFound)) {
  211. if (replacement.is8Bit() && source.is8Bit())
  212. return substituteBackreferencesSlow<LChar>(replacement, source, ovector, reg, i);
  213. return substituteBackreferencesSlow<UChar>(replacement, source, ovector, reg, i);
  214. }
  215. return replacement;
  216. }
  217. static inline int localeCompare(const String& a, const String& b)
  218. {
  219. return Collator::userDefault()->collate(reinterpret_cast<const ::UChar*>(a.characters()), a.length(), reinterpret_cast<const ::UChar*>(b.characters()), b.length());
  220. }
  221. struct StringRange {
  222. public:
  223. StringRange(int pos, int len)
  224. : position(pos)
  225. , length(len)
  226. {
  227. }
  228. StringRange()
  229. {
  230. }
  231. int position;
  232. int length;
  233. };
  234. static ALWAYS_INLINE JSValue jsSpliceSubstrings(ExecState* exec, JSString* sourceVal, const String& source, const StringRange* substringRanges, int rangeCount)
  235. {
  236. if (rangeCount == 1) {
  237. int sourceSize = source.length();
  238. int position = substringRanges[0].position;
  239. int length = substringRanges[0].length;
  240. if (position <= 0 && length >= sourceSize)
  241. return sourceVal;
  242. // We could call String::substringSharingImpl(), but this would result in redundant checks.
  243. return jsString(exec, StringImpl::create(source.impl(), std::max(0, position), std::min(sourceSize, length)));
  244. }
  245. int totalLength = 0;
  246. for (int i = 0; i < rangeCount; i++)
  247. totalLength += substringRanges[i].length;
  248. if (!totalLength)
  249. return jsEmptyString(exec);
  250. if (source.is8Bit()) {
  251. LChar* buffer;
  252. const LChar* sourceData = source.characters8();
  253. RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength, buffer);
  254. if (!impl)
  255. return throwOutOfMemoryError(exec);
  256. int bufferPos = 0;
  257. for (int i = 0; i < rangeCount; i++) {
  258. if (int srcLen = substringRanges[i].length) {
  259. StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen);
  260. bufferPos += srcLen;
  261. }
  262. }
  263. return jsString(exec, impl.release());
  264. }
  265. UChar* buffer;
  266. const UChar* sourceData = source.characters16();
  267. RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength, buffer);
  268. if (!impl)
  269. return throwOutOfMemoryError(exec);
  270. int bufferPos = 0;
  271. for (int i = 0; i < rangeCount; i++) {
  272. if (int srcLen = substringRanges[i].length) {
  273. StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen);
  274. bufferPos += srcLen;
  275. }
  276. }
  277. return jsString(exec, impl.release());
  278. }
  279. static ALWAYS_INLINE JSValue jsSpliceSubstringsWithSeparators(ExecState* exec, JSString* sourceVal, const String& source, const StringRange* substringRanges, int rangeCount, const String* separators, int separatorCount)
  280. {
  281. if (rangeCount == 1 && separatorCount == 0) {
  282. int sourceSize = source.length();
  283. int position = substringRanges[0].position;
  284. int length = substringRanges[0].length;
  285. if (position <= 0 && length >= sourceSize)
  286. return sourceVal;
  287. // We could call String::substringSharingImpl(), but this would result in redundant checks.
  288. return jsString(exec, StringImpl::create(source.impl(), std::max(0, position), std::min(sourceSize, length)));
  289. }
  290. Checked<int, RecordOverflow> totalLength = 0;
  291. bool allSeparators8Bit = true;
  292. for (int i = 0; i < rangeCount; i++)
  293. totalLength += substringRanges[i].length;
  294. for (int i = 0; i < separatorCount; i++) {
  295. totalLength += separators[i].length();
  296. if (separators[i].length() && !separators[i].is8Bit())
  297. allSeparators8Bit = false;
  298. }
  299. if (totalLength.hasOverflowed())
  300. return throwOutOfMemoryError(exec);
  301. if (!totalLength)
  302. return jsEmptyString(exec);
  303. if (source.is8Bit() && allSeparators8Bit) {
  304. LChar* buffer;
  305. const LChar* sourceData = source.characters8();
  306. RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength.unsafeGet(), buffer);
  307. if (!impl)
  308. return throwOutOfMemoryError(exec);
  309. int maxCount = std::max(rangeCount, separatorCount);
  310. int bufferPos = 0;
  311. for (int i = 0; i < maxCount; i++) {
  312. if (i < rangeCount) {
  313. if (int srcLen = substringRanges[i].length) {
  314. StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen);
  315. bufferPos += srcLen;
  316. }
  317. }
  318. if (i < separatorCount) {
  319. if (int sepLen = separators[i].length()) {
  320. StringImpl::copyChars(buffer + bufferPos, separators[i].characters8(), sepLen);
  321. bufferPos += sepLen;
  322. }
  323. }
  324. }
  325. return jsString(exec, impl.release());
  326. }
  327. UChar* buffer;
  328. RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength.unsafeGet(), buffer);
  329. if (!impl)
  330. return throwOutOfMemoryError(exec);
  331. int maxCount = std::max(rangeCount, separatorCount);
  332. int bufferPos = 0;
  333. for (int i = 0; i < maxCount; i++) {
  334. if (i < rangeCount) {
  335. if (int srcLen = substringRanges[i].length) {
  336. if (source.is8Bit())
  337. StringImpl::copyChars(buffer + bufferPos, source.characters8() + substringRanges[i].position, srcLen);
  338. else
  339. StringImpl::copyChars(buffer + bufferPos, source.characters16() + substringRanges[i].position, srcLen);
  340. bufferPos += srcLen;
  341. }
  342. }
  343. if (i < separatorCount) {
  344. if (int sepLen = separators[i].length()) {
  345. if (separators[i].is8Bit())
  346. StringImpl::copyChars(buffer + bufferPos, separators[i].characters8(), sepLen);
  347. else
  348. StringImpl::copyChars(buffer + bufferPos, separators[i].characters16(), sepLen);
  349. bufferPos += sepLen;
  350. }
  351. }
  352. }
  353. return jsString(exec, impl.release());
  354. }
  355. static NEVER_INLINE EncodedJSValue removeUsingRegExpSearch(ExecState* exec, JSString* string, const String& source, RegExp* regExp)
  356. {
  357. size_t lastIndex = 0;
  358. unsigned startPosition = 0;
  359. Vector<StringRange, 16> sourceRanges;
  360. VM* vm = &exec->vm();
  361. RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
  362. unsigned sourceLen = source.length();
  363. while (true) {
  364. MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition);
  365. if (!result)
  366. break;
  367. if (lastIndex < result.start)
  368. sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
  369. lastIndex = result.end;
  370. startPosition = lastIndex;
  371. // special case of empty match
  372. if (result.empty()) {
  373. startPosition++;
  374. if (startPosition > sourceLen)
  375. break;
  376. }
  377. }
  378. if (!lastIndex)
  379. return JSValue::encode(string);
  380. if (static_cast<unsigned>(lastIndex) < sourceLen)
  381. sourceRanges.append(StringRange(lastIndex, sourceLen - lastIndex));
  382. return JSValue::encode(jsSpliceSubstrings(exec, string, source, sourceRanges.data(), sourceRanges.size()));
  383. }
  384. static NEVER_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSString* string, JSValue searchValue)
  385. {
  386. JSValue replaceValue = exec->argument(1);
  387. String replacementString;
  388. CallData callData;
  389. CallType callType = getCallData(replaceValue, callData);
  390. if (callType == CallTypeNone)
  391. replacementString = replaceValue.toString(exec)->value(exec);
  392. const String& source = string->value(exec);
  393. unsigned sourceLen = source.length();
  394. if (exec->hadException())
  395. return JSValue::encode(JSValue());
  396. RegExpObject* regExpObject = asRegExpObject(searchValue);
  397. RegExp* regExp = regExpObject->regExp();
  398. bool global = regExp->global();
  399. if (global) {
  400. // ES5.1 15.5.4.10 step 8.a.
  401. regExpObject->setLastIndex(exec, 0);
  402. if (exec->hadException())
  403. return JSValue::encode(JSValue());
  404. if (callType == CallTypeNone && !replacementString.length())
  405. return removeUsingRegExpSearch(exec, string, source, regExp);
  406. }
  407. RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
  408. size_t lastIndex = 0;
  409. unsigned startPosition = 0;
  410. Vector<StringRange, 16> sourceRanges;
  411. Vector<String, 16> replacements;
  412. // This is either a loop (if global is set) or a one-way (if not).
  413. if (global && callType == CallTypeJS) {
  414. // regExp->numSubpatterns() + 1 for pattern args, + 2 for match start and string
  415. int argCount = regExp->numSubpatterns() + 1 + 2;
  416. JSFunction* func = jsCast<JSFunction*>(replaceValue);
  417. CachedCall cachedCall(exec, func, argCount);
  418. if (exec->hadException())
  419. return JSValue::encode(jsNull());
  420. VM* vm = &exec->vm();
  421. if (source.is8Bit()) {
  422. while (true) {
  423. int* ovector;
  424. MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition, &ovector);
  425. if (!result)
  426. break;
  427. sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
  428. unsigned i = 0;
  429. for (; i < regExp->numSubpatterns() + 1; ++i) {
  430. int matchStart = ovector[i * 2];
  431. int matchLen = ovector[i * 2 + 1] - matchStart;
  432. if (matchStart < 0)
  433. cachedCall.setArgument(i, jsUndefined());
  434. else
  435. cachedCall.setArgument(i, jsSubstring8(vm, source, matchStart, matchLen));
  436. }
  437. cachedCall.setArgument(i++, jsNumber(result.start));
  438. cachedCall.setArgument(i++, string);
  439. cachedCall.setThis(jsUndefined());
  440. JSValue jsResult = cachedCall.call();
  441. replacements.append(jsResult.toString(cachedCall.newCallFrame(exec))->value(exec));
  442. if (exec->hadException())
  443. break;
  444. lastIndex = result.end;
  445. startPosition = lastIndex;
  446. // special case of empty match
  447. if (result.empty()) {
  448. startPosition++;
  449. if (startPosition > sourceLen)
  450. break;
  451. }
  452. }
  453. } else {
  454. while (true) {
  455. int* ovector;
  456. MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition, &ovector);
  457. if (!result)
  458. break;
  459. sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
  460. unsigned i = 0;
  461. for (; i < regExp->numSubpatterns() + 1; ++i) {
  462. int matchStart = ovector[i * 2];
  463. int matchLen = ovector[i * 2 + 1] - matchStart;
  464. if (matchStart < 0)
  465. cachedCall.setArgument(i, jsUndefined());
  466. else
  467. cachedCall.setArgument(i, jsSubstring(vm, source, matchStart, matchLen));
  468. }
  469. cachedCall.setArgument(i++, jsNumber(result.start));
  470. cachedCall.setArgument(i++, string);
  471. cachedCall.setThis(jsUndefined());
  472. JSValue jsResult = cachedCall.call();
  473. replacements.append(jsResult.toString(cachedCall.newCallFrame(exec))->value(exec));
  474. if (exec->hadException())
  475. break;
  476. lastIndex = result.end;
  477. startPosition = lastIndex;
  478. // special case of empty match
  479. if (result.empty()) {
  480. startPosition++;
  481. if (startPosition > sourceLen)
  482. break;
  483. }
  484. }
  485. }
  486. } else {
  487. VM* vm = &exec->vm();
  488. do {
  489. int* ovector;
  490. MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition, &ovector);
  491. if (!result)
  492. break;
  493. if (callType != CallTypeNone) {
  494. sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
  495. MarkedArgumentBuffer args;
  496. for (unsigned i = 0; i < regExp->numSubpatterns() + 1; ++i) {
  497. int matchStart = ovector[i * 2];
  498. int matchLen = ovector[i * 2 + 1] - matchStart;
  499. if (matchStart < 0)
  500. args.append(jsUndefined());
  501. else
  502. args.append(jsSubstring(exec, source, matchStart, matchLen));
  503. }
  504. args.append(jsNumber(result.start));
  505. args.append(string);
  506. replacements.append(call(exec, replaceValue, callType, callData, jsUndefined(), args).toString(exec)->value(exec));
  507. if (exec->hadException())
  508. break;
  509. } else {
  510. int replLen = replacementString.length();
  511. if (lastIndex < result.start || replLen) {
  512. sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
  513. if (replLen)
  514. replacements.append(substituteBackreferences(replacementString, source, ovector, regExp));
  515. else
  516. replacements.append(String());
  517. }
  518. }
  519. lastIndex = result.end;
  520. startPosition = lastIndex;
  521. // special case of empty match
  522. if (result.empty()) {
  523. startPosition++;
  524. if (startPosition > sourceLen)
  525. break;
  526. }
  527. } while (global);
  528. }
  529. if (!lastIndex && replacements.isEmpty())
  530. return JSValue::encode(string);
  531. if (static_cast<unsigned>(lastIndex) < sourceLen)
  532. sourceRanges.append(StringRange(lastIndex, sourceLen - lastIndex));
  533. return JSValue::encode(jsSpliceSubstringsWithSeparators(exec, string, source, sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size()));
  534. }
  535. static inline EncodedJSValue replaceUsingStringSearch(ExecState* exec, JSString* jsString, JSValue searchValue)
  536. {
  537. const String& string = jsString->value(exec);
  538. String searchString = searchValue.toString(exec)->value(exec);
  539. if (exec->hadException())
  540. return JSValue::encode(jsUndefined());
  541. size_t matchStart = string.find(searchString);
  542. if (matchStart == notFound)
  543. return JSValue::encode(jsString);
  544. JSValue replaceValue = exec->argument(1);
  545. CallData callData;
  546. CallType callType = getCallData(replaceValue, callData);
  547. if (callType != CallTypeNone) {
  548. MarkedArgumentBuffer args;
  549. args.append(jsSubstring(exec, string, matchStart, searchString.impl()->length()));
  550. args.append(jsNumber(matchStart));
  551. args.append(jsString);
  552. replaceValue = call(exec, replaceValue, callType, callData, jsUndefined(), args);
  553. if (exec->hadException())
  554. return JSValue::encode(jsUndefined());
  555. }
  556. String replaceString = replaceValue.toString(exec)->value(exec);
  557. if (exec->hadException())
  558. return JSValue::encode(jsUndefined());
  559. StringImpl* stringImpl = string.impl();
  560. String leftPart(StringImpl::create(stringImpl, 0, matchStart));
  561. size_t matchEnd = matchStart + searchString.impl()->length();
  562. int ovector[2] = { static_cast<int>(matchStart), static_cast<int>(matchEnd)};
  563. String middlePart = substituteBackreferences(replaceString, string, ovector, 0);
  564. size_t leftLength = stringImpl->length() - matchEnd;
  565. String rightPart(StringImpl::create(stringImpl, matchEnd, leftLength));
  566. return JSValue::encode(JSC::jsString(exec, leftPart, middlePart, rightPart));
  567. }
  568. EncodedJSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec)
  569. {
  570. JSValue thisValue = exec->hostThisValue();
  571. if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
  572. return throwVMTypeError(exec);
  573. JSString* string = thisValue.toString(exec);
  574. JSValue searchValue = exec->argument(0);
  575. if (searchValue.inherits(&RegExpObject::s_info))
  576. return replaceUsingRegExpSearch(exec, string, searchValue);
  577. return replaceUsingStringSearch(exec, string, searchValue);
  578. }
  579. EncodedJSValue JSC_HOST_CALL stringProtoFuncToString(ExecState* exec)
  580. {
  581. JSValue thisValue = exec->hostThisValue();
  582. // Also used for valueOf.
  583. if (thisValue.isString())
  584. return JSValue::encode(thisValue);
  585. if (thisValue.inherits(&StringObject::s_info))
  586. return JSValue::encode(asStringObject(thisValue)->internalValue());
  587. return throwVMTypeError(exec);
  588. }
  589. EncodedJSValue JSC_HOST_CALL stringProtoFuncCharAt(ExecState* exec)
  590. {
  591. JSValue thisValue = exec->hostThisValue();
  592. if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
  593. return throwVMTypeError(exec);
  594. String s = thisValue.toString(exec)->value(exec);
  595. unsigned len = s.length();
  596. JSValue a0 = exec->argument(0);
  597. if (a0.isUInt32()) {
  598. uint32_t i = a0.asUInt32();
  599. if (i < len)
  600. return JSValue::encode(jsSingleCharacterSubstring(exec, s, i));
  601. return JSValue::encode(jsEmptyString(exec));
  602. }
  603. double dpos = a0.toInteger(exec);
  604. if (dpos >= 0 && dpos < len)
  605. return JSValue::encode(jsSingleCharacterSubstring(exec, s, static_cast<unsigned>(dpos)));
  606. return JSValue::encode(jsEmptyString(exec));
  607. }
  608. EncodedJSValue JSC_HOST_CALL stringProtoFuncCharCodeAt(ExecState* exec)
  609. {
  610. JSValue thisValue = exec->hostThisValue();
  611. if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
  612. return throwVMTypeError(exec);
  613. String s = thisValue.toString(exec)->value(exec);
  614. unsigned len = s.length();
  615. JSValue a0 = exec->argument(0);
  616. if (a0.isUInt32()) {
  617. uint32_t i = a0.asUInt32();
  618. if (i < len) {
  619. if (s.is8Bit())
  620. return JSValue::encode(jsNumber(s.characters8()[i]));
  621. return JSValue::encode(jsNumber(s.characters16()[i]));
  622. }
  623. return JSValue::encode(jsNaN());
  624. }
  625. double dpos = a0.toInteger(exec);
  626. if (dpos >= 0 && dpos < len)
  627. return JSValue::encode(jsNumber(s[static_cast<int>(dpos)]));
  628. return JSValue::encode(jsNaN());
  629. }
  630. EncodedJSValue JSC_HOST_CALL stringProtoFuncConcat(ExecState* exec)
  631. {
  632. JSValue thisValue = exec->hostThisValue();
  633. if (thisValue.isString() && (exec->argumentCount() == 1))
  634. return JSValue::encode(jsString(exec, asString(thisValue), exec->argument(0).toString(exec)));
  635. if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
  636. return throwVMTypeError(exec);
  637. return JSValue::encode(jsStringFromArguments(exec, thisValue));
  638. }
  639. EncodedJSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState* exec)
  640. {
  641. JSValue thisValue = exec->hostThisValue();
  642. if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
  643. return throwVMTypeError(exec);
  644. String s = thisValue.toString(exec)->value(exec);
  645. JSValue a0 = exec->argument(0);
  646. JSValue a1 = exec->argument(1);
  647. String u2 = a0.toString(exec)->value(exec);
  648. size_t result;
  649. if (a1.isUndefined())
  650. result = s.find(u2);
  651. else {
  652. unsigned pos;
  653. int len = s.length();
  654. if (a1.isUInt32())
  655. pos = std::min<uint32_t>(a1.asUInt32(), len);
  656. else {
  657. double dpos = a1.toInteger(exec);
  658. if (dpos < 0)
  659. dpos = 0;
  660. else if (dpos > len)
  661. dpos = len;
  662. pos = static_cast<unsigned>(dpos);
  663. }
  664. result = s.find(u2, pos);
  665. }
  666. if (result == notFound)
  667. return JSValue::encode(jsNumber(-1));
  668. return JSValue::encode(jsNumber(result));
  669. }
  670. EncodedJSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState* exec)
  671. {
  672. JSValue thisValue = exec->hostThisValue();
  673. if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
  674. return throwVMTypeError(exec);
  675. String s = thisValue.toString(exec)->value(exec);
  676. int len = s.length();
  677. JSValue a0 = exec->argument(0);
  678. JSValue a1 = exec->argument(1);
  679. String u2 = a0.toString(exec)->value(exec);
  680. double dpos = a1.toIntegerPreserveNaN(exec);
  681. if (dpos < 0)
  682. dpos = 0;
  683. else if (!(dpos <= len)) // true for NaN
  684. dpos = len;
  685. size_t result;
  686. unsigned startPosition = static_cast<unsigned>(dpos);
  687. if (!startPosition)
  688. result = s.startsWith(u2) ? 0 : notFound;
  689. else
  690. result = s.reverseFind(u2, startPosition);
  691. if (result == notFound)
  692. return JSValue::encode(jsNumber(-1));
  693. return JSValue::encode(jsNumber(result));
  694. }
  695. EncodedJSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState* exec)
  696. {
  697. JSValue thisValue = exec->hostThisValue();
  698. if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
  699. return throwVMTypeError(exec);
  700. JSString* string = thisValue.toString(exec);
  701. String s = string->value(exec);
  702. VM* vm = &exec->vm();
  703. JSValue a0 = exec->argument(0);
  704. RegExp* regExp;
  705. bool global = false;
  706. if (a0.inherits(&RegExpObject::s_info)) {
  707. RegExpObject* regExpObject = asRegExpObject(a0);
  708. regExp = regExpObject->regExp();
  709. if ((global = regExp->global())) {
  710. // ES5.1 15.5.4.10 step 8.a.
  711. regExpObject->setLastIndex(exec, 0);
  712. if (exec->hadException())
  713. return JSValue::encode(JSValue());
  714. }
  715. } else {
  716. /*
  717. * ECMA 15.5.4.12 String.prototype.search (regexp)
  718. * If regexp is not an object whose [[Class]] property is "RegExp", it is
  719. * replaced with the result of the expression new RegExp(regexp).
  720. * Per ECMA 15.10.4.1, if a0 is undefined substitute the empty string.
  721. */
  722. regExp = RegExp::create(exec->vm(), a0.isUndefined() ? String("") : a0.toString(exec)->value(exec), NoFlags);
  723. if (!regExp->isValid())
  724. return throwVMError(exec, createSyntaxError(exec, regExp->errorMessage()));
  725. }
  726. RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
  727. MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, s, 0);
  728. // case without 'g' flag is handled like RegExp.prototype.exec
  729. if (!global)
  730. return JSValue::encode(result ? RegExpMatchesArray::create(exec, string, regExp, result) : jsNull());
  731. // return array of matches
  732. MarkedArgumentBuffer list;
  733. while (result) {
  734. size_t end = result.end;
  735. size_t length = end - result.start;
  736. list.append(jsSubstring(exec, s, result.start, length));
  737. if (!length)
  738. ++end;
  739. result = regExpConstructor->performMatch(*vm, regExp, string, s, end);
  740. }
  741. if (list.isEmpty()) {
  742. // if there are no matches at all, it's important to return
  743. // Null instead of an empty array, because this matches
  744. // other browsers and because Null is a false value.
  745. return JSValue::encode(jsNull());
  746. }
  747. return JSValue::encode(constructArray(exec, static_cast<ArrayAllocationProfile*>(0), list));
  748. }
  749. EncodedJSValue JSC_HOST_CALL stringProtoFuncSearch(ExecState* exec)
  750. {
  751. JSValue thisValue = exec->hostThisValue();
  752. if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
  753. return throwVMTypeError(exec);
  754. JSString* string = thisValue.toString(exec);
  755. String s = string->value(exec);
  756. VM* vm = &exec->vm();
  757. JSValue a0 = exec->argument(0);
  758. RegExp* reg;
  759. if (a0.inherits(&RegExpObject::s_info))
  760. reg = asRegExpObject(a0)->regExp();
  761. else {
  762. /*
  763. * ECMA 15.5.4.12 String.prototype.search (regexp)
  764. * If regexp is not an object whose [[Class]] property is "RegExp", it is
  765. * replaced with the result of the expression new RegExp(regexp).
  766. * Per ECMA 15.10.4.1, if a0 is undefined substitute the empty string.
  767. */
  768. reg = RegExp::create(exec->vm(), a0.isUndefined() ? String("") : a0.toString(exec)->value(exec), NoFlags);
  769. if (!reg->isValid())
  770. return throwVMError(exec, createSyntaxError(exec, reg->errorMessage()));
  771. }
  772. RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
  773. MatchResult result = regExpConstructor->performMatch(*vm, reg, string, s, 0);
  774. return JSValue::encode(result ? jsNumber(result.start) : jsNumber(-1));
  775. }
  776. EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState* exec)
  777. {
  778. JSValue thisValue = exec->hostThisValue();
  779. if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
  780. return throwVMTypeError(exec);
  781. String s = thisValue.toString(exec)->value(exec);
  782. int len = s.length();
  783. JSValue a0 = exec->argument(0);
  784. JSValue a1 = exec->argument(1);
  785. // The arg processing is very much like ArrayProtoFunc::Slice
  786. double start = a0.toInteger(exec);
  787. double end = a1.isUndefined() ? len : a1.toInteger(exec);
  788. double from = start < 0 ? len + start : start;
  789. double to = end < 0 ? len + end : end;
  790. if (to > from && to > 0 && from < len) {
  791. if (from < 0)
  792. from = 0;
  793. if (to > len)
  794. to = len;
  795. return JSValue::encode(jsSubstring(exec, s, static_cast<unsigned>(from), static_cast<unsigned>(to) - static_cast<unsigned>(from)));
  796. }
  797. return JSValue::encode(jsEmptyString(exec));
  798. }
  799. // Return true in case of early return (resultLength got to limitLength).
  800. template<typename CharacterType>
  801. static ALWAYS_INLINE bool splitStringByOneCharacterImpl(ExecState* exec, JSArray* result, const String& input, StringImpl* string, UChar separatorCharacter, size_t& position, unsigned& resultLength, unsigned limitLength)
  802. {
  803. // 12. Let q = p.
  804. size_t matchPosition;
  805. const CharacterType* characters = string->getCharacters<CharacterType>();
  806. // 13. Repeat, while q != s
  807. // a. Call SplitMatch(S, q, R) and let z be its MatchResult result.
  808. // b. If z is failure, then let q = q+1.
  809. // c. Else, z is not failure
  810. while ((matchPosition = WTF::find(characters, string->length(), separatorCharacter, position)) != notFound) {
  811. // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
  812. // through q (exclusive).
  813. // 2. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA),
  814. // Property Descriptor {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
  815. result->putDirectIndex(exec, resultLength, jsSubstring(exec, input, position, matchPosition - position));
  816. // 3. Increment lengthA by 1.
  817. // 4. If lengthA == lim, return A.
  818. if (++resultLength == limitLength)
  819. return true;
  820. // 5. Let p = e.
  821. // 8. Let q = p.
  822. position = matchPosition + 1;
  823. }
  824. return false;
  825. }
  826. // ES 5.1 - 15.5.4.14 String.prototype.split (separator, limit)
  827. EncodedJSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec)
  828. {
  829. // 1. Call CheckObjectCoercible passing the this value as its argument.
  830. JSValue thisValue = exec->hostThisValue();
  831. if (thisValue.isUndefinedOrNull())
  832. return throwVMTypeError(exec);
  833. // 2. Let S be the result of calling ToString, giving it the this value as its argument.
  834. // 6. Let s be the number of characters in S.
  835. String input = thisValue.toString(exec)->value(exec);
  836. // 3. Let A be a new array created as if by the expression new Array()
  837. // where Array is the standard built-in constructor with that name.
  838. JSArray* result = constructEmptyArray(exec, 0);
  839. // 4. Let lengthA be 0.
  840. unsigned resultLength = 0;
  841. // 5. If limit is undefined, let lim = 2^32-1; else let lim = ToUint32(limit).
  842. JSValue limitValue = exec->argument(1);
  843. unsigned limit = limitValue.isUndefined() ? 0xFFFFFFFFu : limitValue.toUInt32(exec);
  844. // 7. Let p = 0.
  845. size_t position = 0;
  846. // 8. If separator is a RegExp object (its [[Class]] is "RegExp"), let R = separator;
  847. // otherwise let R = ToString(separator).
  848. JSValue separatorValue = exec->argument(0);
  849. if (separatorValue.inherits(&RegExpObject::s_info)) {
  850. VM* vm = &exec->vm();
  851. RegExp* reg = asRegExpObject(separatorValue)->regExp();
  852. // 9. If lim == 0, return A.
  853. if (!limit)
  854. return JSValue::encode(result);
  855. // 10. If separator is undefined, then
  856. if (separatorValue.isUndefined()) {
  857. // a. Call the [[DefineOwnProperty]] internal method of A with arguments "0",
  858. // Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
  859. result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input));
  860. // b. Return A.
  861. return JSValue::encode(result);
  862. }
  863. // 11. If s == 0, then
  864. if (input.isEmpty()) {
  865. // a. Call SplitMatch(S, 0, R) and let z be its MatchResult result.
  866. // b. If z is not failure, return A.
  867. // c. Call the [[DefineOwnProperty]] internal method of A with arguments "0",
  868. // Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
  869. // d. Return A.
  870. if (!reg->match(*vm, input, 0))
  871. result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input));
  872. return JSValue::encode(result);
  873. }
  874. // 12. Let q = p.
  875. size_t matchPosition = 0;
  876. // 13. Repeat, while q != s
  877. while (matchPosition < input.length()) {
  878. // a. Call SplitMatch(S, q, R) and let z be its MatchResult result.
  879. Vector<int, 32> ovector;
  880. int mpos = reg->match(*vm, input, matchPosition, ovector);
  881. // b. If z is failure, then let q = q + 1.
  882. if (mpos < 0)
  883. break;
  884. matchPosition = mpos;
  885. // c. Else, z is not failure
  886. // i. z must be a State. Let e be z's endIndex and let cap be z's captures array.
  887. size_t matchEnd = ovector[1];
  888. // ii. If e == p, then let q = q + 1.
  889. if (matchEnd == position) {
  890. ++matchPosition;
  891. continue;
  892. }
  893. // iii. Else, e != p
  894. // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
  895. // through q (exclusive).
  896. // 2. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA),
  897. // Property Descriptor {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
  898. result->putDirectIndex(exec, resultLength, jsSubstring(exec, input, position, matchPosition - position));
  899. // 3. Increment lengthA by 1.
  900. // 4. If lengthA == lim, return A.
  901. if (++resultLength == limit)
  902. return JSValue::encode(result);
  903. // 5. Let p = e.
  904. // 8. Let q = p.
  905. position = matchEnd;
  906. matchPosition = matchEnd;
  907. // 6. Let i = 0.
  908. // 7. Repeat, while i is not equal to the number of elements in cap.
  909. // a Let i = i + 1.
  910. for (unsigned i = 1; i <= reg->numSubpatterns(); ++i) {
  911. // b Call the [[DefineOwnProperty]] internal method of A with arguments
  912. // ToString(lengthA), Property Descriptor {[[Value]]: cap[i], [[Writable]]:
  913. // true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
  914. int sub = ovector[i * 2];
  915. result->putDirectIndex(exec, resultLength, sub < 0 ? jsUndefined() : jsSubstring(exec, input, sub, ovector[i * 2 + 1] - sub));
  916. // c Increment lengthA by 1.
  917. // d If lengthA == lim, return A.
  918. if (++resultLength == limit)
  919. return JSValue::encode(result);
  920. }
  921. }
  922. } else {
  923. String separator = separatorValue.toString(exec)->value(exec);
  924. // 9. If lim == 0, return A.
  925. if (!limit)
  926. return JSValue::encode(result);
  927. // 10. If separator is undefined, then
  928. JSValue separatorValue = exec->argument(0);
  929. if (separatorValue.isUndefined()) {
  930. // a. Call the [[DefineOwnProperty]] internal method of A with arguments "0",
  931. // Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
  932. result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input));
  933. // b. Return A.
  934. return JSValue::encode(result);
  935. }
  936. // 11. If s == 0, then
  937. if (input.isEmpty()) {
  938. // a. Call SplitMatch(S, 0, R) and let z be its MatchResult result.
  939. // b. If z is not failure, return A.
  940. // c. Call the [[DefineOwnProperty]] internal method of A with arguments "0",
  941. // Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
  942. // d. Return A.
  943. if (!separator.isEmpty())
  944. result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input));
  945. return JSValue::encode(result);
  946. }
  947. // Optimized case for splitting on the empty string.
  948. if (separator.isEmpty()) {
  949. limit = std::min(limit, input.length());
  950. // Zero limt/input length handled in steps 9/11 respectively, above.
  951. ASSERT(limit);
  952. do {
  953. result->putDirectIndex(exec, position, jsSingleCharacterSubstring(exec, input, position));
  954. } while (++position < limit);
  955. return JSValue::encode(result);
  956. }
  957. // 3 cases:
  958. // -separator length == 1, 8 bits
  959. // -separator length == 1, 16 bits
  960. // -separator length > 1
  961. StringImpl* stringImpl = input.impl();
  962. StringImpl* separatorImpl = separator.impl();
  963. size_t separatorLength = separatorImpl->length();
  964. if (separatorLength == 1) {
  965. UChar separatorCharacter;
  966. if (separatorImpl->is8Bit())
  967. separatorCharacter = separatorImpl->characters8()[0];
  968. else
  969. separatorCharacter = separatorImpl->characters16()[0];
  970. if (stringImpl->is8Bit()) {
  971. if (splitStringByOneCharacterImpl<LChar>(exec, result, input, stringImpl, separatorCharacter, position, resultLength, limit))
  972. return JSValue::encode(result);
  973. } else {
  974. if (splitStringByOneCharacterImpl<UChar>(exec, result, input, stringImpl, separatorCharacter, position, resultLength, limit))
  975. return JSValue::encode(result);
  976. }
  977. } else {
  978. // 12. Let q = p.
  979. size_t matchPosition;
  980. // 13. Repeat, while q != s
  981. // a. Call SplitMatch(S, q, R) and let z be its MatchResult result.
  982. // b. If z is failure, then let q = q+1.
  983. // c. Else, z is not failure
  984. while ((matchPosition = stringImpl->find(separatorImpl, position)) != notFound) {
  985. // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
  986. // through q (exclusive).
  987. // 2. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA),
  988. // Property Descriptor {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
  989. result->putDirectIndex(exec, resultLength, jsSubstring(exec, input, position, matchPosition - position));
  990. // 3. Increment lengthA by 1.
  991. // 4. If lengthA == lim, return A.
  992. if (++resultLength == limit)
  993. return JSValue::encode(result);
  994. // 5. Let p = e.
  995. // 8. Let q = p.
  996. position = matchPosition + separator.length();
  997. }
  998. }
  999. }
  1000. // 14. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
  1001. // through s (exclusive).
  1002. // 15. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA), Property Descriptor
  1003. // {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
  1004. result->putDirectIndex(exec, resultLength++, jsSubstring(exec, input, position, input.length() - position));
  1005. // 16. Return A.
  1006. return JSValue::encode(result);
  1007. }
  1008. EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState* exec)
  1009. {
  1010. JSValue thisValue = exec->hostThisValue();
  1011. unsigned len;
  1012. JSString* jsString = 0;
  1013. String uString;
  1014. if (thisValue.isString()) {
  1015. jsString = jsCast<JSString*>(thisValue.asCell());
  1016. len = jsString->length();
  1017. } else if (thisValue.isUndefinedOrNull()) {
  1018. // CheckObjectCoercible
  1019. return throwVMTypeError(exec);
  1020. } else {
  1021. uString = thisValue.toString(exec)->value(exec);
  1022. if (exec->hadException())
  1023. return JSValue::encode(jsUndefined());
  1024. len = uString.length();
  1025. }
  1026. JSValue a0 = exec->argument(0);
  1027. JSValue a1 = exec->argument(1);
  1028. double start = a0.toInteger(exec);
  1029. double length = a1.isUndefined() ? len : a1.toInteger(exec);
  1030. if (start >= len || length <= 0)
  1031. return JSValue::encode(jsEmptyString(exec));
  1032. if (start < 0) {
  1033. start += len;
  1034. if (start < 0)
  1035. start = 0;
  1036. }
  1037. if (start + length > len)
  1038. length = len - start;
  1039. unsigned substringStart = static_cast<unsigned>(start);
  1040. unsigned substringLength = static_cast<unsigned>(length);
  1041. if (jsString)
  1042. return JSValue::encode(jsSubstring(exec, jsString, substringStart, substringLength));
  1043. return JSValue::encode(jsSubstring(exec, uString, substringStart, substringLength));
  1044. }
  1045. EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState* exec)
  1046. {
  1047. JSValue thisValue = exec->hostThisValue();
  1048. if (thisValue.isUndefinedOrNull()) // CheckObjectCo