1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 07:47:37 +00:00

LibJS: Add remaining missing spec comments to StringPrototype

The changes from ac2f109 were from an old branch, which either had a
rebase accident or was simply incomplete. Should be complete now :^)
This commit is contained in:
Linus Groh 2023-04-15 16:00:59 +02:00
parent 1649138a29
commit d8ee4c0e7d

View file

@ -900,95 +900,160 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::replace_all)
// 3. Let string be ? ToString(O). // 3. Let string be ? ToString(O).
auto string = TRY(this_object.to_utf16_string(vm)); auto string = TRY(this_object.to_utf16_string(vm));
// 4. Let searchString be ? ToString(searchValue).
auto search_string = TRY(search_value.to_utf16_string(vm)); auto search_string = TRY(search_value.to_utf16_string(vm));
// 5. Let functionalReplace be IsCallable(replaceValue).
// 6. If functionalReplace is false, then
if (!replace_value.is_function()) { if (!replace_value.is_function()) {
// a. Set replaceValue to ? ToString(replaceValue).
auto replace_string = TRY(replace_value.to_utf16_string(vm)); auto replace_string = TRY(replace_value.to_utf16_string(vm));
replace_value = PrimitiveString::create(vm, move(replace_string)); replace_value = PrimitiveString::create(vm, move(replace_string));
} }
auto string_length = string.length_in_code_units(); // 7. Let searchLength be the length of searchString.
auto search_length = search_string.length_in_code_units(); auto search_length = search_string.length_in_code_units();
Vector<size_t> match_positions; // 8. Let advanceBy be max(1, searchLength).
size_t advance_by = max(1u, search_length); size_t advance_by = max(1u, search_length);
// 9. Let matchPositions be a new empty List.
Vector<size_t> match_positions;
// 10. Let position be StringIndexOf(string, searchString, 0).
auto position = string_index_of(string.view(), search_string.view(), 0); auto position = string_index_of(string.view(), search_string.view(), 0);
// 11. Repeat, while position ≠ -1,
while (position.has_value()) { while (position.has_value()) {
// a. Append position to matchPositions.
TRY_OR_THROW_OOM(vm, match_positions.try_append(*position)); TRY_OR_THROW_OOM(vm, match_positions.try_append(*position));
// b. Set position to StringIndexOf(string, searchString, position + advanceBy).
position = string_index_of(string.view(), search_string.view(), *position + advance_by); position = string_index_of(string.view(), search_string.view(), *position + advance_by);
} }
// 12. Let endOfLastMatch be 0.
size_t end_of_last_match = 0; size_t end_of_last_match = 0;
// 13. Let result be the empty String.
ThrowableStringBuilder result(vm); ThrowableStringBuilder result(vm);
// 14. For each element p of matchPositions, do
for (auto position : match_positions) { for (auto position : match_positions) {
// a. Let preserved be the substring of string from endOfLastMatch to p.
auto preserved = string.substring_view(end_of_last_match, position - end_of_last_match); auto preserved = string.substring_view(end_of_last_match, position - end_of_last_match);
String replacement; String replacement;
// b. If functionalReplace is true, then
if (replace_value.is_function()) { if (replace_value.is_function()) {
auto result = TRY(call(vm, replace_value.as_function(), js_undefined(), PrimitiveString::create(vm, search_string), Value(position), PrimitiveString::create(vm, string))); // i. Let replacement be ? ToString(? Call(replaceValue, undefined, « searchString, 𝔽(p), string »)).
replacement = TRY(result.to_string(vm)); 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));
} else { }
// c. Else,
else {
// i. Assert: replaceValue is a String.
// ii. Let captures be a new empty List.
// iii. Let replacement be ! GetSubstitution(searchString, string, p, captures, undefined, replaceValue).
replacement = TRY(get_substitution(vm, search_string.view(), string.view(), position, {}, js_undefined(), replace_value)); replacement = TRY(get_substitution(vm, search_string.view(), string.view(), position, {}, js_undefined(), replace_value));
} }
// d. Set result to the string-concatenation of result, preserved, and replacement.
TRY(result.append(preserved)); TRY(result.append(preserved));
TRY(result.append(replacement)); TRY(result.append(replacement));
// e. Set endOfLastMatch to p + searchLength.
end_of_last_match = position + search_length; end_of_last_match = position + search_length;
} }
if (end_of_last_match < string_length) auto string_length = string.length_in_code_units();
TRY(result.append(string.substring_view(end_of_last_match)));
// 15. If endOfLastMatch < the length of string, then
if (end_of_last_match < string_length) {
// a. Set result to the string-concatenation of result and the substring of string from endOfLastMatch.
TRY(result.append(string.substring_view(end_of_last_match)));
}
// 16. Return result.
return PrimitiveString::create(vm, TRY(result.to_string())); return PrimitiveString::create(vm, TRY(result.to_string()));
} }
// 22.1.3.20 String.prototype.search ( regexp ), https://tc39.es/ecma262/#sec-string.prototype.search // 22.1.3.20 String.prototype.search ( regexp ), https://tc39.es/ecma262/#sec-string.prototype.search
JS_DEFINE_NATIVE_FUNCTION(StringPrototype::search) JS_DEFINE_NATIVE_FUNCTION(StringPrototype::search)
{ {
auto this_object = TRY(require_object_coercible(vm, vm.this_value()));
auto regexp = vm.argument(0); auto regexp = vm.argument(0);
// 1. Let O be ? RequireObjectCoercible(this value).
auto this_object = TRY(require_object_coercible(vm, vm.this_value()));
// 2. If regexp is neither undefined nor null, then
if (!regexp.is_nullish()) { if (!regexp.is_nullish()) {
if (auto searcher = TRY(regexp.get_method(vm, vm.well_known_symbol_search()))) // a. Let searcher be ? GetMethod(regexp, @@search).
auto searcher = TRY(regexp.get_method(vm, vm.well_known_symbol_search()));
// b. If searcher is not undefined, then
if (searcher) {
// i. Return ? Call(searcher, regexp, « O »).
return TRY(call(vm, *searcher, regexp, this_object)); return TRY(call(vm, *searcher, regexp, this_object));
}
} }
// 3. Let string be ? ToString(O).
auto string = TRY(this_object.to_utf16_string(vm)); auto string = TRY(this_object.to_utf16_string(vm));
// 4. Let rx be ? RegExpCreate(regexp, undefined).
auto rx = TRY(regexp_create(vm, regexp, js_undefined())); auto rx = TRY(regexp_create(vm, regexp, js_undefined()));
// 5. Return ? Invoke(rx, @@search, « string »).
return TRY(Value(rx).invoke(vm, vm.well_known_symbol_search(), PrimitiveString::create(vm, move(string)))); return TRY(Value(rx).invoke(vm, vm.well_known_symbol_search(), PrimitiveString::create(vm, move(string))));
} }
// 22.1.3.21 String.prototype.slice ( start, end ), https://tc39.es/ecma262/#sec-string.prototype.slice // 22.1.3.21 String.prototype.slice ( start, end ), https://tc39.es/ecma262/#sec-string.prototype.slice
JS_DEFINE_NATIVE_FUNCTION(StringPrototype::slice) JS_DEFINE_NATIVE_FUNCTION(StringPrototype::slice)
{ {
auto start = vm.argument(0);
auto end = vm.argument(1);
// 1. Let O be ? RequireObjectCoercible(this value).
// 2. Let S be ? ToString(O).
auto string = TRY(utf16_string_from(vm)); auto string = TRY(utf16_string_from(vm));
// 3. Let len be the length of S.
auto string_length = static_cast<double>(string.length_in_code_units()); auto string_length = static_cast<double>(string.length_in_code_units());
auto int_start = TRY(vm.argument(0).to_integer_or_infinity(vm)); // 4. Let intStart be ? ToIntegerOrInfinity(start).
auto int_start = TRY(start.to_integer_or_infinity(vm));
// 5. If intStart = -∞, let from be 0.
if (Value(int_start).is_negative_infinity()) if (Value(int_start).is_negative_infinity())
int_start = 0; int_start = 0;
// 6. Else if intStart < 0, let from be max(len + intStart, 0).
else if (int_start < 0) else if (int_start < 0)
int_start = max(string_length + int_start, 0); int_start = max(string_length + int_start, 0);
// 7. Else, let from be min(intStart, len).
else else
int_start = min(int_start, string_length); int_start = min(int_start, string_length);
// 8. If end is undefined, let intEnd be len; else let intEnd be ? ToIntegerOrInfinity(end).
auto int_end = string_length; auto int_end = string_length;
if (!vm.argument(1).is_undefined()) { if (!end.is_undefined()) {
int_end = TRY(vm.argument(1).to_integer_or_infinity(vm)); int_end = TRY(end.to_integer_or_infinity(vm));
// 9. If intEnd = -∞, let to be 0.
if (Value(int_end).is_negative_infinity()) if (Value(int_end).is_negative_infinity())
int_end = 0; int_end = 0;
// 10. Else if intEnd < 0, let to be max(len + intEnd, 0).
else if (int_end < 0) else if (int_end < 0)
int_end = max(string_length + int_end, 0); int_end = max(string_length + int_end, 0);
// 11. Else, let to be min(intEnd, len).
else else
int_end = min(int_end, string_length); int_end = min(int_end, string_length);
} }
// 12. If from ≥ to, return the empty String.
if (int_start >= int_end) if (int_start >= int_end)
return PrimitiveString::create(vm, String {}); return PrimitiveString::create(vm, String {});
// 13. Return the substring of S from from to to.
return PrimitiveString::create(vm, TRY(Utf16String::create(vm, string.substring_view(int_start, int_end - int_start)))); return PrimitiveString::create(vm, TRY(Utf16String::create(vm, string.substring_view(int_start, int_end - int_start))));
} }
@ -996,99 +1061,156 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::slice)
JS_DEFINE_NATIVE_FUNCTION(StringPrototype::split) JS_DEFINE_NATIVE_FUNCTION(StringPrototype::split)
{ {
auto& realm = *vm.current_realm(); auto& realm = *vm.current_realm();
auto object = TRY(require_object_coercible(vm, vm.this_value()));
auto separator_argument = vm.argument(0); auto separator_argument = vm.argument(0);
auto limit_argument = vm.argument(1); auto limit_argument = vm.argument(1);
// 1. Let O be ? RequireObjectCoercible(this value).
auto object = TRY(require_object_coercible(vm, vm.this_value()));
// 2. If separator is neither undefined nor null, then
if (!separator_argument.is_nullish()) { if (!separator_argument.is_nullish()) {
// a. Let splitter be ? GetMethod(separator, @@split).
auto splitter = TRY(separator_argument.get_method(vm, vm.well_known_symbol_split())); auto splitter = TRY(separator_argument.get_method(vm, vm.well_known_symbol_split()));
if (splitter) // b. If splitter is not undefined, then
if (splitter) {
// i. Return ? Call(splitter, separator, « O, limit »).
return TRY(call(vm, *splitter, separator_argument, object, limit_argument)); return TRY(call(vm, *splitter, separator_argument, object, limit_argument));
}
} }
// 3. Let S be ? ToString(O).
auto string = TRY(object.to_utf16_string(vm)); auto string = TRY(object.to_utf16_string(vm));
// 11. Let substrings be a new empty List.
auto array = MUST(Array::create(realm, 0)); auto array = MUST(Array::create(realm, 0));
size_t array_length = 0; size_t array_length = 0;
// 4. If limit is undefined, let lim be 232 - 1; else let lim be (? ToUint32(limit)).
auto limit = NumericLimits<u32>::max(); auto limit = NumericLimits<u32>::max();
if (!limit_argument.is_undefined()) if (!limit_argument.is_undefined())
limit = TRY(limit_argument.to_u32(vm)); limit = TRY(limit_argument.to_u32(vm));
// 5. Let R be ? ToString(separator).
auto separator = TRY(separator_argument.to_utf16_string(vm)); auto separator = TRY(separator_argument.to_utf16_string(vm));
if (limit == 0) // 6. If lim = 0, then
if (limit == 0) {
// a. Return CreateArrayFromList(« »).
return array; return array;
}
auto string_length = string.length_in_code_units(); auto string_length = string.length_in_code_units();
auto separator_length = separator.length_in_code_units();
// 7. If separator is undefined, then
if (separator_argument.is_undefined()) { if (separator_argument.is_undefined()) {
// a. Return CreateArrayFromList(« S »).
MUST(array->create_data_property_or_throw(0, PrimitiveString::create(vm, move(string)))); MUST(array->create_data_property_or_throw(0, PrimitiveString::create(vm, move(string))));
return array; return array;
} }
// 8. Let separatorLength be the length of R.
auto separator_length = separator.length_in_code_units();
// 10. If S is the empty String, return CreateArrayFromList(« S »).
if (string_length == 0) { if (string_length == 0) {
if (separator_length > 0) if (separator_length > 0)
MUST(array->create_data_property_or_throw(0, PrimitiveString::create(vm, move(string)))); MUST(array->create_data_property_or_throw(0, PrimitiveString::create(vm, move(string))));
return array; return array;
} }
size_t start = 0; // 'p' in the spec. // 12. Let i be 0.
auto position = start; // 'q' in the spec. size_t start = 0;
// 13. Let j be StringIndexOf(S, R, 0).
auto position = start;
// 14. Repeat, while j ≠ -1,
while (position != string_length) { while (position != string_length) {
auto match = split_match(string.view(), position, separator.view()); // 'e' in the spec. // a. Let T be the substring of S from i to j.
auto match = split_match(string.view(), position, separator.view());
if (!match.has_value() || match.value() == start) { if (!match.has_value() || match.value() == start) {
++position; ++position;
continue; continue;
} }
auto segment = string.substring_view(start, position - start); auto segment = string.substring_view(start, position - start);
// b. Append T to substrings.
MUST(array->create_data_property_or_throw(array_length, PrimitiveString::create(vm, TRY(Utf16String::create(vm, segment))))); MUST(array->create_data_property_or_throw(array_length, PrimitiveString::create(vm, TRY(Utf16String::create(vm, segment)))));
++array_length; ++array_length;
// c. If the number of elements in substrings is lim, return CreateArrayFromList(substrings).
if (array_length == limit) if (array_length == limit)
return array; return array;
// d. Set i to j + separatorLength.
start = match.value(); start = match.value();
// e. Set j to StringIndexOf(S, R, i).
position = start; position = start;
} }
// 15. Let T be the substring of S from i.
auto rest = string.substring_view(start); auto rest = string.substring_view(start);
// 16. Append T to substrings.
MUST(array->create_data_property_or_throw(array_length, PrimitiveString::create(vm, TRY(Utf16String::create(vm, rest))))); MUST(array->create_data_property_or_throw(array_length, PrimitiveString::create(vm, TRY(Utf16String::create(vm, rest)))));
// 17. Return CreateArrayFromList(substrings).
return array; return array;
} }
// 22.1.3.23 String.prototype.startsWith ( searchString [ , position ] ), https://tc39.es/ecma262/#sec-string.prototype.startswith // 22.1.3.23 String.prototype.startsWith ( searchString [ , position ] ), https://tc39.es/ecma262/#sec-string.prototype.startswith
JS_DEFINE_NATIVE_FUNCTION(StringPrototype::starts_with) JS_DEFINE_NATIVE_FUNCTION(StringPrototype::starts_with)
{ {
auto search_string_value = vm.argument(0);
auto position = vm.argument(1);
// 1. Let O be ? RequireObjectCoercible(this value).
// 2. Let S be ? ToString(O).
auto string = TRY(utf16_string_from(vm)); auto string = TRY(utf16_string_from(vm));
auto search_string_value = vm.argument(0); // 3. Let isRegExp be ? IsRegExp(searchString).
bool is_regexp = TRY(search_string_value.is_regexp(vm)); bool is_regexp = TRY(search_string_value.is_regexp(vm));
// 4. If isRegExp is true, throw a TypeError exception.
if (is_regexp) if (is_regexp)
return vm.throw_completion<TypeError>(ErrorType::IsNotA, "searchString", "string, but a regular expression"); return vm.throw_completion<TypeError>(ErrorType::IsNotA, "searchString", "string, but a regular expression");
// 5. Let searchStr be ? ToString(searchString).
auto search_string = TRY(search_string_value.to_utf16_string(vm)); auto search_string = TRY(search_string_value.to_utf16_string(vm));
// 6. Let len be the length of S.
auto string_length = string.length_in_code_units(); auto string_length = string.length_in_code_units();
auto search_length = search_string.length_in_code_units();
size_t start = 0; size_t start = 0;
if (!vm.argument(1).is_undefined()) {
auto position = TRY(vm.argument(1).to_integer_or_infinity(vm)); // 7. If position is undefined, let pos be 0; else let pos be ? ToIntegerOrInfinity(position).
start = clamp(position, static_cast<double>(0), static_cast<double>(string_length)); if (!position.is_undefined()) {
auto pos = TRY(position.to_integer_or_infinity(vm));
// 8. Let start be the result of clamping pos between 0 and len.
start = clamp(pos, static_cast<double>(0), static_cast<double>(string_length));
} }
// 9. Let searchLength be the length of searchStr.
auto search_length = search_string.length_in_code_units();
// 10. If searchLength = 0, return true.
if (search_length == 0) if (search_length == 0)
return Value(true); return Value(true);
// 11. Let end be start + searchLength.
size_t end = start + search_length; size_t end = start + search_length;
// 12. If end > len, return false.
if (end > string_length) if (end > string_length)
return Value(false); return Value(false);
// 13. Let substring be the substring of S from start to end.
auto substring_view = string.substring_view(start, end - start); auto substring_view = string.substring_view(start, end - start);
// 14. If substring is searchStr, return true.
// 15. Return false.
return Value(substring_view == search_string.view()); return Value(substring_view == search_string.view());
} }
@ -1104,6 +1226,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::substring)
// 4. Let intStart be ? ToIntegerOrInfinity(start). // 4. Let intStart be ? ToIntegerOrInfinity(start).
auto start = TRY(vm.argument(0).to_integer_or_infinity(vm)); auto start = TRY(vm.argument(0).to_integer_or_infinity(vm));
// 5. If end is undefined, let intEnd be len; else let intEnd be ? ToIntegerOrInfinity(end). // 5. If end is undefined, let intEnd be len; else let intEnd be ? ToIntegerOrInfinity(end).
auto end = string_length; auto end = string_length;
if (!vm.argument(1).is_undefined()) if (!vm.argument(1).is_undefined())
@ -1111,11 +1234,13 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::substring)
// 6. Let finalStart be the result of clamping intStart between 0 and len. // 6. Let finalStart be the result of clamping intStart between 0 and len.
size_t final_start = clamp(start, static_cast<double>(0), string_length); size_t final_start = clamp(start, static_cast<double>(0), string_length);
// 7. Let finalEnd be the result of clamping intEnd between 0 and len. // 7. Let finalEnd be the result of clamping intEnd between 0 and len.
size_t final_end = clamp(end, static_cast<double>(0), string_length); size_t final_end = clamp(end, static_cast<double>(0), string_length);
// 8. Let from be min(finalStart, finalEnd). // 8. Let from be min(finalStart, finalEnd).
size_t from = min(final_start, final_end); size_t from = min(final_start, final_end);
// 9. Let to be max(finalStart, finalEnd). // 9. Let to be max(finalStart, finalEnd).
size_t to = max(final_start, final_end); size_t to = max(final_start, final_end);
@ -1215,20 +1340,31 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::to_locale_uppercase)
// 22.1.3.27 String.prototype.toLowerCase ( ), https://tc39.es/ecma262/#sec-string.prototype.tolowercase // 22.1.3.27 String.prototype.toLowerCase ( ), https://tc39.es/ecma262/#sec-string.prototype.tolowercase
JS_DEFINE_NATIVE_FUNCTION(StringPrototype::to_lowercase) JS_DEFINE_NATIVE_FUNCTION(StringPrototype::to_lowercase)
{ {
// 1. Let O be ? RequireObjectCoercible(this value).
// 2. Let S be ? ToString(O).
// 3. Let sText be StringToCodePoints(S).
auto string = TRY(utf8_string_from(vm)); auto string = TRY(utf8_string_from(vm));
// 4. Let lowerText be the result of toLowercase(sText), according to the Unicode Default Case Conversion algorithm.
auto lowercase = TRY_OR_THROW_OOM(vm, string.to_lowercase()); auto lowercase = TRY_OR_THROW_OOM(vm, string.to_lowercase());
// 5. Let L be CodePointsToString(lowerText).
// 6. Return L.
return PrimitiveString::create(vm, move(lowercase)); return PrimitiveString::create(vm, move(lowercase));
} }
// 22.1.3.28 String.prototype.toString ( ), https://tc39.es/ecma262/#sec-string.prototype.tostring // 22.1.3.28 String.prototype.toString ( ), https://tc39.es/ecma262/#sec-string.prototype.tostring
JS_DEFINE_NATIVE_FUNCTION(StringPrototype::to_string) JS_DEFINE_NATIVE_FUNCTION(StringPrototype::to_string)
{ {
// 1. Return ? thisStringValue(this value).
return TRY(this_string_value(vm, vm.this_value())); return TRY(this_string_value(vm, vm.this_value()));
} }
// 22.1.3.29 String.prototype.toUpperCase ( ), https://tc39.es/ecma262/#sec-string.prototype.touppercase // 22.1.3.29 String.prototype.toUpperCase ( ), https://tc39.es/ecma262/#sec-string.prototype.touppercase
JS_DEFINE_NATIVE_FUNCTION(StringPrototype::to_uppercase) JS_DEFINE_NATIVE_FUNCTION(StringPrototype::to_uppercase)
{ {
// This method interprets a String value as a sequence of UTF-16 encoded code points, as described in 6.1.4.
// 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.
auto string = TRY(utf8_string_from(vm)); auto string = TRY(utf8_string_from(vm));
auto uppercase = TRY_OR_THROW_OOM(vm, string.to_uppercase()); auto uppercase = TRY_OR_THROW_OOM(vm, string.to_uppercase());
return PrimitiveString::create(vm, move(uppercase)); return PrimitiveString::create(vm, move(uppercase));
@ -1274,6 +1410,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::to_well_formed)
return PrimitiveString::create(vm, TRY(result.to_string())); return PrimitiveString::create(vm, TRY(result.to_string()));
} }
// 22.1.3.30.1 TrimString ( string, where ), https://tc39.es/ecma262/#sec-trimstring
ThrowCompletionOr<String> trim_string(VM& vm, Value input_value, TrimMode where) ThrowCompletionOr<String> trim_string(VM& vm, Value input_value, TrimMode where)
{ {
// 1. Let str be ? RequireObjectCoercible(string). // 1. Let str be ? RequireObjectCoercible(string).
@ -1296,18 +1433,24 @@ ThrowCompletionOr<String> trim_string(VM& vm, Value input_value, TrimMode where)
// 22.1.3.30 String.prototype.trim ( ), https://tc39.es/ecma262/#sec-string.prototype.trim // 22.1.3.30 String.prototype.trim ( ), https://tc39.es/ecma262/#sec-string.prototype.trim
JS_DEFINE_NATIVE_FUNCTION(StringPrototype::trim) JS_DEFINE_NATIVE_FUNCTION(StringPrototype::trim)
{ {
// 1. Let S be the this value.
// 2. Return ? TrimString(S, start+end).
return PrimitiveString::create(vm, TRY(trim_string(vm, vm.this_value(), TrimMode::Both))); return PrimitiveString::create(vm, TRY(trim_string(vm, vm.this_value(), TrimMode::Both)));
} }
// 22.1.3.31 String.prototype.trimEnd ( ), https://tc39.es/ecma262/#sec-string.prototype.trimend // 22.1.3.31 String.prototype.trimEnd ( ), https://tc39.es/ecma262/#sec-string.prototype.trimend
JS_DEFINE_NATIVE_FUNCTION(StringPrototype::trim_end) JS_DEFINE_NATIVE_FUNCTION(StringPrototype::trim_end)
{ {
// 1. Let S be the this value.
// 2. Return ? TrimString(S, end).
return PrimitiveString::create(vm, TRY(trim_string(vm, vm.this_value(), TrimMode::Right))); return PrimitiveString::create(vm, TRY(trim_string(vm, vm.this_value(), TrimMode::Right)));
} }
// 22.1.3.32 String.prototype.trimStart ( ), https://tc39.es/ecma262/#sec-string.prototype.trimstart // 22.1.3.32 String.prototype.trimStart ( ), https://tc39.es/ecma262/#sec-string.prototype.trimstart
JS_DEFINE_NATIVE_FUNCTION(StringPrototype::trim_start) JS_DEFINE_NATIVE_FUNCTION(StringPrototype::trim_start)
{ {
// 1. Let S be the this value.
// 2. Return ? TrimString(S, start).
return PrimitiveString::create(vm, TRY(trim_string(vm, vm.this_value(), TrimMode::Left))); return PrimitiveString::create(vm, TRY(trim_string(vm, vm.this_value(), TrimMode::Left)));
} }
@ -1322,8 +1465,15 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::symbol_iterator)
{ {
auto& realm = *vm.current_realm(); auto& realm = *vm.current_realm();
// 1. Let O be ? RequireObjectCoercible(this value).
auto this_object = TRY(require_object_coercible(vm, vm.this_value())); auto this_object = TRY(require_object_coercible(vm, vm.this_value()));
// 2. Let s be ? ToString(O).
auto string = TRY(this_object.to_string(vm)); auto string = TRY(this_object.to_string(vm));
// 3. Let closure be a new Abstract Closure with no parameters that captures s and performs the following steps when called:
// ...
// 4. Return CreateIteratorFromClosure(closure, "%StringIteratorPrototype%", %StringIteratorPrototype%).
return StringIterator::create(realm, string); return StringIterator::create(realm, string);
} }
@ -1370,102 +1520,164 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::substr)
// B.2.2.2.1 CreateHTML ( string, tag, attribute, value ), https://tc39.es/ecma262/#sec-createhtml // B.2.2.2.1 CreateHTML ( string, tag, attribute, value ), https://tc39.es/ecma262/#sec-createhtml
static ThrowCompletionOr<Value> create_html(VM& vm, Value string, StringView tag, StringView attribute, Value value) static ThrowCompletionOr<Value> create_html(VM& vm, Value string, StringView tag, StringView attribute, Value value)
{ {
// 1. Let str be ? RequireObjectCoercible(string).
TRY(require_object_coercible(vm, string)); TRY(require_object_coercible(vm, string));
// 2. Let S be ? ToString(str).
auto str = TRY(string.to_string(vm)); auto str = TRY(string.to_string(vm));
// 3. Let p1 be the string-concatenation of "<" and tag.
ThrowableStringBuilder builder(vm); ThrowableStringBuilder builder(vm);
TRY(builder.append('<')); TRY(builder.append('<'));
TRY(builder.append(tag)); TRY(builder.append(tag));
// 4. If attribute is not the empty String, then
if (!attribute.is_empty()) { if (!attribute.is_empty()) {
// a. Let V be ? ToString(value).
auto value_string = TRY(value.to_string(vm)); auto value_string = TRY(value.to_string(vm));
// 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;".
auto escaped_value_string = TRY_OR_THROW_OOM(vm, value_string.replace("\""sv, "&quot;"sv, ReplaceMode::All));
// c. Set p1 to the string-concatenation of:
// - p1
// - the code unit 0x0020 (SPACE)
TRY(builder.append(' ')); TRY(builder.append(' '));
// - attribute
TRY(builder.append(attribute)); TRY(builder.append(attribute));
// - the code unit 0x003D (EQUALS SIGN)
// - the code unit 0x0022 (QUOTATION MARK)
TRY(builder.append("=\""sv)); TRY(builder.append("=\""sv));
TRY(builder.append(TRY_OR_THROW_OOM(vm, value_string.replace("\""sv, "&quot;"sv, ReplaceMode::All)))); // - escapedV
TRY(builder.append(escaped_value_string));
// - the code unit 0x0022 (QUOTATION MARK)
TRY(builder.append('"')); TRY(builder.append('"'));
} }
// 5. Let p2 be the string-concatenation of p1 and ">".
TRY(builder.append('>')); TRY(builder.append('>'));
// 6. Let p3 be the string-concatenation of p2 and S.
TRY(builder.append(str)); TRY(builder.append(str));
// 7. Let p4 be the string-concatenation of p3, "</", tag, and ">".
TRY(builder.append("</"sv)); TRY(builder.append("</"sv));
TRY(builder.append(tag)); TRY(builder.append(tag));
TRY(builder.append('>')); TRY(builder.append('>'));
// 8. Return p4.
return PrimitiveString::create(vm, TRY(builder.to_string())); return PrimitiveString::create(vm, TRY(builder.to_string()));
} }
// B.2.2.2 String.prototype.anchor ( name ), https://tc39.es/ecma262/#sec-string.prototype.anchor // B.2.2.2 String.prototype.anchor ( name ), https://tc39.es/ecma262/#sec-string.prototype.anchor
JS_DEFINE_NATIVE_FUNCTION(StringPrototype::anchor) JS_DEFINE_NATIVE_FUNCTION(StringPrototype::anchor)
{ {
return create_html(vm, vm.this_value(), "a"sv, "name"sv, vm.argument(0)); auto name = vm.argument(0);
// 1. Let S be the this value.
// 2. Return ? CreateHTML(S, "a", "name", name).
return create_html(vm, vm.this_value(), "a"sv, "name"sv, name);
} }
// B.2.2.3 String.prototype.big ( ), https://tc39.es/ecma262/#sec-string.prototype.big // B.2.2.3 String.prototype.big ( ), https://tc39.es/ecma262/#sec-string.prototype.big
JS_DEFINE_NATIVE_FUNCTION(StringPrototype::big) JS_DEFINE_NATIVE_FUNCTION(StringPrototype::big)
{ {
// 1. Let S be the this value.
// 2. Return ? CreateHTML(S, "big", "", "").
return create_html(vm, vm.this_value(), "big"sv, {}, Value()); return create_html(vm, vm.this_value(), "big"sv, {}, Value());
} }
// B.2.2.4 String.prototype.blink ( ), https://tc39.es/ecma262/#sec-string.prototype.blink // B.2.2.4 String.prototype.blink ( ), https://tc39.es/ecma262/#sec-string.prototype.blink
JS_DEFINE_NATIVE_FUNCTION(StringPrototype::blink) JS_DEFINE_NATIVE_FUNCTION(StringPrototype::blink)
{ {
// 1. Let S be the this value.
// 2. Return ? CreateHTML(S, "blink", "", "").
return create_html(vm, vm.this_value(), "blink"sv, {}, Value()); return create_html(vm, vm.this_value(), "blink"sv, {}, Value());
} }
// B.2.2.5 String.prototype.bold ( ), https://tc39.es/ecma262/#sec-string.prototype.bold // B.2.2.5 String.prototype.bold ( ), https://tc39.es/ecma262/#sec-string.prototype.bold
JS_DEFINE_NATIVE_FUNCTION(StringPrototype::bold) JS_DEFINE_NATIVE_FUNCTION(StringPrototype::bold)
{ {
// 1. Let S be the this value.
// 2. Return ? CreateHTML(S, "b", "", "").
return create_html(vm, vm.this_value(), "b"sv, {}, Value()); return create_html(vm, vm.this_value(), "b"sv, {}, Value());
} }
// B.2.2.6 String.prototype.fixed ( ), https://tc39.es/ecma262/#sec-string.prototype.fixed // B.2.2.6 String.prototype.fixed ( ), https://tc39.es/ecma262/#sec-string.prototype.fixed
JS_DEFINE_NATIVE_FUNCTION(StringPrototype::fixed) JS_DEFINE_NATIVE_FUNCTION(StringPrototype::fixed)
{ {
// 1. Let S be the this value.
// 2. Return ? CreateHTML(S, "tt", "", "").
return create_html(vm, vm.this_value(), "tt"sv, {}, Value()); return create_html(vm, vm.this_value(), "tt"sv, {}, Value());
} }
// B.2.2.7 String.prototype.fontcolor ( color ), https://tc39.es/ecma262/#sec-string.prototype.fontcolor // B.2.2.7 String.prototype.fontcolor ( color ), https://tc39.es/ecma262/#sec-string.prototype.fontcolor
JS_DEFINE_NATIVE_FUNCTION(StringPrototype::fontcolor) JS_DEFINE_NATIVE_FUNCTION(StringPrototype::fontcolor)
{ {
return create_html(vm, vm.this_value(), "font"sv, "color"sv, vm.argument(0)); auto color = vm.argument(0);
// 1. Let S be the this value.
// 2. Return ? CreateHTML(S, "font", "color", color).
return create_html(vm, vm.this_value(), "font"sv, "color"sv, color);
} }
// B.2.2.8 String.prototype.fontsize ( size ), https://tc39.es/ecma262/#sec-string.prototype.fontsize // B.2.2.8 String.prototype.fontsize ( size ), https://tc39.es/ecma262/#sec-string.prototype.fontsize
JS_DEFINE_NATIVE_FUNCTION(StringPrototype::fontsize) JS_DEFINE_NATIVE_FUNCTION(StringPrototype::fontsize)
{ {
return create_html(vm, vm.this_value(), "font"sv, "size"sv, vm.argument(0)); auto size = vm.argument(0);
// 1. Let S be the this value.
// 2. Return ? CreateHTML(S, "font", "size", size).
return create_html(vm, vm.this_value(), "font"sv, "size"sv, size);
} }
// B.2.2.9 String.prototype.italics ( ), https://tc39.es/ecma262/#sec-string.prototype.italics // B.2.2.9 String.prototype.italics ( ), https://tc39.es/ecma262/#sec-string.prototype.italics
JS_DEFINE_NATIVE_FUNCTION(StringPrototype::italics) JS_DEFINE_NATIVE_FUNCTION(StringPrototype::italics)
{ {
// 1. Let S be the this value.
// 2. Return ? CreateHTML(S, "i", "", "").
return create_html(vm, vm.this_value(), "i"sv, {}, Value()); return create_html(vm, vm.this_value(), "i"sv, {}, Value());
} }
// B.2.2.10 String.prototype.link ( url ), https://tc39.es/ecma262/#sec-string.prototype.link // B.2.2.10 String.prototype.link ( url ), https://tc39.es/ecma262/#sec-string.prototype.link
JS_DEFINE_NATIVE_FUNCTION(StringPrototype::link) JS_DEFINE_NATIVE_FUNCTION(StringPrototype::link)
{ {
return create_html(vm, vm.this_value(), "a"sv, "href"sv, vm.argument(0)); auto url = vm.argument(0);
// 1. Let S be the this value.
// 2. Return ? CreateHTML(S, "a", "href", url).
return create_html(vm, vm.this_value(), "a"sv, "href"sv, url);
} }
// B.2.2.11 String.prototype.small ( ), https://tc39.es/ecma262/#sec-string.prototype.small // B.2.2.11 String.prototype.small ( ), https://tc39.es/ecma262/#sec-string.prototype.small
JS_DEFINE_NATIVE_FUNCTION(StringPrototype::small) JS_DEFINE_NATIVE_FUNCTION(StringPrototype::small)
{ {
// 1. Let S be the this value.
// 2. Return ? CreateHTML(S, "small", "", "").
return create_html(vm, vm.this_value(), "small"sv, {}, Value()); return create_html(vm, vm.this_value(), "small"sv, {}, Value());
} }
// B.2.2.12 String.prototype.strike ( ), https://tc39.es/ecma262/#sec-string.prototype.strike // B.2.2.12 String.prototype.strike ( ), https://tc39.es/ecma262/#sec-string.prototype.strike
JS_DEFINE_NATIVE_FUNCTION(StringPrototype::strike) JS_DEFINE_NATIVE_FUNCTION(StringPrototype::strike)
{ {
// 1. Let S be the this value.
// 2. Return ? CreateHTML(S, "strike", "", "").
return create_html(vm, vm.this_value(), "strike"sv, {}, Value()); return create_html(vm, vm.this_value(), "strike"sv, {}, Value());
} }
// B.2.2.13 String.prototype.sub ( ), https://tc39.es/ecma262/#sec-string.prototype.sub // B.2.2.13 String.prototype.sub ( ), https://tc39.es/ecma262/#sec-string.prototype.sub
JS_DEFINE_NATIVE_FUNCTION(StringPrototype::sub) JS_DEFINE_NATIVE_FUNCTION(StringPrototype::sub)
{ {
// 1. Let S be the this value.
// 2. Return ? CreateHTML(S, "sub", "", "").
return create_html(vm, vm.this_value(), "sub"sv, {}, Value()); return create_html(vm, vm.this_value(), "sub"sv, {}, Value());
} }
// B.2.2.14 String.prototype.sup ( ), https://tc39.es/ecma262/#sec-string.prototype.sup // B.2.2.14 String.prototype.sup ( ), https://tc39.es/ecma262/#sec-string.prototype.sup
JS_DEFINE_NATIVE_FUNCTION(StringPrototype::sup) JS_DEFINE_NATIVE_FUNCTION(StringPrototype::sup)
{ {
// 1. Let S be the this value.
// 2. Return ? CreateHTML(S, "sup", "", "").
return create_html(vm, vm.this_value(), "sup"sv, {}, Value()); return create_html(vm, vm.this_value(), "sup"sv, {}, Value());
} }