StringPrototype.cpp 66 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668
  1. /*
  2. * Copyright (c) 2020-2021, Andreas Kling <andreas@ladybird.org>
  3. * Copyright (c) 2020-2023, Linus Groh <linusg@serenityos.org>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <AK/Checked.h>
  8. #include <AK/Function.h>
  9. #include <AK/StringBuilder.h>
  10. #include <AK/Utf16View.h>
  11. #include <LibGC/Heap.h>
  12. #include <LibJS/Runtime/AbstractOperations.h>
  13. #include <LibJS/Runtime/Array.h>
  14. #include <LibJS/Runtime/Completion.h>
  15. #include <LibJS/Runtime/Error.h>
  16. #include <LibJS/Runtime/GlobalObject.h>
  17. #include <LibJS/Runtime/Intl/AbstractOperations.h>
  18. #include <LibJS/Runtime/Intl/Collator.h>
  19. #include <LibJS/Runtime/Intl/CollatorCompareFunction.h>
  20. #include <LibJS/Runtime/Intl/CollatorConstructor.h>
  21. #include <LibJS/Runtime/PrimitiveString.h>
  22. #include <LibJS/Runtime/RegExpObject.h>
  23. #include <LibJS/Runtime/StringIterator.h>
  24. #include <LibJS/Runtime/StringObject.h>
  25. #include <LibJS/Runtime/StringPrototype.h>
  26. #include <LibJS/Runtime/Utf16String.h>
  27. #include <LibJS/Runtime/Value.h>
  28. #include <LibJS/Runtime/ValueInlines.h>
  29. #include <LibUnicode/CharacterTypes.h>
  30. #include <LibUnicode/Locale.h>
  31. #include <LibUnicode/Normalize.h>
  32. #include <string.h>
  33. namespace JS {
  34. GC_DEFINE_ALLOCATOR(StringPrototype);
  35. static ThrowCompletionOr<String> utf8_string_from(VM& vm)
  36. {
  37. auto this_value = TRY(require_object_coercible(vm, vm.this_value()));
  38. return TRY(this_value.to_string(vm));
  39. }
  40. static ThrowCompletionOr<Utf16String> utf16_string_from(VM& vm)
  41. {
  42. auto this_value = TRY(require_object_coercible(vm, vm.this_value()));
  43. return TRY(this_value.to_utf16_string(vm));
  44. }
  45. // 22.1.3.21.1 SplitMatch ( S, q, R ), https://tc39.es/ecma262/#sec-splitmatch
  46. // FIXME: This no longer exists in the spec!
  47. static Optional<size_t> split_match(Utf16View const& haystack, size_t start, Utf16View const& needle)
  48. {
  49. auto r = needle.length_in_code_units();
  50. auto s = haystack.length_in_code_units();
  51. if (start + r > s)
  52. return {};
  53. for (size_t i = 0; i < r; ++i) {
  54. if (haystack.code_unit_at(start + i) != needle.code_unit_at(i))
  55. return {};
  56. }
  57. return start + r;
  58. }
  59. // 6.1.4.1 StringIndexOf ( string, searchValue, fromIndex ), https://tc39.es/ecma262/#sec-stringindexof
  60. Optional<size_t> string_index_of(Utf16View const& string, Utf16View const& search_value, size_t from_index)
  61. {
  62. // 1. Let len be the length of string.
  63. size_t string_length = string.length_in_code_units();
  64. // 3. Let searchLen be the length of searchValue.
  65. size_t search_length = search_value.length_in_code_units();
  66. // 2. If searchValue is the empty String and fromIndex ≤ len, return fromIndex.
  67. if ((search_length == 0) && (from_index <= string_length))
  68. return from_index;
  69. // OPTIMIZATION: If the needle is longer than the haystack, don't bother searching :^)
  70. if (search_length > string_length)
  71. return {};
  72. // 4. For each integer i such that fromIndex ≤ i ≤ len - searchLen, in ascending order, do
  73. for (size_t i = from_index; i <= string_length - search_length; ++i) {
  74. // a. Let candidate be the substring of string from i to i + searchLen.
  75. auto candidate = string.substring_view(i, search_length);
  76. // b. If candidate is searchValue, return i.
  77. if (candidate == search_value)
  78. return i;
  79. }
  80. // 5. Return -1.
  81. return {};
  82. }
  83. // 7.2.9 Static Semantics: IsStringWellFormedUnicode ( string )
  84. static bool is_string_well_formed_unicode(Utf16View string)
  85. {
  86. // OPTIMIZATION: simdutf can do this much faster.
  87. return string.validate();
  88. }
  89. // 11.1.4 CodePointAt ( string, position ), https://tc39.es/ecma262/#sec-codepointat
  90. CodePoint code_point_at(Utf16View const& string, size_t position)
  91. {
  92. // 1. Let size be the length of string.
  93. // 2. Assert: position ≥ 0 and position < size.
  94. VERIFY(position < string.length_in_code_units());
  95. // 3. Let first be the code unit at index position within string.
  96. auto first = string.code_unit_at(position);
  97. // 4. Let cp be the code point whose numeric value is that of first.
  98. auto code_point = static_cast<u32>(first);
  99. // 5. If first is not a leading surrogate or trailing surrogate, then
  100. if (!is_unicode_surrogate(first)) {
  101. // a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: false }.
  102. return { false, code_point, 1 };
  103. }
  104. // 6. If first is a trailing surrogate or position + 1 = size, then
  105. if (Utf16View::is_low_surrogate(first) || (position + 1 == string.length_in_code_units())) {
  106. // a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }.
  107. return { true, code_point, 1 };
  108. }
  109. // 7. Let second be the code unit at index position + 1 within string.
  110. auto second = string.code_unit_at(position + 1);
  111. // 8. If second is not a trailing surrogate, then
  112. if (!Utf16View::is_low_surrogate(second)) {
  113. // a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }.
  114. return { true, code_point, 1 };
  115. }
  116. // 9. Set cp to UTF16SurrogatePairToCodePoint(first, second).
  117. code_point = Utf16View::decode_surrogate_pair(first, second);
  118. // 10. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 2, [[IsUnpairedSurrogate]]: false }.
  119. return { false, code_point, 2 };
  120. }
  121. StringPrototype::StringPrototype(Realm& realm)
  122. : StringObject(*PrimitiveString::create(realm.vm(), String {}), realm.intrinsics().object_prototype())
  123. {
  124. }
  125. void StringPrototype::initialize(Realm& realm)
  126. {
  127. auto& vm = this->vm();
  128. Base::initialize(realm);
  129. u8 attr = Attribute::Writable | Attribute::Configurable;
  130. // 22.1.3 Properties of the String Prototype Object, https://tc39.es/ecma262/#sec-properties-of-the-string-prototype-object
  131. define_native_function(realm, vm.names.at, at, 1, attr);
  132. define_native_function(realm, vm.names.charAt, char_at, 1, attr);
  133. define_native_function(realm, vm.names.charCodeAt, char_code_at, 1, attr);
  134. define_native_function(realm, vm.names.codePointAt, code_point_at, 1, attr);
  135. define_native_function(realm, vm.names.concat, concat, 1, attr);
  136. define_native_function(realm, vm.names.endsWith, ends_with, 1, attr);
  137. define_native_function(realm, vm.names.includes, includes, 1, attr);
  138. define_native_function(realm, vm.names.indexOf, index_of, 1, attr);
  139. define_native_function(realm, vm.names.isWellFormed, is_well_formed, 0, attr);
  140. define_native_function(realm, vm.names.lastIndexOf, last_index_of, 1, attr);
  141. define_native_function(realm, vm.names.localeCompare, locale_compare, 1, attr);
  142. define_native_function(realm, vm.names.match, match, 1, attr);
  143. define_native_function(realm, vm.names.matchAll, match_all, 1, attr);
  144. define_native_function(realm, vm.names.normalize, normalize, 0, attr);
  145. define_native_function(realm, vm.names.padEnd, pad_end, 1, attr);
  146. define_native_function(realm, vm.names.padStart, pad_start, 1, attr);
  147. define_native_function(realm, vm.names.repeat, repeat, 1, attr);
  148. define_native_function(realm, vm.names.replace, replace, 2, attr);
  149. define_native_function(realm, vm.names.replaceAll, replace_all, 2, attr);
  150. define_native_function(realm, vm.names.search, search, 1, attr);
  151. define_native_function(realm, vm.names.slice, slice, 2, attr);
  152. define_native_function(realm, vm.names.split, split, 2, attr);
  153. define_native_function(realm, vm.names.startsWith, starts_with, 1, attr);
  154. define_native_function(realm, vm.names.substring, substring, 2, attr);
  155. define_native_function(realm, vm.names.toLocaleLowerCase, to_locale_lowercase, 0, attr);
  156. define_native_function(realm, vm.names.toLocaleUpperCase, to_locale_uppercase, 0, attr);
  157. define_native_function(realm, vm.names.toLowerCase, to_lowercase, 0, attr);
  158. define_native_function(realm, vm.names.toString, to_string, 0, attr);
  159. define_native_function(realm, vm.names.toUpperCase, to_uppercase, 0, attr);
  160. define_native_function(realm, vm.names.toWellFormed, to_well_formed, 0, attr);
  161. define_native_function(realm, vm.names.trim, trim, 0, attr);
  162. define_native_function(realm, vm.names.trimEnd, trim_end, 0, attr);
  163. define_native_function(realm, vm.names.trimStart, trim_start, 0, attr);
  164. define_native_function(realm, vm.names.valueOf, value_of, 0, attr);
  165. define_native_function(realm, vm.well_known_symbol_iterator(), symbol_iterator, 0, attr);
  166. // B.2.2 Additional Properties of the String.prototype Object, https://tc39.es/ecma262/#sec-additional-properties-of-the-string.prototype-object
  167. define_native_function(realm, vm.names.substr, substr, 2, attr);
  168. define_native_function(realm, vm.names.anchor, anchor, 1, attr);
  169. define_native_function(realm, vm.names.big, big, 0, attr);
  170. define_native_function(realm, vm.names.blink, blink, 0, attr);
  171. define_native_function(realm, vm.names.bold, bold, 0, attr);
  172. define_native_function(realm, vm.names.fixed, fixed, 0, attr);
  173. define_native_function(realm, vm.names.fontcolor, fontcolor, 1, attr);
  174. define_native_function(realm, vm.names.fontsize, fontsize, 1, attr);
  175. define_native_function(realm, vm.names.italics, italics, 0, attr);
  176. define_native_function(realm, vm.names.link, link, 1, attr);
  177. define_native_function(realm, vm.names.small, small, 0, attr);
  178. define_native_function(realm, vm.names.strike, strike, 0, attr);
  179. define_native_function(realm, vm.names.sub, sub, 0, attr);
  180. define_native_function(realm, vm.names.sup, sup, 0, attr);
  181. define_direct_property(vm.names.trimLeft, get_without_side_effects(vm.names.trimStart), attr);
  182. define_direct_property(vm.names.trimRight, get_without_side_effects(vm.names.trimEnd), attr);
  183. }
  184. // thisStringValue ( value ), https://tc39.es/ecma262/#thisstringvalue
  185. static ThrowCompletionOr<GC::Ref<PrimitiveString>> this_string_value(VM& vm, Value value)
  186. {
  187. // 1. If value is a String, return value.
  188. if (value.is_string())
  189. return value.as_string();
  190. // 2. If value is an Object and value has a [[StringData]] internal slot, then
  191. if (value.is_object() && is<StringObject>(value.as_object())) {
  192. // a. Let s be value.[[StringData]].
  193. // b. Assert: s is a String.
  194. // c. Return s.
  195. return static_cast<StringObject&>(value.as_object()).primitive_string();
  196. }
  197. // 3. Throw a TypeError exception.
  198. return vm.throw_completion<TypeError>(ErrorType::NotAnObjectOfType, "String");
  199. }
  200. // 22.1.3.1 String.prototype.at ( index ), https://tc39.es/ecma262/#sec-string.prototype.at
  201. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::at)
  202. {
  203. // 1. Let O be ? ToObject(this value).
  204. auto string = TRY(utf16_string_from(vm));
  205. // 2. Let len be ? LengthOfArrayLike(O).
  206. auto length = string.length_in_code_units();
  207. // 3. Let relativeIndex be ? ToIntegerOrInfinity(index).
  208. auto relative_index = TRY(vm.argument(0).to_integer_or_infinity(vm));
  209. if (Value(relative_index).is_infinity())
  210. return js_undefined();
  211. Checked<size_t> index { 0 };
  212. // 4. If relativeIndex ≥ 0, then
  213. if (relative_index >= 0) {
  214. // a. Let k be relativeIndex.
  215. index += relative_index;
  216. }
  217. // 5. Else,
  218. else {
  219. // a. Let k be len + relativeIndex.
  220. index += length;
  221. index -= -relative_index;
  222. }
  223. // 6. If k < 0 or k ≥ len, return undefined.
  224. if (index.has_overflow() || index.value() >= length)
  225. return js_undefined();
  226. // 7. Return ? Get(O, ! ToString(𝔽(k))).
  227. return PrimitiveString::create(vm, Utf16String::create(string.substring_view(index.value(), 1)));
  228. }
  229. // 22.1.3.2 String.prototype.charAt ( pos ), https://tc39.es/ecma262/#sec-string.prototype.charat
  230. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::char_at)
  231. {
  232. // 1. Let O be ? RequireObjectCoercible(this value).
  233. // 2. Let S be ? ToString(O).
  234. auto string = TRY(utf16_string_from(vm));
  235. // 3. Let position be ? ToIntegerOrInfinity(pos).
  236. auto position = TRY(vm.argument(0).to_integer_or_infinity(vm));
  237. // 4. Let size be the length of S.
  238. // 5. If position < 0 or position ≥ size, return the empty String.
  239. if (position < 0 || position >= string.length_in_code_units())
  240. return PrimitiveString::create(vm, String {});
  241. // 6. Return the substring of S from position to position + 1.
  242. return PrimitiveString::create(vm, Utf16String::create(string.substring_view(position, 1)));
  243. }
  244. // 22.1.3.3 String.prototype.charCodeAt ( pos ), https://tc39.es/ecma262/#sec-string.prototype.charcodeat
  245. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::char_code_at)
  246. {
  247. // 1. Let O be ? RequireObjectCoercible(this value).
  248. // 2. Let S be ? ToString(O).
  249. auto string = TRY(utf16_string_from(vm));
  250. // 3. Let position be ? ToIntegerOrInfinity(pos).
  251. auto position = TRY(vm.argument(0).to_integer_or_infinity(vm));
  252. // 4. Let size be the length of S.
  253. // 5. If position < 0 or position ≥ size, return NaN.
  254. if (position < 0 || position >= string.length_in_code_units())
  255. return js_nan();
  256. // 6. Return the Number value for the numeric value of the code unit at index position within the String S.
  257. return Value(string.code_unit_at(position));
  258. }
  259. // 22.1.3.4 String.prototype.codePointAt ( pos ), https://tc39.es/ecma262/#sec-string.prototype.codepointat
  260. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::code_point_at)
  261. {
  262. // 1. Let O be ? RequireObjectCoercible(this value).
  263. // 2. Let S be ? ToString(O).
  264. auto string = TRY(utf16_string_from(vm));
  265. // 3. Let position be ? ToIntegerOrInfinity(pos).
  266. auto position = TRY(vm.argument(0).to_integer_or_infinity(vm));
  267. // 4. Let size be the length of S.
  268. // 5. If position < 0 or position ≥ size, return undefined.
  269. if (position < 0 || position >= string.length_in_code_units())
  270. return js_undefined();
  271. // 6. Let cp be CodePointAt(S, position).
  272. auto code_point = JS::code_point_at(string.view(), position);
  273. // 7. Return 𝔽(cp.[[CodePoint]]).
  274. return Value(code_point.code_point);
  275. }
  276. // 22.1.3.5 String.prototype.concat ( ...args ), https://tc39.es/ecma262/#sec-string.prototype.concat
  277. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::concat)
  278. {
  279. // 1. Let O be ? RequireObjectCoercible(this value).
  280. auto object = TRY(require_object_coercible(vm, vm.this_value()));
  281. // 2. Let S be ? ToString(O).
  282. auto string = TRY(object.to_primitive_string(vm));
  283. // 3. Let R be S.
  284. auto result = string;
  285. // 4. For each element next of args, do
  286. for (size_t i = 0; i < vm.argument_count(); ++i) {
  287. // a. Let nextString be ? ToString(next).
  288. auto next_string = TRY(vm.argument(i).to_primitive_string(vm));
  289. // b. Set R to the string-concatenation of R and nextString.
  290. result = PrimitiveString::create(vm, *result, *next_string);
  291. }
  292. // 5. Return R.
  293. return result;
  294. }
  295. // 22.1.3.7 String.prototype.endsWith ( searchString [ , endPosition ] ), https://tc39.es/ecma262/#sec-string.prototype.endswith
  296. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::ends_with)
  297. {
  298. auto search_string_value = vm.argument(0);
  299. auto end_position = vm.argument(1);
  300. // 1. Let O be ? RequireObjectCoercible(this value).
  301. // 2. Let S be ? ToString(O).
  302. auto string = TRY(utf16_string_from(vm));
  303. // Let isRegExp be ? IsRegExp(searchString).
  304. bool is_regexp = TRY(search_string_value.is_regexp(vm));
  305. // 4. If isRegExp is true, throw a TypeError exception.
  306. if (is_regexp)
  307. return vm.throw_completion<TypeError>(ErrorType::IsNotA, "searchString", "string, but a regular expression");
  308. // 5. Let searchStr be ? ToString(searchString).
  309. auto search_string = TRY(search_string_value.to_utf16_string(vm));
  310. // 6. Let len be the length of S.
  311. auto string_length = string.length_in_code_units();
  312. // 7. If endPosition is undefined, let pos be len; else let pos be ? ToIntegerOrInfinity(endPosition).
  313. size_t end = string_length;
  314. if (!end_position.is_undefined()) {
  315. auto position = TRY(end_position.to_integer_or_infinity(vm));
  316. // 8. Let end be the result of clamping pos between 0 and len.
  317. end = clamp(position, static_cast<double>(0), static_cast<double>(string_length));
  318. }
  319. // 9. Let searchLength be the length of searchStr.
  320. auto search_length = search_string.length_in_code_units();
  321. // 10. If searchLength = 0, return true.
  322. if (search_length == 0)
  323. return Value(true);
  324. // 12. If start < 0, return false.
  325. if (search_length > end)
  326. return Value(false);
  327. // 11. Let start be end - searchLength.
  328. size_t start = end - search_length;
  329. // 13. Let substring be the substring of S from start to end.
  330. auto substring_view = string.substring_view(start, end - start);
  331. // 14. If substring is searchStr, return true.
  332. // 15. Return false.
  333. return Value(substring_view == search_string.view());
  334. }
  335. // 22.1.3.8 String.prototype.includes ( searchString [ , position ] ), https://tc39.es/ecma262/#sec-string.prototype.includes
  336. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::includes)
  337. {
  338. auto search_string_value = vm.argument(0);
  339. auto position = vm.argument(1);
  340. // 1. Let O be ? RequireObjectCoercible(this value).
  341. // 2. Let S be ? ToString(O).
  342. auto string = TRY(utf16_string_from(vm));
  343. // 3. Let isRegExp be ? IsRegExp(searchString).
  344. bool is_regexp = TRY(search_string_value.is_regexp(vm));
  345. // 4. If isRegExp is true, throw a TypeError exception.
  346. if (is_regexp)
  347. return vm.throw_completion<TypeError>(ErrorType::IsNotA, "searchString", "string, but a regular expression");
  348. // 5. Let searchStr be ? ToString(searchString).
  349. auto search_string = TRY(search_string_value.to_utf16_string(vm));
  350. size_t start = 0;
  351. if (!position.is_undefined()) {
  352. // 6. Let pos be ? ToIntegerOrInfinity(position).
  353. // 7. Assert: If position is undefined, then pos is 0.
  354. auto pos = TRY(position.to_integer_or_infinity(vm));
  355. // 8. Let len be the length of S.
  356. // 9. Let start be the result of clamping pos between 0 and len.
  357. start = clamp(pos, static_cast<double>(0), static_cast<double>(string.length_in_code_units()));
  358. }
  359. // 10. Let index be StringIndexOf(S, searchStr, start).
  360. auto index = string_index_of(string.view(), search_string.view(), start);
  361. // 11. If index ≠ -1, return true.
  362. // 12. Return false.
  363. return Value(index.has_value());
  364. }
  365. // 22.1.3.9 String.prototype.indexOf ( searchString [ , position ] ), https://tc39.es/ecma262/#sec-string.prototype.indexof
  366. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::index_of)
  367. {
  368. // 1. Let O be ? RequireObjectCoercible(this value).
  369. // 2. Let S be ? ToString(O).
  370. auto string = TRY(utf16_string_from(vm));
  371. // 3. Let searchStr be ? ToString(searchString).
  372. auto search_string = TRY(vm.argument(0).to_utf16_string(vm));
  373. auto utf16_string_view = string.view();
  374. auto utf16_search_view = search_string.view();
  375. size_t start = 0;
  376. if (vm.argument_count() > 1) {
  377. // 4. Let pos be ? ToIntegerOrInfinity(position).
  378. // 5. Assert: If position is undefined, then pos is 0.
  379. auto position = TRY(vm.argument(1).to_integer_or_infinity(vm));
  380. // 6. Let len be the length of S.
  381. // 7. Let start be the result of clamping pos between 0 and len.
  382. start = clamp(position, static_cast<double>(0), static_cast<double>(utf16_string_view.length_in_code_units()));
  383. }
  384. // 8. Return 𝔽(StringIndexOf(S, searchStr, start)).
  385. auto index = string_index_of(utf16_string_view, utf16_search_view, start);
  386. return index.has_value() ? Value(*index) : Value(-1);
  387. }
  388. // 22.1.3.10 String.prototype.isWellFormed ( ), https://tc39.es/ecma262/#sec-string.prototype.iswellformed
  389. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::is_well_formed)
  390. {
  391. // 1. Let O be ? RequireObjectCoercible(this value).
  392. // 2. Let S be ? ToString(O).
  393. auto string = TRY(utf16_string_from(vm));
  394. // 3. Return IsStringWellFormedUnicode(S).
  395. return is_string_well_formed_unicode(string.view());
  396. }
  397. // 22.1.3.11 String.prototype.lastIndexOf ( searchString [ , position ] ), https://tc39.es/ecma262/#sec-string.prototype.lastindexof
  398. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::last_index_of)
  399. {
  400. // 1. Let O be ? RequireObjectCoercible(this value).
  401. // 2. Let S be ? ToString(O).
  402. auto string = TRY(utf16_string_from(vm));
  403. // 3. Let searchStr be ? ToString(searchString).
  404. auto search_string = TRY(vm.argument(0).to_utf16_string(vm));
  405. // 4. Let numPos be ? ToNumber(position).
  406. // 5. Assert: If position is undefined, then numPos is NaN.
  407. auto position = TRY(vm.argument(1).to_number(vm));
  408. // 6. If numPos is NaN, let pos be +∞; otherwise, let pos be ! ToIntegerOrInfinity(numPos).
  409. double pos = position.is_nan() ? static_cast<double>(INFINITY) : MUST(position.to_integer_or_infinity(vm));
  410. // 7. Let len be the length of S.
  411. auto string_length = string.length_in_code_units();
  412. // 8. Let searchLen be the length of searchStr.
  413. auto search_length = search_string.length_in_code_units();
  414. // 9. Let start be the result of clamping pos between 0 and len - searchLen.
  415. size_t start = clamp(pos, static_cast<double>(0), static_cast<double>(string_length));
  416. Optional<size_t> last_index;
  417. // 10. If searchStr is the empty String, return 𝔽(start).
  418. // 11. For each integer i such that 0 ≤ i ≤ start, in descending order, do
  419. for (size_t k = 0; (k <= start) && (k + search_length <= string_length); ++k) {
  420. bool is_match = true;
  421. // a. Let candidate be the substring of S from i to i + searchLen.
  422. for (size_t j = 0; j < search_length; ++j) {
  423. if (string.code_unit_at(k + j) != search_string.code_unit_at(j)) {
  424. is_match = false;
  425. break;
  426. }
  427. }
  428. // b. If candidate is searchStr, return 𝔽(i).
  429. if (is_match)
  430. last_index = k;
  431. }
  432. // 12. Return -1𝔽.
  433. return last_index.has_value() ? Value(*last_index) : Value(-1);
  434. }
  435. // 22.1.3.12 String.prototype.localeCompare ( that [ , reserved1 [ , reserved2 ] ] ), https://tc39.es/ecma262/#sec-string.prototype.localecompare
  436. // 19.1.1 String.prototype.localeCompare ( that [ , locales [ , options ] ] ), https://tc39.es/ecma402/#sup-String.prototype.localeCompare
  437. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::locale_compare)
  438. {
  439. auto& realm = *vm.current_realm();
  440. // 1. Let O be ? RequireObjectCoercible(this value).
  441. auto object = TRY(require_object_coercible(vm, vm.this_value()));
  442. // 2. Let S be ? ToString(O).
  443. auto string = TRY(object.to_string(vm));
  444. // 3. Let thatValue be ? ToString(that).
  445. auto that_value = TRY(vm.argument(0).to_string(vm));
  446. // 4. Let collator be ? Construct(%Collator%, « locales, options »).
  447. auto collator = TRY(construct(vm, realm.intrinsics().intl_collator_constructor(), vm.argument(1), vm.argument(2)));
  448. // 5. Return CompareStrings(collator, S, thatValue).
  449. return Intl::compare_strings(static_cast<Intl::Collator const&>(*collator), string, that_value);
  450. }
  451. // 22.1.3.13 String.prototype.match ( regexp ), https://tc39.es/ecma262/#sec-string.prototype.match
  452. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::match)
  453. {
  454. // 1. Let O be ? RequireObjectCoercible(this value).
  455. auto this_object = TRY(require_object_coercible(vm, vm.this_value()));
  456. // 2. If regexp is neither undefined nor null, then
  457. auto regexp = vm.argument(0);
  458. if (!regexp.is_nullish()) {
  459. // a. Let matcher be ? GetMethod(regexp, @@match).
  460. auto matcher = TRY(regexp.get_method(vm, vm.well_known_symbol_match()));
  461. // b. If matcher is not undefined, then
  462. if (matcher) {
  463. // i. Return ? Call(matcher, regexp, « O »).
  464. return TRY(call(vm, *matcher, regexp, this_object));
  465. }
  466. }
  467. // 3. Let S be ? ToString(O).
  468. auto string = TRY(this_object.to_utf16_string(vm));
  469. // 4. Let rx be ? RegExpCreate(regexp, undefined).
  470. auto rx = TRY(regexp_create(vm, regexp, js_undefined()));
  471. // 5. Return ? Invoke(rx, @@match, « S »).
  472. return TRY(Value(rx).invoke(vm, vm.well_known_symbol_match(), PrimitiveString::create(vm, move(string))));
  473. }
  474. // 22.1.3.14 String.prototype.matchAll ( regexp ), https://tc39.es/ecma262/#sec-string.prototype.matchall
  475. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::match_all)
  476. {
  477. auto regexp = vm.argument(0);
  478. // 1. Let O be ? RequireObjectCoercible(this value).
  479. auto this_object = TRY(require_object_coercible(vm, vm.this_value()));
  480. // 2. If regexp is neither undefined nor null, then
  481. if (!regexp.is_nullish()) {
  482. // a. Let isRegExp be ? IsRegExp(regexp).
  483. auto is_regexp = TRY(regexp.is_regexp(vm));
  484. // b. If isRegExp is true, then
  485. if (is_regexp) {
  486. // i. Let flags be ? Get(regexp, "flags").
  487. auto flags = TRY(regexp.as_object().get("flags"));
  488. // ii. Perform ? RequireObjectCoercible(flags).
  489. auto flags_object = TRY(require_object_coercible(vm, flags));
  490. // iii. If ? ToString(flags) does not contain "g", throw a TypeError exception.
  491. auto flags_string = TRY(flags_object.to_string(vm));
  492. if (!flags_string.contains('g'))
  493. return vm.throw_completion<TypeError>(ErrorType::StringNonGlobalRegExp);
  494. }
  495. // c. Let matcher be ? GetMethod(regexp, @@matchAll).
  496. auto matcher = TRY(regexp.get_method(vm, vm.well_known_symbol_match_all()));
  497. // d. If matcher is not undefined, then
  498. if (matcher) {
  499. // i. Return ? Call(matcher, regexp, « O »).
  500. return TRY(call(vm, *matcher, regexp, this_object));
  501. }
  502. }
  503. // 3. Let S be ? ToString(O).
  504. auto string = TRY(this_object.to_utf16_string(vm));
  505. // 4. Let rx be ? RegExpCreate(regexp, "g").
  506. auto rx = TRY(regexp_create(vm, regexp, PrimitiveString::create(vm, "g"_string)));
  507. // 5. Return ? Invoke(rx, @@matchAll, « S »).
  508. return TRY(Value(rx).invoke(vm, vm.well_known_symbol_match_all(), PrimitiveString::create(vm, move(string))));
  509. }
  510. // 22.1.3.15 String.prototype.normalize ( [ form ] ), https://tc39.es/ecma262/#sec-string.prototype.normalize
  511. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::normalize)
  512. {
  513. // 1. Let O be ? RequireObjectCoercible(this value).
  514. // 2. Let S be ? ToString(O).
  515. auto string = TRY(utf8_string_from(vm));
  516. String form;
  517. // 3. If form is undefined, let f be "NFC".
  518. if (auto form_value = vm.argument(0); form_value.is_undefined()) {
  519. form = "NFC"_string;
  520. }
  521. // 4. Else, let f be ? ToString(form).
  522. else {
  523. form = TRY(form_value.to_string(vm));
  524. }
  525. // 5. If f is not one of "NFC", "NFD", "NFKC", or "NFKD", throw a RangeError exception.
  526. if (!form.is_one_of("NFC"sv, "NFD"sv, "NFKC"sv, "NFKD"sv))
  527. return vm.throw_completion<RangeError>(ErrorType::InvalidNormalizationForm, form);
  528. // 6. Let ns be the String value that is the result of normalizing S into the normalization form named by f as specified in https://unicode.org/reports/tr15/.
  529. auto unicode_form = Unicode::normalization_form_from_string(form);
  530. auto ns = Unicode::normalize(string, unicode_form);
  531. // 7. Return ns.
  532. return PrimitiveString::create(vm, move(ns));
  533. }
  534. enum class PadPlacement {
  535. Start,
  536. End,
  537. };
  538. // 22.1.3.17.1 StringPad ( O, maxLength, fillString, placement ), https://tc39.es/ecma262/#sec-stringpad
  539. static ThrowCompletionOr<Value> pad_string(VM& vm, Utf16String string, Value max_length, Value fill_string, PadPlacement placement)
  540. {
  541. // 1. Let S be ? ToString(O).
  542. // 2. Let intMaxLength be ℝ(? ToLength(maxLength)).
  543. auto int_max_length = TRY(max_length.to_length(vm));
  544. // 3. Let stringLength be the length of S.
  545. auto string_length = string.length_in_code_units();
  546. // 4. If intMaxLength ≤ stringLength, return S.
  547. if (int_max_length <= string_length)
  548. return PrimitiveString::create(vm, move(string));
  549. // 5. If fillString is undefined, let filler be the String value consisting solely of the code unit 0x0020 (SPACE).
  550. auto filler = Utf16String::create(Utf16Data { 0x20 });
  551. if (!fill_string.is_undefined()) {
  552. // 6. Else, let filler be ? ToString(fillString).
  553. filler = TRY(fill_string.to_utf16_string(vm));
  554. // 7. If filler is the empty String, return S.
  555. if (filler.is_empty())
  556. return PrimitiveString::create(vm, move(string));
  557. }
  558. // 8. Let fillLen be intMaxLength - stringLength.
  559. auto fill_length = int_max_length - string_length;
  560. StringBuilder truncated_string_filler_builder;
  561. auto fill_code_units = filler.length_in_code_units();
  562. for (size_t i = 0; i < fill_length / fill_code_units; ++i)
  563. truncated_string_filler_builder.append(filler.view());
  564. // 9. Let truncatedStringFiller be the String value consisting of repeated concatenations of filler truncated to length fillLen.
  565. truncated_string_filler_builder.append(filler.substring_view(0, fill_length % fill_code_units));
  566. auto truncated_string_filler = MUST(truncated_string_filler_builder.to_string());
  567. // 10. If placement is start, return the string-concatenation of truncatedStringFiller and S.
  568. // 11. Else, return the string-concatenation of S and truncatedStringFiller.
  569. auto formatted = placement == PadPlacement::Start
  570. ? MUST(String::formatted("{}{}", truncated_string_filler, string.view()))
  571. : MUST(String::formatted("{}{}", string.view(), truncated_string_filler));
  572. return PrimitiveString::create(vm, move(formatted));
  573. }
  574. // 22.1.3.16 String.prototype.padEnd ( maxLength [ , fillString ] ), https://tc39.es/ecma262/#sec-string.prototype.padend
  575. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::pad_end)
  576. {
  577. auto max_length = vm.argument(0);
  578. auto fill_string = vm.argument(1);
  579. // 1. Let O be ? RequireObjectCoercible(this value).
  580. auto string = TRY(utf16_string_from(vm));
  581. // 2. Return ? StringPad(O, maxLength, fillString, end).
  582. return pad_string(vm, move(string), max_length, fill_string, PadPlacement::End);
  583. }
  584. // 22.1.3.17 String.prototype.padStart ( maxLength [ , fillString ] ), https://tc39.es/ecma262/#sec-string.prototype.padstart
  585. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::pad_start)
  586. {
  587. auto max_length = vm.argument(0);
  588. auto fill_string = vm.argument(1);
  589. // 1. Let O be ? RequireObjectCoercible(this value).
  590. auto string = TRY(utf16_string_from(vm));
  591. // 2. Return ? StringPad(O, maxLength, fillString, start).
  592. return pad_string(vm, move(string), max_length, fill_string, PadPlacement::Start);
  593. }
  594. // 22.1.3.18 String.prototype.repeat ( count ), https://tc39.es/ecma262/#sec-string.prototype.repeat
  595. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::repeat)
  596. {
  597. // 1. Let O be ? RequireObjectCoercible(this value).
  598. // 2. Let S be ? ToString(O).
  599. auto string = TRY(utf8_string_from(vm));
  600. // 3. Let n be ? ToIntegerOrInfinity(count).
  601. auto n = TRY(vm.argument(0).to_integer_or_infinity(vm));
  602. // 4. If n < 0 or n = +∞, throw a RangeError exception.
  603. if (n < 0)
  604. return vm.throw_completion<RangeError>(ErrorType::StringRepeatCountMustBe, "positive");
  605. if (Value(n).is_positive_infinity())
  606. return vm.throw_completion<RangeError>(ErrorType::StringRepeatCountMustBe, "finite");
  607. // 5. If n = 0, return the empty String.
  608. if (n == 0)
  609. return PrimitiveString::create(vm, String {});
  610. // OPTIMIZATION: If the string is empty, the result will be empty as well.
  611. if (string.is_empty())
  612. return PrimitiveString::create(vm, String {});
  613. auto repeated = String::repeated(string, n);
  614. if (repeated.is_error())
  615. return vm.throw_completion<RangeError>(ErrorType::StringRepeatCountMustNotOverflow);
  616. // 6. Return the String value that is made from n copies of S appended together.
  617. return PrimitiveString::create(vm, repeated.release_value());
  618. }
  619. // 22.1.3.19 String.prototype.replace ( searchValue, replaceValue ), https://tc39.es/ecma262/#sec-string.prototype.replace
  620. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::replace)
  621. {
  622. auto search_value = vm.argument(0);
  623. auto replace_value = vm.argument(1);
  624. // 1. Let O be ? RequireObjectCoercible(this value).
  625. auto this_object = TRY(require_object_coercible(vm, vm.this_value()));
  626. // 2. If searchValue is neither undefined nor null, then
  627. if (!search_value.is_nullish()) {
  628. // a. Let replacer be ? GetMethod(searchValue, @@replace).
  629. auto replacer = TRY(search_value.get_method(vm, vm.well_known_symbol_replace()));
  630. // b. If replacer is not undefined, then
  631. if (replacer) {
  632. // i. Return ? Call(replacer, searchValue, « O, replaceValue »).
  633. return TRY(call(vm, *replacer, search_value, this_object, replace_value));
  634. }
  635. }
  636. // 3. Let string be ? ToString(O).
  637. auto string = TRY(this_object.to_utf16_string(vm));
  638. // 4. Let searchString be ? ToString(searchValue).
  639. auto search_string = TRY(search_value.to_utf16_string(vm));
  640. // 5. Let functionalReplace be IsCallable(replaceValue).
  641. // 6. If functionalReplace is false, then
  642. if (!replace_value.is_function()) {
  643. // a. Set replaceValue to ? ToString(replaceValue).
  644. auto replace_string = TRY(replace_value.to_utf16_string(vm));
  645. replace_value = PrimitiveString::create(vm, move(replace_string));
  646. }
  647. // 7. Let searchLength be the length of searchString.
  648. auto search_length = search_string.length_in_code_units();
  649. // 8. Let position be StringIndexOf(string, searchString, 0).
  650. auto position = string_index_of(string.view(), search_string.view(), 0);
  651. // 9. If position = -1, return string.
  652. if (!position.has_value())
  653. return PrimitiveString::create(vm, move(string));
  654. // 10. Let preceding be the substring of string from 0 to position.
  655. auto preceding = string.substring_view(0, *position);
  656. String replacement;
  657. // 11. Let following be the substring of string from position + searchLength.
  658. auto following = string.substring_view(*position + search_length);
  659. // 12. If functionalReplace is true, then
  660. if (replace_value.is_function()) {
  661. // a. Let replacement be ? ToString(? Call(replaceValue, undefined, « searchString, 𝔽(position), string »)).
  662. auto result = TRY(call(vm, replace_value.as_function(), js_undefined(), PrimitiveString::create(vm, search_string), Value(*position), PrimitiveString::create(vm, string)));
  663. replacement = TRY(result.to_string(vm));
  664. }
  665. // 13. Else,
  666. else {
  667. // a. Assert: replaceValue is a String.
  668. VERIFY(replace_value.is_string());
  669. // b. Let captures be a new empty List.
  670. Span<Value> captures;
  671. // c. Let replacement be ! GetSubstitution(searchString, string, position, captures, undefined, replaceValue).
  672. replacement = TRY(get_substitution(vm, search_string.view(), string.view(), *position, captures, js_undefined(), replace_value));
  673. }
  674. // 14. Return the string-concatenation of preceding, replacement, and following.
  675. StringBuilder builder;
  676. builder.append(preceding);
  677. builder.append(replacement);
  678. builder.append(following);
  679. return PrimitiveString::create(vm, MUST(builder.to_string()));
  680. }
  681. // 22.1.3.20 String.prototype.replaceAll ( searchValue, replaceValue ), https://tc39.es/ecma262/#sec-string.prototype.replaceall
  682. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::replace_all)
  683. {
  684. auto search_value = vm.argument(0);
  685. auto replace_value = vm.argument(1);
  686. // 1. Let O be ? RequireObjectCoercible(this value).
  687. auto this_object = TRY(require_object_coercible(vm, vm.this_value()));
  688. // 2. If searchValue is neither undefined nor null, then
  689. if (!search_value.is_nullish()) {
  690. // a. Let isRegExp be ? IsRegExp(searchValue).
  691. bool is_regexp = TRY(search_value.is_regexp(vm));
  692. // b. If isRegExp is true, then
  693. if (is_regexp) {
  694. // i. Let flags be ? Get(searchValue, "flags").
  695. auto flags = TRY(search_value.as_object().get(vm.names.flags));
  696. // ii. Perform ? RequireObjectCoercible(flags).
  697. auto flags_object = TRY(require_object_coercible(vm, flags));
  698. // iii. If ? ToString(flags) does not contain "g", throw a TypeError exception.
  699. if (!TRY(flags_object.to_string(vm)).contains('g'))
  700. return vm.throw_completion<TypeError>(ErrorType::StringNonGlobalRegExp);
  701. }
  702. // c. Let replacer be ? GetMethod(searchValue, @@replace).
  703. auto replacer = TRY(search_value.get_method(vm, vm.well_known_symbol_replace()));
  704. // d. If replacer is not undefined, then
  705. if (replacer) {
  706. // i. Return ? Call(replacer, searchValue, « O, replaceValue »).
  707. return TRY(call(vm, *replacer, search_value, this_object, replace_value));
  708. }
  709. }
  710. // 3. Let string be ? ToString(O).
  711. auto string = TRY(this_object.to_utf16_string(vm));
  712. // 4. Let searchString be ? ToString(searchValue).
  713. auto search_string = TRY(search_value.to_utf16_string(vm));
  714. // 5. Let functionalReplace be IsCallable(replaceValue).
  715. // 6. If functionalReplace is false, then
  716. if (!replace_value.is_function()) {
  717. // a. Set replaceValue to ? ToString(replaceValue).
  718. auto replace_string = TRY(replace_value.to_utf16_string(vm));
  719. replace_value = PrimitiveString::create(vm, move(replace_string));
  720. }
  721. // 7. Let searchLength be the length of searchString.
  722. auto search_length = search_string.length_in_code_units();
  723. // 8. Let advanceBy be max(1, searchLength).
  724. size_t advance_by = max(1u, search_length);
  725. // 9. Let matchPositions be a new empty List.
  726. Vector<size_t> match_positions;
  727. // 10. Let position be StringIndexOf(string, searchString, 0).
  728. auto position = string_index_of(string.view(), search_string.view(), 0);
  729. // 11. Repeat, while position ≠ -1,
  730. while (position.has_value()) {
  731. // a. Append position to matchPositions.
  732. match_positions.append(*position);
  733. // b. Set position to StringIndexOf(string, searchString, position + advanceBy).
  734. position = string_index_of(string.view(), search_string.view(), *position + advance_by);
  735. }
  736. // 12. Let endOfLastMatch be 0.
  737. size_t end_of_last_match = 0;
  738. // 13. Let result be the empty String.
  739. StringBuilder result;
  740. // 14. For each element p of matchPositions, do
  741. for (auto position : match_positions) {
  742. // a. Let preserved be the substring of string from endOfLastMatch to p.
  743. auto preserved = string.substring_view(end_of_last_match, position - end_of_last_match);
  744. String replacement;
  745. // b. If functionalReplace is true, then
  746. if (replace_value.is_function()) {
  747. // i. Let replacement be ? ToString(? Call(replaceValue, undefined, « searchString, 𝔽(p), string »)).
  748. replacement = TRY(TRY(call(vm, replace_value.as_function(), js_undefined(), PrimitiveString::create(vm, search_string), Value(position), PrimitiveString::create(vm, string))).to_string(vm));
  749. }
  750. // c. Else,
  751. else {
  752. // i. Assert: replaceValue is a String.
  753. // ii. Let captures be a new empty List.
  754. // iii. Let replacement be ! GetSubstitution(searchString, string, p, captures, undefined, replaceValue).
  755. replacement = TRY(get_substitution(vm, search_string.view(), string.view(), position, {}, js_undefined(), replace_value));
  756. }
  757. // d. Set result to the string-concatenation of result, preserved, and replacement.
  758. result.append(preserved);
  759. result.append(replacement);
  760. // e. Set endOfLastMatch to p + searchLength.
  761. end_of_last_match = position + search_length;
  762. }
  763. auto string_length = string.length_in_code_units();
  764. // 15. If endOfLastMatch < the length of string, then
  765. if (end_of_last_match < string_length) {
  766. // a. Set result to the string-concatenation of result and the substring of string from endOfLastMatch.
  767. result.append(string.substring_view(end_of_last_match));
  768. }
  769. // 16. Return result.
  770. return PrimitiveString::create(vm, MUST(result.to_string()));
  771. }
  772. // 22.1.3.21 String.prototype.search ( regexp ), https://tc39.es/ecma262/#sec-string.prototype.search
  773. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::search)
  774. {
  775. auto regexp = vm.argument(0);
  776. // 1. Let O be ? RequireObjectCoercible(this value).
  777. auto this_object = TRY(require_object_coercible(vm, vm.this_value()));
  778. // 2. If regexp is neither undefined nor null, then
  779. if (!regexp.is_nullish()) {
  780. // a. Let searcher be ? GetMethod(regexp, @@search).
  781. auto searcher = TRY(regexp.get_method(vm, vm.well_known_symbol_search()));
  782. // b. If searcher is not undefined, then
  783. if (searcher) {
  784. // i. Return ? Call(searcher, regexp, « O »).
  785. return TRY(call(vm, *searcher, regexp, this_object));
  786. }
  787. }
  788. // 3. Let string be ? ToString(O).
  789. auto string = TRY(this_object.to_utf16_string(vm));
  790. // 4. Let rx be ? RegExpCreate(regexp, undefined).
  791. auto rx = TRY(regexp_create(vm, regexp, js_undefined()));
  792. // 5. Return ? Invoke(rx, @@search, « string »).
  793. return TRY(Value(rx).invoke(vm, vm.well_known_symbol_search(), PrimitiveString::create(vm, move(string))));
  794. }
  795. // 22.1.3.22 String.prototype.slice ( start, end ), https://tc39.es/ecma262/#sec-string.prototype.slice
  796. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::slice)
  797. {
  798. auto start = vm.argument(0);
  799. auto end = vm.argument(1);
  800. // 1. Let O be ? RequireObjectCoercible(this value).
  801. // 2. Let S be ? ToString(O).
  802. auto string = TRY(utf16_string_from(vm));
  803. // 3. Let len be the length of S.
  804. auto string_length = static_cast<double>(string.length_in_code_units());
  805. // 4. Let intStart be ? ToIntegerOrInfinity(start).
  806. auto int_start = TRY(start.to_integer_or_infinity(vm));
  807. // 5. If intStart = -∞, let from be 0.
  808. if (Value(int_start).is_negative_infinity())
  809. int_start = 0;
  810. // 6. Else if intStart < 0, let from be max(len + intStart, 0).
  811. else if (int_start < 0)
  812. int_start = max(string_length + int_start, 0);
  813. // 7. Else, let from be min(intStart, len).
  814. else
  815. int_start = min(int_start, string_length);
  816. // 8. If end is undefined, let intEnd be len; else let intEnd be ? ToIntegerOrInfinity(end).
  817. auto int_end = string_length;
  818. if (!end.is_undefined()) {
  819. int_end = TRY(end.to_integer_or_infinity(vm));
  820. // 9. If intEnd = -∞, let to be 0.
  821. if (Value(int_end).is_negative_infinity())
  822. int_end = 0;
  823. // 10. Else if intEnd < 0, let to be max(len + intEnd, 0).
  824. else if (int_end < 0)
  825. int_end = max(string_length + int_end, 0);
  826. // 11. Else, let to be min(intEnd, len).
  827. else
  828. int_end = min(int_end, string_length);
  829. }
  830. // 12. If from ≥ to, return the empty String.
  831. if (int_start >= int_end)
  832. return PrimitiveString::create(vm, String {});
  833. // 13. Return the substring of S from from to to.
  834. return PrimitiveString::create(vm, Utf16String::create(string.substring_view(int_start, int_end - int_start)));
  835. }
  836. // 22.1.3.23 String.prototype.split ( separator, limit ), https://tc39.es/ecma262/#sec-string.prototype.split
  837. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::split)
  838. {
  839. auto& realm = *vm.current_realm();
  840. auto separator_argument = vm.argument(0);
  841. auto limit_argument = vm.argument(1);
  842. // 1. Let O be ? RequireObjectCoercible(this value).
  843. auto object = TRY(require_object_coercible(vm, vm.this_value()));
  844. // 2. If separator is neither undefined nor null, then
  845. if (!separator_argument.is_nullish()) {
  846. // a. Let splitter be ? GetMethod(separator, @@split).
  847. auto splitter = TRY(separator_argument.get_method(vm, vm.well_known_symbol_split()));
  848. // b. If splitter is not undefined, then
  849. if (splitter) {
  850. // i. Return ? Call(splitter, separator, « O, limit »).
  851. return TRY(call(vm, *splitter, separator_argument, object, limit_argument));
  852. }
  853. }
  854. // 3. Let S be ? ToString(O).
  855. auto string = TRY(object.to_utf16_string(vm));
  856. // 11. Let substrings be a new empty List.
  857. auto array = MUST(Array::create(realm, 0));
  858. size_t array_length = 0;
  859. // 4. If limit is undefined, let lim be 232 - 1; else let lim be ℝ(? ToUint32(limit)).
  860. auto limit = NumericLimits<u32>::max();
  861. if (!limit_argument.is_undefined())
  862. limit = TRY(limit_argument.to_u32(vm));
  863. // 5. Let R be ? ToString(separator).
  864. auto separator = TRY(separator_argument.to_utf16_string(vm));
  865. // 6. If lim = 0, then
  866. if (limit == 0) {
  867. // a. Return CreateArrayFromList(« »).
  868. return array;
  869. }
  870. auto string_length = string.length_in_code_units();
  871. // 7. If separator is undefined, then
  872. if (separator_argument.is_undefined()) {
  873. // a. Return CreateArrayFromList(« S »).
  874. MUST(array->create_data_property_or_throw(0, PrimitiveString::create(vm, move(string))));
  875. return array;
  876. }
  877. // 8. Let separatorLength be the length of R.
  878. auto separator_length = separator.length_in_code_units();
  879. // 10. If S is the empty String, return CreateArrayFromList(« S »).
  880. if (string_length == 0) {
  881. if (separator_length > 0)
  882. MUST(array->create_data_property_or_throw(0, PrimitiveString::create(vm, move(string))));
  883. return array;
  884. }
  885. // 12. Let i be 0.
  886. size_t start = 0;
  887. // 13. Let j be StringIndexOf(S, R, 0).
  888. auto position = start;
  889. // 14. Repeat, while j ≠ -1,
  890. while (position != string_length) {
  891. // a. Let T be the substring of S from i to j.
  892. auto match = split_match(string.view(), position, separator.view());
  893. if (!match.has_value() || match.value() == start) {
  894. ++position;
  895. continue;
  896. }
  897. auto segment = string.substring_view(start, position - start);
  898. // b. Append T to substrings.
  899. MUST(array->create_data_property_or_throw(array_length, PrimitiveString::create(vm, Utf16String::create(segment))));
  900. ++array_length;
  901. // c. If the number of elements in substrings is lim, return CreateArrayFromList(substrings).
  902. if (array_length == limit)
  903. return array;
  904. // d. Set i to j + separatorLength.
  905. start = match.value();
  906. // e. Set j to StringIndexOf(S, R, i).
  907. position = start;
  908. }
  909. // 15. Let T be the substring of S from i.
  910. auto rest = string.substring_view(start);
  911. // 16. Append T to substrings.
  912. MUST(array->create_data_property_or_throw(array_length, PrimitiveString::create(vm, Utf16String::create(rest))));
  913. // 17. Return CreateArrayFromList(substrings).
  914. return array;
  915. }
  916. // 22.1.3.24 String.prototype.startsWith ( searchString [ , position ] ), https://tc39.es/ecma262/#sec-string.prototype.startswith
  917. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::starts_with)
  918. {
  919. auto search_string_value = vm.argument(0);
  920. auto position = vm.argument(1);
  921. // 1. Let O be ? RequireObjectCoercible(this value).
  922. // 2. Let S be ? ToString(O).
  923. auto string = TRY(utf16_string_from(vm));
  924. // 3. Let isRegExp be ? IsRegExp(searchString).
  925. bool is_regexp = TRY(search_string_value.is_regexp(vm));
  926. // 4. If isRegExp is true, throw a TypeError exception.
  927. if (is_regexp)
  928. return vm.throw_completion<TypeError>(ErrorType::IsNotA, "searchString", "string, but a regular expression");
  929. // 5. Let searchStr be ? ToString(searchString).
  930. auto search_string = TRY(search_string_value.to_utf16_string(vm));
  931. // 6. Let len be the length of S.
  932. auto string_length = string.length_in_code_units();
  933. size_t start = 0;
  934. // 7. If position is undefined, let pos be 0; else let pos be ? ToIntegerOrInfinity(position).
  935. if (!position.is_undefined()) {
  936. auto pos = TRY(position.to_integer_or_infinity(vm));
  937. // 8. Let start be the result of clamping pos between 0 and len.
  938. start = clamp(pos, static_cast<double>(0), static_cast<double>(string_length));
  939. }
  940. // 9. Let searchLength be the length of searchStr.
  941. auto search_length = search_string.length_in_code_units();
  942. // 10. If searchLength = 0, return true.
  943. if (search_length == 0)
  944. return Value(true);
  945. // 11. Let end be start + searchLength.
  946. size_t end = start + search_length;
  947. // 12. If end > len, return false.
  948. if (end > string_length)
  949. return Value(false);
  950. // 13. Let substring be the substring of S from start to end.
  951. auto substring_view = string.substring_view(start, end - start);
  952. // 14. If substring is searchStr, return true.
  953. // 15. Return false.
  954. return Value(substring_view == search_string.view());
  955. }
  956. // 22.1.3.25 String.prototype.substring ( start, end ), https://tc39.es/ecma262/#sec-string.prototype.substring
  957. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::substring)
  958. {
  959. // 1. Let O be ? RequireObjectCoercible(this value).
  960. // 2. Let S be ? ToString(O).
  961. auto string = TRY(utf16_string_from(vm));
  962. // 3. Let len be the length of S.
  963. auto string_length = static_cast<double>(string.length_in_code_units());
  964. // 4. Let intStart be ? ToIntegerOrInfinity(start).
  965. auto start = TRY(vm.argument(0).to_integer_or_infinity(vm));
  966. // 5. If end is undefined, let intEnd be len; else let intEnd be ? ToIntegerOrInfinity(end).
  967. auto end = string_length;
  968. if (!vm.argument(1).is_undefined())
  969. end = TRY(vm.argument(1).to_integer_or_infinity(vm));
  970. // 6. Let finalStart be the result of clamping intStart between 0 and len.
  971. size_t final_start = clamp(start, static_cast<double>(0), string_length);
  972. // 7. Let finalEnd be the result of clamping intEnd between 0 and len.
  973. size_t final_end = clamp(end, static_cast<double>(0), string_length);
  974. // 8. Let from be min(finalStart, finalEnd).
  975. size_t from = min(final_start, final_end);
  976. // 9. Let to be max(finalStart, finalEnd).
  977. size_t to = max(final_start, final_end);
  978. // 10. Return the substring of S from from to to.
  979. return PrimitiveString::create(vm, Utf16String::create(string.substring_view(from, to - from)));
  980. }
  981. enum class TargetCase {
  982. Lower,
  983. Upper,
  984. };
  985. // 19.1.2.1 TransformCase ( S, locales, targetCase ), https://tc39.es/ecma402/#sec-transform-case
  986. static ThrowCompletionOr<String> transform_case(VM& vm, String const& string, Value locales, TargetCase target_case)
  987. {
  988. // 1. Let requestedLocales be ? CanonicalizeLocaleList(locales).
  989. auto requested_locales = TRY(Intl::canonicalize_locale_list(vm, locales));
  990. String requested_locale;
  991. // 2. If requestedLocales is not an empty List, then
  992. if (!requested_locales.is_empty()) {
  993. // a. Let requestedLocale be requestedLocales[0].
  994. requested_locale = requested_locales[0];
  995. }
  996. // 3. Else,
  997. else {
  998. // a. Let requestedLocale be ! DefaultLocale().
  999. requested_locale = String::from_utf8_without_validation(Unicode::default_locale().bytes());
  1000. }
  1001. // 4. Let availableLocales be an Available Locales List which includes the language tags for which the Unicode Character Database contains language-sensitive case mappings. If the implementation supports additional locale-sensitive case mappings, availableLocales should also include their corresponding language tags.
  1002. // 5. Let match be LookupMatchingLocaleByPrefix(availableLocales, « requestedLocale »).
  1003. auto match = Intl::lookup_matching_locale_by_prefix({ { requested_locale } });
  1004. // 6. If match is not undefined, let locale be match.[[locale]]; else let locale be "und".
  1005. StringView locale = match.has_value() ? match->locale : "und"sv;
  1006. // 7. Let codePoints be StringToCodePoints(S).
  1007. String new_code_points;
  1008. switch (target_case) {
  1009. // 8. If targetCase is lower, then
  1010. case TargetCase::Lower:
  1011. // a. Let newCodePoints be a List whose elements are the result of a lowercase transformation of codePoints according to an implementation-derived algorithm using locale or the Unicode Default Case Conversion algorithm.
  1012. new_code_points = MUST(string.to_lowercase(locale));
  1013. break;
  1014. // 9. Else,
  1015. case TargetCase::Upper:
  1016. // a. Assert: targetCase is upper.
  1017. // b. Let newCodePoints be a List whose elements are the result of an uppercase transformation of codePoints according to an implementation-derived algorithm using locale or the Unicode Default Case Conversion algorithm.
  1018. new_code_points = MUST(string.to_uppercase(locale));
  1019. break;
  1020. default:
  1021. VERIFY_NOT_REACHED();
  1022. }
  1023. // 10. Return CodePointsToString(newCodePoints).
  1024. return new_code_points;
  1025. }
  1026. // 22.1.3.26 String.prototype.toLocaleLowerCase ( [ reserved1 [ , reserved2 ] ] ), https://tc39.es/ecma262/#sec-string.prototype.tolocalelowercase
  1027. // 19.1.2 String.prototype.toLocaleLowerCase ( [ locales ] ), https://tc39.es/ecma402/#sup-string.prototype.tolocalelowercase
  1028. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::to_locale_lowercase)
  1029. {
  1030. auto locales = vm.argument(0);
  1031. // 1. Let O be ? RequireObjectCoercible(this value).
  1032. // 2. Let S be ? ToString(O).
  1033. auto string = TRY(utf8_string_from(vm));
  1034. // 3. Return ? TransformCase(S, locales, lower).
  1035. return PrimitiveString::create(vm, TRY(transform_case(vm, string, locales, TargetCase::Lower)));
  1036. }
  1037. // 22.1.3.27 String.prototype.toLocaleUpperCase ( [ reserved1 [ , reserved2 ] ] ), https://tc39.es/ecma262/#sec-string.prototype.tolocaleuppercase
  1038. // 19.1.3 String.prototype.toLocaleUpperCase ( [ locales ] ), https://tc39.es/ecma402/#sup-string.prototype.tolocaleuppercase
  1039. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::to_locale_uppercase)
  1040. {
  1041. auto locales = vm.argument(0);
  1042. // 1. Let O be ? RequireObjectCoercible(this value).
  1043. // 2. Let S be ? ToString(O).
  1044. auto string = TRY(utf8_string_from(vm));
  1045. // 3. Return ? TransformCase(S, locales, upper).
  1046. return PrimitiveString::create(vm, TRY(transform_case(vm, string, locales, TargetCase::Upper)));
  1047. }
  1048. // 22.1.3.28 String.prototype.toLowerCase ( ), https://tc39.es/ecma262/#sec-string.prototype.tolowercase
  1049. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::to_lowercase)
  1050. {
  1051. // 1. Let O be ? RequireObjectCoercible(this value).
  1052. // 2. Let S be ? ToString(O).
  1053. // 3. Let sText be StringToCodePoints(S).
  1054. auto string = TRY(utf8_string_from(vm));
  1055. // 4. Let lowerText be the result of toLowercase(sText), according to the Unicode Default Case Conversion algorithm.
  1056. auto lowercase = MUST(string.to_lowercase());
  1057. // 5. Let L be CodePointsToString(lowerText).
  1058. // 6. Return L.
  1059. return PrimitiveString::create(vm, move(lowercase));
  1060. }
  1061. // 22.1.3.29 String.prototype.toString ( ), https://tc39.es/ecma262/#sec-string.prototype.tostring
  1062. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::to_string)
  1063. {
  1064. // 1. Return ? thisStringValue(this value).
  1065. return TRY(this_string_value(vm, vm.this_value()));
  1066. }
  1067. // 22.1.3.30 String.prototype.toUpperCase ( ), https://tc39.es/ecma262/#sec-string.prototype.touppercase
  1068. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::to_uppercase)
  1069. {
  1070. // This method interprets a String value as a sequence of UTF-16 encoded code points, as described in 6.1.4.
  1071. // It behaves in exactly the same way as String.prototype.toLowerCase, except that the String is mapped using the toUppercase algorithm of the Unicode Default Case Conversion.
  1072. auto string = TRY(utf8_string_from(vm));
  1073. auto uppercase = MUST(string.to_uppercase());
  1074. return PrimitiveString::create(vm, move(uppercase));
  1075. }
  1076. // 22.1.3.31 String.prototype.toWellFormed ( ), https://tc39.es/ecma262/#sec-string.prototype.towellformed
  1077. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::to_well_formed)
  1078. {
  1079. // 1. Let O be ? RequireObjectCoercible(this value).
  1080. // 2. Let S be ? ToString(O).
  1081. auto string = TRY(utf16_string_from(vm));
  1082. // NOTE: Rest of steps in to_well_formed below
  1083. return PrimitiveString::create(vm, to_well_formed_string(string));
  1084. }
  1085. // https://tc39.es/ecma262/#sec-string.prototype.towellformed
  1086. String to_well_formed_string(Utf16String const& string)
  1087. {
  1088. // 3. Let strLen be the length of S.
  1089. auto length = string.length_in_code_units();
  1090. // 4. Let k be 0.
  1091. size_t k = 0;
  1092. // 5. Let result be the empty String.
  1093. StringBuilder result;
  1094. // 6. Repeat, while k < strLen,
  1095. while (k < length) {
  1096. // a. Let cp be CodePointAt(S, k).
  1097. auto code_point = JS::code_point_at(string.view(), k);
  1098. // b. If cp.[[IsUnpairedSurrogate]] is true, then
  1099. if (code_point.is_unpaired_surrogate) {
  1100. // i. Set result to the string-concatenation of result and 0xFFFD (REPLACEMENT CHARACTER).
  1101. result.append_code_point(0xfffd);
  1102. }
  1103. // c. Else,
  1104. else {
  1105. // i. Set result to the string-concatenation of result and UTF16EncodeCodePoint(cp.[[CodePoint]]).
  1106. result.append_code_point(code_point.code_point);
  1107. }
  1108. // d. Set k to k + cp.[[CodeUnitCount]].
  1109. k += code_point.code_unit_count;
  1110. }
  1111. // 7. Return result.
  1112. return MUST(result.to_string());
  1113. }
  1114. // 22.1.3.32.1 TrimString ( string, where ), https://tc39.es/ecma262/#sec-trimstring
  1115. ThrowCompletionOr<String> trim_string(VM& vm, Value input_value, TrimMode where)
  1116. {
  1117. // 1. Let str be ? RequireObjectCoercible(string).
  1118. auto input_string = TRY(require_object_coercible(vm, input_value));
  1119. // 2. Let S be ? ToString(str).
  1120. auto string = TRY(input_string.to_string(vm));
  1121. // 3. If where is start, let T be the String value that is a copy of S with leading white space removed.
  1122. // 4. Else if where is end, let T be the String value that is a copy of S with trailing white space removed.
  1123. // 5. Else,
  1124. // a. Assert: where is start+end.
  1125. // b. Let T be the String value that is a copy of S with both leading and trailing white space removed.
  1126. auto trimmed_string = Utf8View(string).trim(whitespace_characters, where).as_string();
  1127. // 6. Return T.
  1128. return MUST(String::from_utf8(trimmed_string));
  1129. }
  1130. // 22.1.3.32 String.prototype.trim ( ), https://tc39.es/ecma262/#sec-string.prototype.trim
  1131. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::trim)
  1132. {
  1133. // 1. Let S be the this value.
  1134. // 2. Return ? TrimString(S, start+end).
  1135. return PrimitiveString::create(vm, TRY(trim_string(vm, vm.this_value(), TrimMode::Both)));
  1136. }
  1137. // 22.1.3.33 String.prototype.trimEnd ( ), https://tc39.es/ecma262/#sec-string.prototype.trimend
  1138. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::trim_end)
  1139. {
  1140. // 1. Let S be the this value.
  1141. // 2. Return ? TrimString(S, end).
  1142. return PrimitiveString::create(vm, TRY(trim_string(vm, vm.this_value(), TrimMode::Right)));
  1143. }
  1144. // 22.1.3.34 String.prototype.trimStart ( ), https://tc39.es/ecma262/#sec-string.prototype.trimstart
  1145. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::trim_start)
  1146. {
  1147. // 1. Let S be the this value.
  1148. // 2. Return ? TrimString(S, start).
  1149. return PrimitiveString::create(vm, TRY(trim_string(vm, vm.this_value(), TrimMode::Left)));
  1150. }
  1151. // 22.1.3.35 String.prototype.valueOf ( ), https://tc39.es/ecma262/#sec-string.prototype.valueof
  1152. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::value_of)
  1153. {
  1154. return TRY(this_string_value(vm, vm.this_value()));
  1155. }
  1156. // 22.1.3.36 String.prototype [ @@iterator ] ( ), https://tc39.es/ecma262/#sec-string.prototype-@@iterator
  1157. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::symbol_iterator)
  1158. {
  1159. auto& realm = *vm.current_realm();
  1160. // 1. Let O be ? RequireObjectCoercible(this value).
  1161. auto this_object = TRY(require_object_coercible(vm, vm.this_value()));
  1162. // 2. Let s be ? ToString(O).
  1163. auto string = TRY(this_object.to_string(vm));
  1164. // 3. Let closure be a new Abstract Closure with no parameters that captures s and performs the following steps when called:
  1165. // ...
  1166. // 4. Return CreateIteratorFromClosure(closure, "%StringIteratorPrototype%", %StringIteratorPrototype%).
  1167. return StringIterator::create(realm, string);
  1168. }
  1169. // B.2.2.1 String.prototype.substr ( start, length ), https://tc39.es/ecma262/#sec-string.prototype.substr
  1170. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::substr)
  1171. {
  1172. // 1. Let O be ? RequireObjectCoercible(this value).
  1173. // 2. Let S be ? ToString(O).
  1174. auto string = TRY(utf16_string_from(vm));
  1175. // 3. Let size be the length of S.
  1176. auto size = string.length_in_code_units();
  1177. // 4. Let intStart be ? ToIntegerOrInfinity(start).
  1178. auto int_start = TRY(vm.argument(0).to_integer_or_infinity(vm));
  1179. // 5. If intStart is -∞, set intStart to 0.
  1180. if (Value(int_start).is_negative_infinity())
  1181. int_start = 0;
  1182. // 6. Else if intStart < 0, set intStart to max(size + intStart, 0).
  1183. else if (int_start < 0)
  1184. int_start = max(size + int_start, 0);
  1185. // 7. Else, set intStart to min(intStart, size).
  1186. else
  1187. int_start = min(int_start, size);
  1188. // 8. If length is undefined, let intLength be size; otherwise let intLength be ? ToIntegerOrInfinity(length).
  1189. auto length = vm.argument(1);
  1190. auto int_length = length.is_undefined() ? size : TRY(length.to_integer_or_infinity(vm));
  1191. // 9. Set intLength to the result of clamping intLength between 0 and size.
  1192. int_length = clamp(int_length, 0, size);
  1193. // 10. Let intEnd be min(intStart + intLength, size).
  1194. auto int_end = min((i32)(int_start + int_length), size);
  1195. if (int_start >= int_end)
  1196. return PrimitiveString::create(vm, String {});
  1197. // 11. Return the substring of S from intStart to intEnd.
  1198. return PrimitiveString::create(vm, Utf16String::create(string.substring_view(int_start, int_end - int_start)));
  1199. }
  1200. // B.2.2.2.1 CreateHTML ( string, tag, attribute, value ), https://tc39.es/ecma262/#sec-createhtml
  1201. static ThrowCompletionOr<Value> create_html(VM& vm, Value string, StringView tag, StringView attribute, Value value)
  1202. {
  1203. // 1. Let str be ? RequireObjectCoercible(string).
  1204. TRY(require_object_coercible(vm, string));
  1205. // 2. Let S be ? ToString(str).
  1206. auto str = TRY(string.to_string(vm));
  1207. // 3. Let p1 be the string-concatenation of "<" and tag.
  1208. StringBuilder builder;
  1209. builder.append('<');
  1210. builder.append(tag);
  1211. // 4. If attribute is not the empty String, then
  1212. if (!attribute.is_empty()) {
  1213. // a. Let V be ? ToString(value).
  1214. auto value_string = TRY(value.to_string(vm));
  1215. // b. Let escapedV be the String value that is the same as V except that each occurrence of the code unit 0x0022 (QUOTATION MARK) in V has been replaced with the six code unit sequence "&quot;".
  1216. auto escaped_value_string = MUST(value_string.replace("\""sv, "&quot;"sv, ReplaceMode::All));
  1217. // c. Set p1 to the string-concatenation of:
  1218. // - p1
  1219. // - the code unit 0x0020 (SPACE)
  1220. builder.append(' ');
  1221. // - attribute
  1222. builder.append(attribute);
  1223. // - the code unit 0x003D (EQUALS SIGN)
  1224. // - the code unit 0x0022 (QUOTATION MARK)
  1225. builder.append("=\""sv);
  1226. // - escapedV
  1227. builder.append(escaped_value_string);
  1228. // - the code unit 0x0022 (QUOTATION MARK)
  1229. builder.append('"');
  1230. }
  1231. // 5. Let p2 be the string-concatenation of p1 and ">".
  1232. builder.append('>');
  1233. // 6. Let p3 be the string-concatenation of p2 and S.
  1234. builder.append(str);
  1235. // 7. Let p4 be the string-concatenation of p3, "</", tag, and ">".
  1236. builder.append("</"sv);
  1237. builder.append(tag);
  1238. builder.append('>');
  1239. // 8. Return p4.
  1240. return PrimitiveString::create(vm, MUST(builder.to_string()));
  1241. }
  1242. // B.2.2.2 String.prototype.anchor ( name ), https://tc39.es/ecma262/#sec-string.prototype.anchor
  1243. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::anchor)
  1244. {
  1245. auto name = vm.argument(0);
  1246. // 1. Let S be the this value.
  1247. // 2. Return ? CreateHTML(S, "a", "name", name).
  1248. return create_html(vm, vm.this_value(), "a"sv, "name"sv, name);
  1249. }
  1250. // B.2.2.3 String.prototype.big ( ), https://tc39.es/ecma262/#sec-string.prototype.big
  1251. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::big)
  1252. {
  1253. // 1. Let S be the this value.
  1254. // 2. Return ? CreateHTML(S, "big", "", "").
  1255. return create_html(vm, vm.this_value(), "big"sv, {}, Value());
  1256. }
  1257. // B.2.2.4 String.prototype.blink ( ), https://tc39.es/ecma262/#sec-string.prototype.blink
  1258. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::blink)
  1259. {
  1260. // 1. Let S be the this value.
  1261. // 2. Return ? CreateHTML(S, "blink", "", "").
  1262. return create_html(vm, vm.this_value(), "blink"sv, {}, Value());
  1263. }
  1264. // B.2.2.5 String.prototype.bold ( ), https://tc39.es/ecma262/#sec-string.prototype.bold
  1265. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::bold)
  1266. {
  1267. // 1. Let S be the this value.
  1268. // 2. Return ? CreateHTML(S, "b", "", "").
  1269. return create_html(vm, vm.this_value(), "b"sv, {}, Value());
  1270. }
  1271. // B.2.2.6 String.prototype.fixed ( ), https://tc39.es/ecma262/#sec-string.prototype.fixed
  1272. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::fixed)
  1273. {
  1274. // 1. Let S be the this value.
  1275. // 2. Return ? CreateHTML(S, "tt", "", "").
  1276. return create_html(vm, vm.this_value(), "tt"sv, {}, Value());
  1277. }
  1278. // B.2.2.7 String.prototype.fontcolor ( color ), https://tc39.es/ecma262/#sec-string.prototype.fontcolor
  1279. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::fontcolor)
  1280. {
  1281. auto color = vm.argument(0);
  1282. // 1. Let S be the this value.
  1283. // 2. Return ? CreateHTML(S, "font", "color", color).
  1284. return create_html(vm, vm.this_value(), "font"sv, "color"sv, color);
  1285. }
  1286. // B.2.2.8 String.prototype.fontsize ( size ), https://tc39.es/ecma262/#sec-string.prototype.fontsize
  1287. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::fontsize)
  1288. {
  1289. auto size = vm.argument(0);
  1290. // 1. Let S be the this value.
  1291. // 2. Return ? CreateHTML(S, "font", "size", size).
  1292. return create_html(vm, vm.this_value(), "font"sv, "size"sv, size);
  1293. }
  1294. // B.2.2.9 String.prototype.italics ( ), https://tc39.es/ecma262/#sec-string.prototype.italics
  1295. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::italics)
  1296. {
  1297. // 1. Let S be the this value.
  1298. // 2. Return ? CreateHTML(S, "i", "", "").
  1299. return create_html(vm, vm.this_value(), "i"sv, {}, Value());
  1300. }
  1301. // B.2.2.10 String.prototype.link ( url ), https://tc39.es/ecma262/#sec-string.prototype.link
  1302. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::link)
  1303. {
  1304. auto url = vm.argument(0);
  1305. // 1. Let S be the this value.
  1306. // 2. Return ? CreateHTML(S, "a", "href", url).
  1307. return create_html(vm, vm.this_value(), "a"sv, "href"sv, url);
  1308. }
  1309. // B.2.2.11 String.prototype.small ( ), https://tc39.es/ecma262/#sec-string.prototype.small
  1310. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::small)
  1311. {
  1312. // 1. Let S be the this value.
  1313. // 2. Return ? CreateHTML(S, "small", "", "").
  1314. return create_html(vm, vm.this_value(), "small"sv, {}, Value());
  1315. }
  1316. // B.2.2.12 String.prototype.strike ( ), https://tc39.es/ecma262/#sec-string.prototype.strike
  1317. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::strike)
  1318. {
  1319. // 1. Let S be the this value.
  1320. // 2. Return ? CreateHTML(S, "strike", "", "").
  1321. return create_html(vm, vm.this_value(), "strike"sv, {}, Value());
  1322. }
  1323. // B.2.2.13 String.prototype.sub ( ), https://tc39.es/ecma262/#sec-string.prototype.sub
  1324. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::sub)
  1325. {
  1326. // 1. Let S be the this value.
  1327. // 2. Return ? CreateHTML(S, "sub", "", "").
  1328. return create_html(vm, vm.this_value(), "sub"sv, {}, Value());
  1329. }
  1330. // B.2.2.14 String.prototype.sup ( ), https://tc39.es/ecma262/#sec-string.prototype.sup
  1331. JS_DEFINE_NATIVE_FUNCTION(StringPrototype::sup)
  1332. {
  1333. // 1. Let S be the this value.
  1334. // 2. Return ? CreateHTML(S, "sup", "", "").
  1335. return create_html(vm, vm.this_value(), "sup"sv, {}, Value());
  1336. }
  1337. }