Преглед изворни кода

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 :^)
Linus Groh пре 2 година
родитељ
комит
d8ee4c0e7d
1 измењених фајлова са 244 додато и 32 уклоњено
  1. 244 32
      Userland/Libraries/LibJS/Runtime/StringPrototype.cpp

+ 244 - 32
Userland/Libraries/LibJS/Runtime/StringPrototype.cpp

@@ -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)));
-            replacement = TRY(result.to_string(vm));
-        } else {
+            // i. Let replacement be ? ToString(? Call(replaceValue, undefined, « searchString, 𝔽(p), string »)).
+            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));
+        }
+        // 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();
+
+    // 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)));
         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()) {
-        int_end = TRY(vm.argument(1).to_integer_or_infinity(vm));
+    if (!end.is_undefined()) {
+        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.
-    auto position = start; // 'q' in the spec.
+    // 12. Let i be 0.
+    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 string = TRY(utf16_string_from(vm));
-
     auto search_string_value = vm.argument(0);
     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));
 
 
+    // 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));
-        start = clamp(position, static_cast<double>(0), static_cast<double>(string_length));
+
+    // 7. If position is undefined, let pos be 0; else let pos be ? ToIntegerOrInfinity(position).
+    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());
 }
 }