diff --git a/Libraries/LibJS/Bytecode/PropertyAccess.h b/Libraries/LibJS/Bytecode/PropertyAccess.h index 02036d9691..3e327fe988 100644 --- a/Libraries/LibJS/Bytecode/PropertyAccess.h +++ b/Libraries/LibJS/Bytecode/PropertyAccess.h @@ -401,7 +401,7 @@ inline ThrowCompletionOr put_by_property_key(VM& vm, Value base, Value thi if (!succeeded && strict == Strict::Yes) [[unlikely]] { if (base.is_object()) return vm.throw_completion(ErrorType::ReferenceNullishSetProperty, name, base); - return vm.throw_completion(ErrorType::ReferencePrimitiveSetProperty, name, base.typeof_(vm)->utf8_string(), base); + return vm.throw_completion(ErrorType::ReferencePrimitiveSetProperty, name, base.typeof_(vm)->utf16_string_view().to_utf8_but_should_be_ported_to_utf16(), base); } break; } diff --git a/Libraries/LibJS/Console.cpp b/Libraries/LibJS/Console.cpp index cc9024cae0..684be9737e 100644 --- a/Libraries/LibJS/Console.cpp +++ b/Libraries/LibJS/Console.cpp @@ -449,7 +449,7 @@ ThrowCompletionOr Console::dirxml() static ThrowCompletionOr label_or_fallback(VM& vm, StringView fallback) { return vm.argument_count() > 0 && !vm.argument(0).is_undefined() - ? vm.argument(0).to_string(vm) + ? TRY(vm.argument(0).to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16() : TRY_OR_THROW_OOM(vm, String::from_utf8(fallback)); } @@ -773,7 +773,7 @@ ThrowCompletionOr Console::value_vector_to_string(GC::RootVector if (!builder.is_empty()) builder.append(' '); - builder.append(TRY(item.to_string(vm))); + builder.append(TRY(item.to_utf16_string(vm))); } return MUST(builder.to_string()); @@ -832,7 +832,7 @@ ThrowCompletionOr> ConsoleClient::formatter(GC::RootVector return args; // 2. Let target be the first element of args. - auto target = (!args.is_empty()) ? TRY(args.first().to_string(vm)) : String {}; + auto target = (!args.is_empty()) ? TRY(args.first().to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16() : String {}; // 3. Let current be the second element of args. auto current = (args.size() > 1) ? args[1] : js_undefined(); @@ -914,13 +914,13 @@ ThrowCompletionOr> ConsoleClient::formatter(GC::RootVector // 6. TODO: process %c else if (specifier == "%c"sv) { // NOTE: This has no spec yet. `%c` specifiers treat the argument as CSS styling for the log message. - add_css_style_to_current_message(TRY(current.to_string(vm))); + add_css_style_to_current_message(TRY(current.to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16()); converted = PrimitiveString::create(vm, String {}); } // 7. If any of the previous steps set converted, replace specifier in target with converted. if (converted.has_value()) - target = TRY_OR_THROW_OOM(vm, target.replace(specifier, TRY(converted->to_string(vm)), ReplaceMode::FirstOnly)); + target = TRY_OR_THROW_OOM(vm, target.replace(specifier, TRY(converted->to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16(), ReplaceMode::FirstOnly)); } // 7. Let result be a list containing target together with the elements of args starting from the third onward. diff --git a/Libraries/LibJS/Contrib/Test262/262Object.cpp b/Libraries/LibJS/Contrib/Test262/262Object.cpp index d7dfe10c12..3e6c3ca1bb 100644 --- a/Libraries/LibJS/Contrib/Test262/262Object.cpp +++ b/Libraries/LibJS/Contrib/Test262/262Object.cpp @@ -92,7 +92,7 @@ JS_DEFINE_NATIVE_FUNCTION($262Object::detach_array_buffer) JS_DEFINE_NATIVE_FUNCTION($262Object::eval_script) { - auto source_text = TRY(vm.argument(0).to_string(vm)); + auto source_text = TRY(vm.argument(0).to_utf16_string(vm)); // 1. Let hostDefined be any host-defined values for the provided sourceText (obtained in an implementation dependent manner) @@ -100,7 +100,7 @@ JS_DEFINE_NATIVE_FUNCTION($262Object::eval_script) auto& realm = *vm.current_realm(); // 3. Let s be ParseScript(sourceText, realm, hostDefined). - auto script_or_error = Script::parse(source_text, realm); + auto script_or_error = Script::parse(source_text.to_utf8_but_should_be_ported_to_utf16(), realm); // 4. If s is a List of errors, then if (script_or_error.is_error()) { diff --git a/Libraries/LibJS/Contrib/Test262/GlobalObject.cpp b/Libraries/LibJS/Contrib/Test262/GlobalObject.cpp index b4b099cd06..479c8587f4 100644 --- a/Libraries/LibJS/Contrib/Test262/GlobalObject.cpp +++ b/Libraries/LibJS/Contrib/Test262/GlobalObject.cpp @@ -36,7 +36,7 @@ void GlobalObject::visit_edges(Cell::Visitor& visitor) JS_DEFINE_NATIVE_FUNCTION(GlobalObject::print) { - auto string = TRY(vm.argument(0).to_string(vm)); + auto string = TRY(vm.argument(0).to_utf16_string(vm)); outln("{}", string); return js_undefined(); } diff --git a/Libraries/LibJS/Runtime/AbstractOperations.cpp b/Libraries/LibJS/Runtime/AbstractOperations.cpp index 5e12e382de..e37ecaadb2 100644 --- a/Libraries/LibJS/Runtime/AbstractOperations.cpp +++ b/Libraries/LibJS/Runtime/AbstractOperations.cpp @@ -646,7 +646,7 @@ ThrowCompletionOr perform_eval(VM& vm, Value x, CallerMode strict_caller, // 6. NOTE: In the case of a direct eval, evalRealm is the realm of both the caller of eval and of the eval function itself. // 7. Perform ? HostEnsureCanCompileStrings(evalRealm, « », xStr, xStr, direct, « », x). - auto code_string_utf8 = code_string->utf8_string(); + auto code_string_utf8 = code_string->utf16_string_view().to_utf8_but_should_be_ported_to_utf16(); TRY(vm.host_ensure_can_compile_strings(eval_realm, {}, code_string_utf8, code_string_utf8, direct == EvalMode::Direct ? CompilationType::DirectEval : CompilationType::IndirectEval, {}, x)); // 8. Let inFunction be false. @@ -1360,8 +1360,7 @@ ThrowCompletionOr get_substitution(VM& vm, Utf16View const& matched auto digits = template_remainder.substring_view(1, digit_count); // iii. Let index be ℝ(StringToNumber(digits)). - auto utf8_digits = MUST(digits.to_utf8()); - auto index = static_cast(string_to_number(utf8_digits)); + auto index = static_cast(string_to_number(digits)); // iv. Assert: 0 ≤ index ≤ 99. VERIFY(index <= 99); @@ -1380,8 +1379,7 @@ ThrowCompletionOr get_substitution(VM& vm, Utf16View const& matched digits = digits.substring_view(0, 1); // 4. Set index to ℝ(StringToNumber(digits)). - utf8_digits = MUST(digits.to_utf8()); - index = static_cast(string_to_number(utf8_digits)); + index = static_cast(string_to_number(digits)); } // vii. Let ref be the substring of templateRemainder from 0 to 1 + digitCount. @@ -1958,8 +1956,12 @@ ThrowCompletionOr get_option(VM& vm, Object const& options, PropertyKey c // NOTE: Every location in the spec that invokes GetOption with type=boolean also has values=undefined. VERIFY(value.is_string()); - if (auto value_string = value.as_string().utf8_string(); !values.contains_slow(value_string)) - return vm.throw_completion(ErrorType::OptionIsNotValidValue, value_string, property.as_string()); + auto value_string = value.as_string().utf16_string_view(); + auto it = find_if(values.begin(), values.end(), [&](auto allowed_value) { return value_string == allowed_value; }); + if (it == values.end()) + return vm.throw_completion(ErrorType::OptionIsNotValidValue, value_string.to_utf8_but_should_be_ported_to_utf16(), property.as_string()); + + value = PrimitiveString::create(vm, *it); } // 6. Return value. @@ -1979,7 +1981,16 @@ ThrowCompletionOr get_rounding_mode_option(VM& vm, Object const& o auto string_value = TRY(get_option(vm, options, vm.names.roundingMode, OptionType::String, allowed_strings, string_fallback)); // 4. Return the value from the "Rounding Mode" column of the row with stringValue in its "String Identifier" column. - return static_cast(allowed_strings.first_index_of(string_value.as_string().utf8_string()).value()); + auto string = string_value.as_string().utf16_string_view(); + Optional index; + for (size_t i = 0; i < allowed_strings.size(); ++i) { + if (string == allowed_strings[i]) { + index = i; + break; + } + } + VERIFY(index.has_value()); + return static_cast(*index); } // 14.5.2.4 GetRoundingIncrementOption ( options ), https://tc39.es/proposal-temporal/#sec-temporal-getroundingincrementoption diff --git a/Libraries/LibJS/Runtime/AggregateErrorConstructor.cpp b/Libraries/LibJS/Runtime/AggregateErrorConstructor.cpp index 2829e508e0..900872637f 100644 --- a/Libraries/LibJS/Runtime/AggregateErrorConstructor.cpp +++ b/Libraries/LibJS/Runtime/AggregateErrorConstructor.cpp @@ -55,7 +55,7 @@ ThrowCompletionOr> AggregateErrorConstructor::construct(Function // 3. If message is not undefined, then if (!message.is_undefined()) { // a. Let msg be ? ToString(message). - auto msg = TRY(message.to_string(vm)); + auto msg = TRY(message.to_utf16_string(vm)); // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). aggregate_error->create_non_enumerable_data_property_or_throw(vm.names.message, PrimitiveString::create(vm, msg)); diff --git a/Libraries/LibJS/Runtime/Array.cpp b/Libraries/LibJS/Runtime/Array.cpp index 3ead3e14d3..bc0a768372 100644 --- a/Libraries/LibJS/Runtime/Array.cpp +++ b/Libraries/LibJS/Runtime/Array.cpp @@ -257,10 +257,10 @@ ThrowCompletionOr compare_array_elements(VM& vm, Value x, Value y, Funct return x.as_string().utf16_string_view() <=> y.as_string().utf16_string_view(); // 5. Let xString be ? ToString(x). - auto x_string = PrimitiveString::create(vm, TRY(x.to_string(vm))); + auto x_string = PrimitiveString::create(vm, TRY(x.to_utf16_string(vm))); // 6. Let yString be ? ToString(y). - auto y_string = PrimitiveString::create(vm, TRY(y.to_string(vm))); + auto y_string = PrimitiveString::create(vm, TRY(y.to_utf16_string(vm))); // 7. Let xSmaller be ! IsLessThan(xString, yString, true). auto x_smaller = MUST(is_less_than(vm, x_string, y_string, true)); diff --git a/Libraries/LibJS/Runtime/Date.cpp b/Libraries/LibJS/Runtime/Date.cpp index 322fa0c0f8..811391ab7b 100644 --- a/Libraries/LibJS/Runtime/Date.cpp +++ b/Libraries/LibJS/Runtime/Date.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -664,7 +665,8 @@ double time_clip(double time) bool is_offset_time_zone_identifier(StringView offset_string) { // 1. Let parseResult be ParseText(StringToCodePoints(offsetString), UTCOffset[~SubMinutePrecision]). - auto parse_result = Temporal::parse_utc_offset(offset_string, Temporal::SubMinutePrecision::No); + auto utf16_offset_string = Utf16String::from_utf8(offset_string); + auto parse_result = Temporal::parse_utc_offset(utf16_offset_string, Temporal::SubMinutePrecision::No); // 2. If parseResult is a List of errors, return false. // 3. Return true. @@ -676,7 +678,8 @@ bool is_offset_time_zone_identifier(StringView offset_string) ThrowCompletionOr parse_date_time_utc_offset(VM& vm, StringView offset_string) { // 1. Let parseResult be ParseText(offsetString, UTCOffset[+SubMinutePrecision]). - auto parse_result = Temporal::parse_utc_offset(offset_string, Temporal::SubMinutePrecision::Yes); + auto utf16_offset_string = Utf16String::from_utf8(offset_string); + auto parse_result = Temporal::parse_utc_offset(utf16_offset_string, Temporal::SubMinutePrecision::Yes); // 2. If parseResult is a List of errors, throw a RangeError exception. if (!parse_result.has_value()) @@ -692,7 +695,8 @@ double parse_date_time_utc_offset(StringView offset_string) // OPTIMIZATION: Some callers can assume that parsing will succeed. // 1. Let parseResult be ParseText(offsetString, UTCOffset[+SubMinutePrecision]). - auto parse_result = Temporal::parse_utc_offset(offset_string, Temporal::SubMinutePrecision::Yes); + auto utf16_offset_string = Utf16String::from_utf8(offset_string); + auto parse_result = Temporal::parse_utc_offset(utf16_offset_string, Temporal::SubMinutePrecision::Yes); VERIFY(parse_result.has_value()); return parse_date_time_utc_offset(*parse_result); @@ -751,13 +755,13 @@ double parse_date_time_utc_offset(Temporal::TimeZoneOffset const& parse_result) auto parsed_fraction = *parse_result.fraction; // b. Let fraction be the string-concatenation of CodePointsToString(parsedFraction) and "000000000". - auto fraction = ByteString::formatted("{}000000000", parsed_fraction); - // c. Let nanosecondsString be the substring of fraction from 1 to 10. - auto nanoseconds_string = fraction.substring_view(1, 9); - // d. Let nanoseconds be ℝ(StringToNumber(nanosecondsString)). - nanoseconds = string_to_number(nanoseconds_string); + for (size_t i = 1; i < 10; ++i) { + nanoseconds *= 10; + if (i < parsed_fraction.length_in_code_units()) + nanoseconds += parse_ascii_digit(static_cast(parsed_fraction.code_unit_at(i))); + } } // 17. Return sign × (((hours × 60 + minutes) × 60 + seconds) × 10^9 + nanoseconds). diff --git a/Libraries/LibJS/Runtime/DateConstructor.cpp b/Libraries/LibJS/Runtime/DateConstructor.cpp index 901fe7bf8d..14f0d5bb87 100644 --- a/Libraries/LibJS/Runtime/DateConstructor.cpp +++ b/Libraries/LibJS/Runtime/DateConstructor.cpp @@ -9,6 +9,7 @@ */ #include +#include #include #include #include @@ -32,6 +33,12 @@ static double parse_date_string(VM& vm, StringView date_string) return result; } +static double parse_date_string(VM& vm, Utf16View date_string) +{ + auto utf8_date_string = date_string.to_utf8_but_should_be_ported_to_utf16(); + return parse_date_string(vm, utf8_date_string.bytes_as_string_view()); +} + DateConstructor::DateConstructor(Realm& realm) : NativeFunction(realm.vm().names.Date.as_string(), realm.intrinsics().function_prototype()) { @@ -97,7 +104,7 @@ ThrowCompletionOr> DateConstructor::construct(FunctionObject& ne if (primitive.is_string()) { // 1. Assert: The next step never returns an abrupt completion because Type(v) is String. // 2. Let tv be the result of parsing v as a date, in exactly the same manner as for the parse method (21.4.3.2). - time_value = parse_date_string(vm, primitive.as_string().utf8_string()); + time_value = parse_date_string(vm, primitive.as_string().utf16_string_view()); } // iii. Else, else { @@ -174,7 +181,7 @@ JS_DEFINE_NATIVE_FUNCTION(DateConstructor::parse) // This function applies the ToString operator to its argument. If ToString results in an abrupt completion the // Completion Record is immediately returned. - auto date_string = TRY(vm.argument(0).to_string(vm)); + auto date_string = TRY(vm.argument(0).to_utf16_string(vm)); // Otherwise, this function interprets the resulting String as a date and time; it returns a Number, the UTC time // value corresponding to the date and time. diff --git a/Libraries/LibJS/Runtime/ErrorPrototype.cpp b/Libraries/LibJS/Runtime/ErrorPrototype.cpp index fc0baed0f3..040a4580a9 100644 --- a/Libraries/LibJS/Runtime/ErrorPrototype.cpp +++ b/Libraries/LibJS/Runtime/ErrorPrototype.cpp @@ -47,8 +47,8 @@ JS_DEFINE_NATIVE_FUNCTION(ErrorPrototype::to_string) // 4. If name is undefined, set name to "Error"; otherwise set name to ? ToString(name). auto name = name_property.is_undefined() - ? "Error"_string - : TRY(name_property.to_string(vm)); + ? "Error"_utf16 + : TRY(name_property.to_utf16_string(vm)); // 5. Let msg be ? Get(O, "message"). auto message_property = TRY(this_object->get(vm.names.message)); @@ -67,7 +67,7 @@ JS_DEFINE_NATIVE_FUNCTION(ErrorPrototype::to_string) return PrimitiveString::create(vm, move(name)); // 9. Return the string-concatenation of name, the code unit 0x003A (COLON), the code unit 0x0020 (SPACE), and msg. - return PrimitiveString::create(vm, MUST(String::formatted("{}: {}", name, message))); + return PrimitiveString::create(vm, Utf16String::formatted("{}: {}", name, message)); } // B.1.1 get Error.prototype.stack ( ), https://tc39.es/proposal-error-stacks/#sec-get-error.prototype-stack @@ -91,11 +91,11 @@ JS_DEFINE_NATIVE_FUNCTION(ErrorPrototype::stack_getter) // 4. Return ? GetStackString(error). // NOTE: These steps are not implemented based on the proposal, but to roughly follow behavior of other browsers. - String name {}; + Utf16String name {}; if (auto name_property = TRY(this_object->get(vm.names.name)); !name_property.is_undefined()) - name = TRY(name_property.to_string(vm)); + name = TRY(name_property.to_utf16_string(vm)); else - name = "Error"_string; + name = "Error"_utf16; Utf16String message {}; if (auto message_property = TRY(this_object->get(vm.names.message)); !message_property.is_undefined()) @@ -103,7 +103,7 @@ JS_DEFINE_NATIVE_FUNCTION(ErrorPrototype::stack_getter) auto header = message.is_empty() ? move(name) - : MUST(String::formatted("{}: {}", name, message)); + : Utf16String::formatted("{}: {}", name, message); auto string = PrimitiveString::create(vm, Utf16String::formatted("{}\n{}", header, error_data->stack_string())); error_data->set_cached_string(string); diff --git a/Libraries/LibJS/Runtime/FunctionConstructor.cpp b/Libraries/LibJS/Runtime/FunctionConstructor.cpp index 3542933fdb..f772934441 100644 --- a/Libraries/LibJS/Runtime/FunctionConstructor.cpp +++ b/Libraries/LibJS/Runtime/FunctionConstructor.cpp @@ -115,11 +115,11 @@ ThrowCompletionOr> FunctionConstructor::create // 8. For each element arg of parameterArgs, do for (auto const& parameter_value : parameter_args) { // a. Append ? ToString(arg) to parameterStrings. - parameter_strings.unchecked_append(TRY(parameter_value.to_string(vm))); + parameter_strings.unchecked_append(TRY(parameter_value.to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16()); } // 9. Let bodyString be ? ToString(bodyArg). - auto body_string = TRY(body_arg.to_string(vm)); + auto body_string = TRY(body_arg.to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16(); // 10. Let currentRealm be the current Realm Record. auto& realm = *vm.current_realm(); diff --git a/Libraries/LibJS/Runtime/GlobalObject.cpp b/Libraries/LibJS/Runtime/GlobalObject.cpp index 30eec103d1..85766eac4f 100644 --- a/Libraries/LibJS/Runtime/GlobalObject.cpp +++ b/Libraries/LibJS/Runtime/GlobalObject.cpp @@ -237,7 +237,7 @@ JS_DEFINE_NATIVE_FUNCTION(GlobalObject::parse_float) } // 1. Let inputString be ? ToString(string). - auto input_string = TRY(string.to_string(vm)); + auto input_string = TRY(string.to_utf16_string(vm)); // 2. Let trimmedString be ! TrimString(inputString, start). auto trimmed_string = MUST(trim_string(vm, PrimitiveString::create(vm, move(input_string)), TrimMode::Left)); @@ -249,17 +249,17 @@ JS_DEFINE_NATIVE_FUNCTION(GlobalObject::parse_float) // 5. Let parsedNumber be ParseText(StringToCodePoints(numberString), StrDecimalLiteral). // 6. Assert: parsedNumber is a Parse Node. // 7. Return StringNumericValue of parsedNumber. - auto trimmed_string_view = trimmed_string.bytes_as_string_view(); + auto trimmed_string_view = trimmed_string.utf16_view(); auto parsed_number = AK::parse_first_number(trimmed_string_view, TrimWhitespace::No); if (parsed_number.has_value()) return parsed_number->value; - auto first_code_point = *trimmed_string.code_points().begin(); + auto first_code_point = *trimmed_string.begin(); if (first_code_point == '-' || first_code_point == '+') trimmed_string_view = trimmed_string_view.substring_view(1); - if (trimmed_string_view.starts_with("Infinity"sv, AK::CaseSensitivity::CaseSensitive)) { + if (trimmed_string_view.starts_with("Infinity"sv)) { // Only an immediate - means we should return negative infinity return first_code_point == '-' ? js_negative_infinity() : js_infinity(); } @@ -273,12 +273,12 @@ JS_DEFINE_NATIVE_FUNCTION(GlobalObject::parse_int) auto string = vm.argument(0); // 1. Let inputString be ? ToString(string). - auto input_string = TRY(string.to_string(vm)); + auto input_string = TRY(string.to_utf16_string(vm)); // 2. Let S be ! TrimString(inputString, start). - String trimmed_string; + Utf16String trimmed_string; // OPTIMIZATION: We can skip the trimming step when the value already starts with an alphanumeric ASCII character. - if (input_string.is_empty() || is_ascii_alphanumeric(input_string.bytes_as_string_view()[0])) { + if (input_string.is_empty() || is_ascii_alphanumeric(input_string.code_unit_at(0))) { trimmed_string = input_string; } else { trimmed_string = MUST(trim_string(vm, PrimitiveString::create(vm, move(input_string)), TrimMode::Left)); @@ -288,12 +288,12 @@ JS_DEFINE_NATIVE_FUNCTION(GlobalObject::parse_int) auto sign = 1; // 4. If S is not empty and the first code unit of S is the code unit 0x002D (HYPHEN-MINUS), set sign to -1. - auto first_code_point = trimmed_string.is_empty() ? OptionalNone {} : Optional { *trimmed_string.code_points().begin() }; + auto first_code_point = trimmed_string.is_empty() ? OptionalNone {} : Optional { *trimmed_string.begin() }; if (first_code_point == 0x2Du) sign = -1; // 5. If S is not empty and the first code unit of S is the code unit 0x002B (PLUS SIGN) or the code unit 0x002D (HYPHEN-MINUS), remove the first code unit from S. - auto trimmed_view = trimmed_string.bytes_as_string_view(); + auto trimmed_view = trimmed_string.utf16_view(); if (first_code_point == 0x2Bu || first_code_point == 0x2Du) trimmed_view = trimmed_view.substring_view(1); @@ -322,7 +322,7 @@ JS_DEFINE_NATIVE_FUNCTION(GlobalObject::parse_int) // 10. If stripPrefix is true, then if (strip_prefix) { // a. If the length of S is at least 2 and the first two code units of S are either "0x" or "0X", then - if (trimmed_view.length() >= 2 && trimmed_view.substring_view(0, 2).equals_ignoring_ascii_case("0x"sv)) { + if (trimmed_view.length_in_code_units() >= 2 && trimmed_view.substring_view(0, 2).equals_ignoring_ascii_case("0x"sv)) { // i. Remove the first two code units from S. trimmed_view = trimmed_view.substring_view(2); @@ -346,7 +346,7 @@ JS_DEFINE_NATIVE_FUNCTION(GlobalObject::parse_int) bool had_digits = false; double number = 0; - for (auto code_point : Utf8View(trimmed_view)) { + for (auto code_point : trimmed_view) { auto digit = parse_digit(code_point); if (!digit.has_value()) break; diff --git a/Libraries/LibJS/Runtime/Intl/AbstractOperations.cpp b/Libraries/LibJS/Runtime/Intl/AbstractOperations.cpp index 2ddded130e..b0dcdc8ef6 100644 --- a/Libraries/LibJS/Runtime/Intl/AbstractOperations.cpp +++ b/Libraries/LibJS/Runtime/Intl/AbstractOperations.cpp @@ -307,7 +307,7 @@ ThrowCompletionOr> canonicalize_locale_list(VM& vm, Value locales // iv. Else, else { // 1. Let tag be ? ToString(kValue). - tag = TRY(key_value.to_string(vm)); + tag = TRY(key_value.to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16(); } // v. If IsWellFormedLanguageTag(tag) is false, throw a RangeError exception. @@ -451,7 +451,7 @@ ResolvedLocale resolve_locale(ReadonlySpan requested_locales, LocaleOpti Optional matcher_result; // 2. If matcher is "lookup", then - if (matcher.is_string() && matcher.as_string().utf8_string() == "lookup"sv) { + if (matcher.is_string() && matcher.as_string().utf16_string_view() == "lookup"sv) { // a. Let r be LookupMatchingLocaleByPrefix(availableLocales, requestedLocales). matcher_result = lookup_matching_locale_by_prefix(requested_locales); } @@ -639,7 +639,7 @@ ThrowCompletionOr resolve_options(VM& vm, IntlObject& object, V // d. If value is not undefined, then if (!value.is_undefined()) { // i. Set value to ! ToString(value). - auto value_string = MUST(value.to_string(vm)); + auto value_string = MUST(value.to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16(); // ii. If value cannot be matched by the type Unicode locale nonterminal, throw a RangeError exception. if (!Unicode::is_type_identifier(value_string)) @@ -688,7 +688,7 @@ ThrowCompletionOr> filter_locales(VM& vm, ReadonlySpan re Optional match; // a. If matcher is "lookup", then - if (matcher.as_string().utf8_string() == "lookup"sv) { + if (matcher.as_string().utf16_string_view() == "lookup"sv) { // i. Let match be LookupMatchingLocaleByPrefix(availableLocales, « locale »). match = lookup_matching_locale_by_prefix({ { locale } }); } @@ -743,12 +743,13 @@ ThrowCompletionOr get_boolean_or_string_number_format_option(VM return StringOrBoolean { false }; // 5. Let value be ? ToString(value). - auto value_string = TRY(value.to_string(vm)); + auto value_string = TRY(value.to_utf16_string(vm)); // 6. If stringValues does not contain value, throw a RangeError exception. - auto it = find(string_values.begin(), string_values.end(), value_string.bytes_as_string_view()); + auto value_string_view = value_string.utf16_view(); + auto it = find_if(string_values.begin(), string_values.end(), [&](auto allowed_value) { return value_string_view == allowed_value; }); if (it == string_values.end()) - return vm.throw_completion(ErrorType::OptionIsNotValidValue, value_string, property.as_string()); + return vm.throw_completion(ErrorType::OptionIsNotValidValue, value_string_view.to_utf8_but_should_be_ported_to_utf16(), property.as_string()); // 7. Return value. return StringOrBoolean { *it }; diff --git a/Libraries/LibJS/Runtime/Intl/CollatorConstructor.cpp b/Libraries/LibJS/Runtime/Intl/CollatorConstructor.cpp index 9e5f217ff0..1ed97a853a 100644 --- a/Libraries/LibJS/Runtime/Intl/CollatorConstructor.cpp +++ b/Libraries/LibJS/Runtime/Intl/CollatorConstructor.cpp @@ -69,7 +69,7 @@ ThrowCompletionOr> CollatorConstructor::construct(FunctionObject auto usage = TRY(get_option(vm, options, vm.names.usage, OptionType::String, { "sort"sv, "search"sv }, "sort"sv)); // 8. Set collator.[[Usage]] to usage. - collator->set_usage(usage.as_string().utf8_string()); + collator->set_usage(usage.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16()); // 9. If usage is "sort", then // a. Let localeData be %Intl.Collator%.[[SortLocaleData]]. @@ -113,7 +113,7 @@ ThrowCompletionOr> CollatorConstructor::construct(FunctionObject Optional sensitivity; if (!sensitivity_value.is_undefined()) - sensitivity = Unicode::sensitivity_from_string(sensitivity_value.as_string().utf8_string()); + sensitivity = Unicode::sensitivity_from_string(sensitivity_value.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16()); // 21. Let defaultIgnorePunctuation be resolvedLocaleData.[[ignorePunctuation]]. // NOTE: We do not acquire resolvedLocaleData.[[ignorePunctuation]] here. Instead, we let LibUnicode fill in the diff --git a/Libraries/LibJS/Runtime/Intl/DateTimeFormatConstructor.cpp b/Libraries/LibJS/Runtime/Intl/DateTimeFormatConstructor.cpp index 3f72a29e99..849abb22bc 100644 --- a/Libraries/LibJS/Runtime/Intl/DateTimeFormatConstructor.cpp +++ b/Libraries/LibJS/Runtime/Intl/DateTimeFormatConstructor.cpp @@ -4,6 +4,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include #include @@ -191,7 +192,7 @@ ThrowCompletionOr> create_date_time_format(VM& vm, Funct return vm.throw_completion(ErrorType::IntlInvalidDateTimeFormatOption, vm.names.timeZone, "a toLocaleString time zone"sv); // b. Set timeZone to ? ToString(timeZone). - time_zone = TRY(time_zone_value.to_string(vm)); + time_zone = TRY(time_zone_value.to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16(); } // 20. If IsTimeZoneOffsetString(timeZone) is true, then @@ -199,7 +200,8 @@ ThrowCompletionOr> create_date_time_format(VM& vm, Funct if (is_time_zone_offset_string) { // a. Let parseResult be ParseText(StringToCodePoints(timeZone), UTCOffset[~SubMinutePrecision]). - auto parse_result = Temporal::parse_utc_offset(time_zone, Temporal::SubMinutePrecision::No); + auto utf16_time_zone = Utf16String::from_utf8(time_zone); + auto parse_result = Temporal::parse_utc_offset(utf16_time_zone, Temporal::SubMinutePrecision::No); // b. Assert: parseResult is a Parse Node. VERIFY(parse_result.has_value()); @@ -275,7 +277,7 @@ ThrowCompletionOr> create_date_time_format(VM& vm, Funct // d. Set formatOptions.[[]] to value. if (!value.is_undefined()) { - option = Unicode::calendar_pattern_style_from_string(value.as_string().utf8_string()); + option = Unicode::calendar_pattern_style_from_string(value.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16()); // e. If value is not undefined, then // i. Set hasExplicitFormatComponents to true. @@ -294,14 +296,14 @@ ThrowCompletionOr> create_date_time_format(VM& vm, Funct // 29. Set dateTimeFormat.[[DateStyle]] to dateStyle. if (!date_style.is_undefined()) - date_time_format->set_date_style(date_style.as_string().utf8_string()); + date_time_format->set_date_style(date_style.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16()); // 30. Let timeStyle be ? GetOption(options, "timeStyle", string, « "full", "long", "medium", "short" », undefined). auto time_style = TRY(get_option(vm, *options, vm.names.timeStyle, OptionType::String, AK::Array { "full"sv, "long"sv, "medium"sv, "short"sv }, Empty {})); // 31. Set dateTimeFormat.[[TimeStyle]] to timeStyle. if (!time_style.is_undefined()) - date_time_format->set_time_style(time_style.as_string().utf8_string()); + date_time_format->set_time_style(time_style.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16()); // 32. Let formats be resolvedLocaleData.[[formats]].[[]]. diff --git a/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.cpp b/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.cpp index b4309085cf..7e8d7ebf1e 100644 --- a/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.cpp +++ b/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.cpp @@ -64,7 +64,7 @@ ThrowCompletionOr> DisplayNamesConstructor::construct(FunctionOb auto style = TRY(get_option(vm, *options, vm.names.style, OptionType::String, { "narrow"sv, "short"sv, "long"sv }, "long"sv)); // 7. Set displayNames.[[Style]] to style. - display_names->set_style(style.as_string().utf8_string()); + display_names->set_style(style.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16()); // 8. Let type be ? GetOption(options, "type", string, « "language", "region", "script", "currency", "calendar", "dateTimeField" », undefined). auto type = TRY(get_option(vm, *options, vm.names.type, OptionType::String, { "language"sv, "region"sv, "script"sv, "currency"sv, "calendar"sv, "dateTimeField"sv }, Empty {})); @@ -74,13 +74,13 @@ ThrowCompletionOr> DisplayNamesConstructor::construct(FunctionOb return vm.throw_completion(ErrorType::IsUndefined, "options.type"sv); // 10. Set displayNames.[[Type]] to type. - display_names->set_type(type.as_string().utf8_string()); + display_names->set_type(type.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16()); // 11. Let fallback be ? GetOption(options, "fallback", string, « "code", "none" », "code"). auto fallback = TRY(get_option(vm, *options, vm.names.fallback, OptionType::String, { "code"sv, "none"sv }, "code"sv)); // 12. Set displayNames.[[Fallback]] to fallback. - display_names->set_fallback(fallback.as_string().utf8_string()); + display_names->set_fallback(fallback.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16()); // 13. Set displayNames.[[Locale]] to r.[[Locale]]. display_names->set_locale(move(result.locale)); @@ -98,7 +98,7 @@ ThrowCompletionOr> DisplayNamesConstructor::construct(FunctionOb // 20. If type is "language", then if (display_names->type() == DisplayNames::Type::Language) { // a. Set displayNames.[[LanguageDisplay]] to languageDisplay. - display_names->set_language_display(language_display.as_string().utf8_string()); + display_names->set_language_display(language_display.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16()); // b. Set typeFields to typeFields.[[]]. // c. Assert: typeFields is a Record (see 12.2.3). diff --git a/Libraries/LibJS/Runtime/Intl/DisplayNamesPrototype.cpp b/Libraries/LibJS/Runtime/Intl/DisplayNamesPrototype.cpp index a5e8dcf7fa..fafe52c10b 100644 --- a/Libraries/LibJS/Runtime/Intl/DisplayNamesPrototype.cpp +++ b/Libraries/LibJS/Runtime/Intl/DisplayNamesPrototype.cpp @@ -74,11 +74,11 @@ JS_DEFINE_NATIVE_FUNCTION(DisplayNamesPrototype::of) auto display_names = TRY(typed_this_object(vm)); // 3. Let code be ? ToString(code). - code = PrimitiveString::create(vm, TRY(code.to_string(vm))); + code = PrimitiveString::create(vm, TRY(code.to_utf16_string(vm))); // 4. Let code be ? CanonicalCodeForDisplayNames(displayNames.[[Type]], code). - code = TRY(canonical_code_for_display_names(vm, display_names->type(), code.as_string().utf8_string())); - auto code_string = code.as_string().utf8_string(); + code = TRY(canonical_code_for_display_names(vm, display_names->type(), code.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16())); + auto code_string = code.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16(); // 5. Let fields be displayNames.[[Fields]]. // 6. If fields has a field [[]], return fields.[[]]. diff --git a/Libraries/LibJS/Runtime/Intl/DurationFormat.cpp b/Libraries/LibJS/Runtime/Intl/DurationFormat.cpp index 63587cb8e0..8c25c1f1b5 100644 --- a/Libraries/LibJS/Runtime/Intl/DurationFormat.cpp +++ b/Libraries/LibJS/Runtime/Intl/DurationFormat.cpp @@ -269,7 +269,7 @@ ThrowCompletionOr get_duration_unit_options display_default = "auto"sv; } } else { - style = DurationFormat::value_style_from_string(style_value.as_string().utf8_string()); + style = DurationFormat::value_style_from_string(style_value.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16()); } // 4. If style is "numeric" and IsFractionalSecondUnitName(unit) is true, then @@ -286,7 +286,7 @@ ThrowCompletionOr get_duration_unit_options // 6. Let display be ? GetOption(options, displayField, STRING, « "auto", "always" », displayDefault). auto display_value = TRY(get_option(vm, options, display_field, OptionType::String, { "auto"sv, "always"sv }, display_default)); - auto display = DurationFormat::display_from_string(display_value.as_string().utf8_string()); + auto display = DurationFormat::display_from_string(display_value.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16()); // 7. Perform ? ValidateDurationUnitStyle(unit, style, display, prevStyle). TRY(validate_duration_unit_style(vm, unit_property_key, style, display, previous_style, display_field)); diff --git a/Libraries/LibJS/Runtime/Intl/DurationFormatConstructor.cpp b/Libraries/LibJS/Runtime/Intl/DurationFormatConstructor.cpp index 5afe13e373..c8ae799b1d 100644 --- a/Libraries/LibJS/Runtime/Intl/DurationFormatConstructor.cpp +++ b/Libraries/LibJS/Runtime/Intl/DurationFormatConstructor.cpp @@ -83,7 +83,7 @@ ThrowCompletionOr> DurationFormatConstructor::construct(Function auto style = TRY(get_option(vm, *options, vm.names.style, OptionType::String, { "long"sv, "short"sv, "narrow"sv, "digital"sv }, "short"sv)); // 13. Set durationFormat.[[Style]] to style. - duration_format->set_style(style.as_string().utf8_string()); + duration_format->set_style(style.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16()); // 14. Let prevStyle be the empty String. Optional previous_style; diff --git a/Libraries/LibJS/Runtime/Intl/Intl.cpp b/Libraries/LibJS/Runtime/Intl/Intl.cpp index e8b3c073f5..58ab214c26 100644 --- a/Libraries/LibJS/Runtime/Intl/Intl.cpp +++ b/Libraries/LibJS/Runtime/Intl/Intl.cpp @@ -111,7 +111,7 @@ JS_DEFINE_NATIVE_FUNCTION(Intl::supported_values_of) auto& realm = *vm.current_realm(); // 1. Let key be ? ToString(key). - auto key = TRY(vm.argument(0).to_string(vm)); + auto key = TRY(vm.argument(0).to_utf16_string(vm)); Optional, ReadonlySpan>> list; diff --git a/Libraries/LibJS/Runtime/Intl/ListFormatConstructor.cpp b/Libraries/LibJS/Runtime/Intl/ListFormatConstructor.cpp index 1d229c934a..d15de16036 100644 --- a/Libraries/LibJS/Runtime/Intl/ListFormatConstructor.cpp +++ b/Libraries/LibJS/Runtime/Intl/ListFormatConstructor.cpp @@ -66,13 +66,13 @@ ThrowCompletionOr> ListFormatConstructor::construct(FunctionObje auto type = TRY(get_option(vm, *options, vm.names.type, OptionType::String, { "conjunction"sv, "disjunction"sv, "unit"sv }, "conjunction"sv)); // 8. Set listFormat.[[Type]] to type. - list_format->set_type(type.as_string().utf8_string()); + list_format->set_type(type.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16()); // 9. Let style be ? GetOption(options, "style", string, « "long", "short", "narrow" », "long"). auto style = TRY(get_option(vm, *options, vm.names.style, OptionType::String, { "long"sv, "short"sv, "narrow"sv }, "long"sv)); // 10. Set listFormat.[[Style]] to style. - list_format->set_style(style.as_string().utf8_string()); + list_format->set_style(style.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16()); // 11. Let resolvedLocaleData be r.[[LocaleData]]. // 12. Let dataLocaleTypes be resolvedLocaleData.[[]]. diff --git a/Libraries/LibJS/Runtime/Intl/LocaleConstructor.cpp b/Libraries/LibJS/Runtime/Intl/LocaleConstructor.cpp index cf6f0ec338..cd4d5d4ac6 100644 --- a/Libraries/LibJS/Runtime/Intl/LocaleConstructor.cpp +++ b/Libraries/LibJS/Runtime/Intl/LocaleConstructor.cpp @@ -37,10 +37,10 @@ static ThrowCompletionOr> get_string_option(VM& vm, Object cons if (option.is_undefined()) return OptionalNone {}; - if (validator && !validator(option.as_string().utf8_string())) + if (validator && !validator(option.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16())) return vm.throw_completion(ErrorType::OptionIsNotValidValue, option, property); - return option.as_string().utf8_string(); + return option.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16(); } // 15.1.2 UpdateLanguageId ( tag, options ), https://tc39.es/ecma402/#sec-updatelanguageid @@ -286,7 +286,7 @@ ThrowCompletionOr> LocaleConstructor::construct(FunctionObject& return locale_tag->locale(); // 9. Else, // a. Let tag be ? ToString(tag). - return tag_value.to_string(vm); + return TRY(tag_value.to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16(); }()); // 10. Set options to ? CoerceOptionsToObject(options). @@ -351,7 +351,7 @@ ThrowCompletionOr> LocaleConstructor::construct(FunctionObject& // 30. If kn is not undefined, set kn to ! ToString(kn). // 31. Set opt.[[kn]] to kn. if (!kn.is_undefined()) - opt.kn = TRY(kn.to_string(vm)); + opt.kn = TRY(kn.to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16(); // 32. Let numberingSystem be ? GetOption(options, "numberingSystem", STRING, EMPTY, undefined). // 33. If numberingSystem is not undefined, then diff --git a/Libraries/LibJS/Runtime/Intl/NumberFormat.cpp b/Libraries/LibJS/Runtime/Intl/NumberFormat.cpp index fc8c4b850b..3fe3c9a2ed 100644 --- a/Libraries/LibJS/Runtime/Intl/NumberFormat.cpp +++ b/Libraries/LibJS/Runtime/Intl/NumberFormat.cpp @@ -222,7 +222,7 @@ ThrowCompletionOr to_intl_mathematical_value(VM& vm, Value va // 3. If Type(primValue) is String, // a. Let str be primValue. - auto string = primitive_value.as_string().utf8_string(); + auto string = primitive_value.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16(); // Step 4 handled separately by the FIXME above. diff --git a/Libraries/LibJS/Runtime/Intl/NumberFormatConstructor.cpp b/Libraries/LibJS/Runtime/Intl/NumberFormatConstructor.cpp index 4e8103106e..e54efad7c9 100644 --- a/Libraries/LibJS/Runtime/Intl/NumberFormatConstructor.cpp +++ b/Libraries/LibJS/Runtime/Intl/NumberFormatConstructor.cpp @@ -78,7 +78,7 @@ ThrowCompletionOr> NumberFormatConstructor::construct(FunctionOb auto notation = TRY(get_option(vm, *options, vm.names.notation, OptionType::String, { "standard"sv, "scientific"sv, "engineering"sv, "compact"sv }, "standard"sv)); // 12. Set numberFormat.[[Notation]] to notation. - number_format->set_notation(notation.as_string().utf8_string()); + number_format->set_notation(notation.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16()); int default_min_fraction_digits = 0; int default_max_fraction_digits = 0; @@ -121,7 +121,7 @@ ThrowCompletionOr> NumberFormatConstructor::construct(FunctionOb // 18. If notation is "compact", then if (number_format->notation() == Unicode::Notation::Compact) { // a. Set numberFormat.[[CompactDisplay]] to compactDisplay. - number_format->set_compact_display(compact_display.as_string().utf8_string()); + number_format->set_compact_display(compact_display.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16()); // b. Set defaultUseGrouping to "min2". default_use_grouping = "min2"sv; @@ -150,7 +150,7 @@ ThrowCompletionOr> NumberFormatConstructor::construct(FunctionOb auto sign_display = TRY(get_option(vm, *options, vm.names.signDisplay, OptionType::String, { "auto"sv, "never"sv, "always"sv, "exceptZero"sv, "negative"sv }, "auto"sv)); // 25. Set numberFormat.[[SignDisplay]] to signDisplay. - number_format->set_sign_display(sign_display.as_string().utf8_string()); + number_format->set_sign_display(sign_display.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16()); // 26. If the implementation supports the normative optional constructor mode of 4.3 Note 1, then // a. Let this be the this value. @@ -202,7 +202,7 @@ ThrowCompletionOr set_number_format_digit_options(VM& vm, NumberFormatBase // 10. Let roundingPriority be ? GetOption(options, "roundingPriority", STRING, « "auto", "morePrecision", "lessPrecision" », "auto"). auto rounding_priority_option = TRY(get_option(vm, options, vm.names.roundingPriority, OptionType::String, { "auto"sv, "morePrecision"sv, "lessPrecision"sv }, "auto"sv)); - auto rounding_priority = rounding_priority_option.as_string().utf8_string(); + auto rounding_priority = rounding_priority_option.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16(); // 11. Let trailingZeroDisplay be ? GetOption(options, "trailingZeroDisplay", STRING, « "auto", "stripIfInteger" », "auto"). auto trailing_zero_display = TRY(get_option(vm, options, vm.names.trailingZeroDisplay, OptionType::String, { "auto"sv, "stripIfInteger"sv }, "auto"sv)); @@ -217,10 +217,10 @@ ThrowCompletionOr set_number_format_digit_options(VM& vm, NumberFormatBase intl_object.set_rounding_increment(*rounding_increment); // 15. Set intlObj.[[RoundingMode]] to roundingMode. - intl_object.set_rounding_mode(rounding_mode.as_string().utf8_string()); + intl_object.set_rounding_mode(rounding_mode.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16()); // 16. Set intlObj.[[TrailingZeroDisplay]] to trailingZeroDisplay. - intl_object.set_trailing_zero_display(trailing_zero_display.as_string().utf8_string()); + intl_object.set_trailing_zero_display(trailing_zero_display.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16()); // 17. If mnsd is undefined and mxsd is undefined, let hasSd be false. Otherwise, let hasSd be true. bool has_significant_digits = !min_significant_digits.is_undefined() || !max_significant_digits.is_undefined(); @@ -379,7 +379,7 @@ ThrowCompletionOr set_number_format_unit_options(VM& vm, NumberFormat& int auto style = TRY(get_option(vm, options, vm.names.style, OptionType::String, { "decimal"sv, "percent"sv, "currency"sv, "unit"sv }, "decimal"sv)); // 2. Set intlObj.[[Style]] to style. - intl_object.set_style(style.as_string().utf8_string()); + intl_object.set_style(style.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16()); // 3. Let currency be ? GetOption(options, "currency", STRING, EMPTY, undefined). auto currency = TRY(get_option(vm, options, vm.names.currency, OptionType::String, {}, Empty {})); @@ -392,7 +392,7 @@ ThrowCompletionOr set_number_format_unit_options(VM& vm, NumberFormat& int } // 5. Else, // a. If IsWellFormedCurrencyCode(currency) is false, throw a RangeError exception. - else if (!is_well_formed_currency_code(currency.as_string().utf8_string())) { + else if (!is_well_formed_currency_code(currency.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16())) { return vm.throw_completion(ErrorType::OptionIsNotValidValue, currency, "currency"sv); } @@ -413,7 +413,7 @@ ThrowCompletionOr set_number_format_unit_options(VM& vm, NumberFormat& int } // 10. Else, // a. If IsWellFormedUnitIdentifier(unit) is false, throw a RangeError exception. - else if (!is_well_formed_unit_identifier(unit.as_string().utf8_string())) { + else if (!is_well_formed_unit_identifier(unit.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16())) { return vm.throw_completion(ErrorType::OptionIsNotValidValue, unit, "unit"sv); } @@ -423,22 +423,22 @@ ThrowCompletionOr set_number_format_unit_options(VM& vm, NumberFormat& int // 12. If style is "currency", then if (intl_object.style() == Unicode::NumberFormatStyle::Currency) { // a. Set intlObj.[[Currency]] to the ASCII-uppercase of currency. - intl_object.set_currency(MUST(currency.as_string().utf8_string().to_uppercase())); + intl_object.set_currency(MUST(currency.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16().to_uppercase())); // c. Set intlObj.[[CurrencyDisplay]] to currencyDisplay. - intl_object.set_currency_display(currency_display.as_string().utf8_string()); + intl_object.set_currency_display(currency_display.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16()); // d. Set intlObj.[[CurrencySign]] to currencySign. - intl_object.set_currency_sign(currency_sign.as_string().utf8_string()); + intl_object.set_currency_sign(currency_sign.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16()); } // 13. If style is "unit", then if (intl_object.style() == Unicode::NumberFormatStyle::Unit) { // a. Set intlObj.[[Unit]] to unit. - intl_object.set_unit(unit.as_string().utf8_string()); + intl_object.set_unit(unit.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16()); // b. Set intlObj.[[UnitDisplay]] to unitDisplay. - intl_object.set_unit_display(unit_display.as_string().utf8_string()); + intl_object.set_unit_display(unit_display.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16()); } // 14. Return UNUSED. diff --git a/Libraries/LibJS/Runtime/Intl/PluralRulesConstructor.cpp b/Libraries/LibJS/Runtime/Intl/PluralRulesConstructor.cpp index e9f36caff8..7b5f9bf2c6 100644 --- a/Libraries/LibJS/Runtime/Intl/PluralRulesConstructor.cpp +++ b/Libraries/LibJS/Runtime/Intl/PluralRulesConstructor.cpp @@ -67,13 +67,13 @@ ThrowCompletionOr> PluralRulesConstructor::construct(FunctionObj auto type = TRY(get_option(vm, *options, vm.names.type, OptionType::String, AK::Array { "cardinal"sv, "ordinal"sv }, "cardinal"sv)); // 8. Set pluralRules.[[Type]] to t. - plural_rules->set_type(type.as_string().utf8_string()); + plural_rules->set_type(type.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16()); // 9. Let notation be ? GetOption(options, "notation", string, « "standard", "scientific", "engineering", "compact" », "standard"). auto notation = TRY(get_option(vm, *options, vm.names.notation, OptionType::String, { "standard"sv, "scientific"sv, "engineering"sv, "compact"sv }, "standard"sv)); // 10. Set pluralRules.[[Notation]] to notation. - plural_rules->set_notation(notation.as_string().utf8_string()); + plural_rules->set_notation(notation.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16()); // 11. Let compactDisplay be ? GetOption(options, "compactDisplay", string, « "short", "long" », "short"). auto compact_display = TRY(get_option(vm, *options, vm.names.compactDisplay, OptionType::String, { "short"sv, "long"sv }, "short"sv)); @@ -81,7 +81,7 @@ ThrowCompletionOr> PluralRulesConstructor::construct(FunctionObj // 12. If notation is "compact", then if (plural_rules->notation() == Unicode::Notation::Compact) { // a. Set pluralRules.[[CompactDisplay]] to compactDisplay. - plural_rules->set_compact_display(compact_display.as_string().utf8_string()); + plural_rules->set_compact_display(compact_display.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16()); } // 13. Perform ? SetNumberFormatDigitOptions(pluralRules, options, 0, 3, notation). diff --git a/Libraries/LibJS/Runtime/Intl/RelativeTimeFormatConstructor.cpp b/Libraries/LibJS/Runtime/Intl/RelativeTimeFormatConstructor.cpp index 724246badc..d4607b5a1b 100644 --- a/Libraries/LibJS/Runtime/Intl/RelativeTimeFormatConstructor.cpp +++ b/Libraries/LibJS/Runtime/Intl/RelativeTimeFormatConstructor.cpp @@ -75,13 +75,13 @@ ThrowCompletionOr> RelativeTimeFormatConstructor::construct(Func auto style = TRY(get_option(vm, *options, vm.names.style, OptionType::String, { "long"sv, "short"sv, "narrow"sv }, "long"sv)); // 11. Set relativeTimeFormat.[[Style]] to style. - relative_time_format->set_style(style.as_string().utf8_string()); + relative_time_format->set_style(style.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16()); // 12. Let numeric be ? GetOption(options, "numeric", STRING, « "always", "auto" », "always"). auto numeric = TRY(get_option(vm, *options, vm.names.numeric, OptionType::String, { "always"sv, "auto"sv }, "always"sv)); // 13. Set relativeTimeFormat.[[Numeric]] to numeric. - relative_time_format->set_numeric(numeric.as_string().utf8_string()); + relative_time_format->set_numeric(numeric.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16()); // 14. Let nfOptions be OrdinaryObjectCreate(null). // 15. Perform ! CreateDataPropertyOrThrow(nfOptions, "numberingSystem", relativeTimeFormat.[[NumberingSystem]]). diff --git a/Libraries/LibJS/Runtime/Intl/RelativeTimeFormatPrototype.cpp b/Libraries/LibJS/Runtime/Intl/RelativeTimeFormatPrototype.cpp index 72331ed64b..a7ffd8b05c 100644 --- a/Libraries/LibJS/Runtime/Intl/RelativeTimeFormatPrototype.cpp +++ b/Libraries/LibJS/Runtime/Intl/RelativeTimeFormatPrototype.cpp @@ -71,10 +71,10 @@ JS_DEFINE_NATIVE_FUNCTION(RelativeTimeFormatPrototype::format) auto value = TRY(vm.argument(0).to_number(vm)); // 4. Let unit be ? ToString(unit). - auto unit = TRY(vm.argument(1).to_string(vm)); + auto unit = TRY(vm.argument(1).to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16(); // 5. Return ? FormatRelativeTime(relativeTimeFormat, value, unit). - auto formatted = TRY(format_relative_time(vm, relative_time_format, value.as_double(), unit.bytes_as_string_view())); + auto formatted = TRY(format_relative_time(vm, relative_time_format, value.as_double(), unit)); return PrimitiveString::create(vm, move(formatted)); } @@ -89,10 +89,10 @@ JS_DEFINE_NATIVE_FUNCTION(RelativeTimeFormatPrototype::format_to_parts) auto value = TRY(vm.argument(0).to_number(vm)); // 4. Let unit be ? ToString(unit). - auto unit = TRY(vm.argument(1).to_string(vm)); + auto unit = TRY(vm.argument(1).to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16(); // 5. Return ? FormatRelativeTimeToParts(relativeTimeFormat, value, unit). - return TRY(format_relative_time_to_parts(vm, relative_time_format, value.as_double(), unit.bytes_as_string_view())); + return TRY(format_relative_time_to_parts(vm, relative_time_format, value.as_double(), unit)); } } diff --git a/Libraries/LibJS/Runtime/Intl/SegmenterConstructor.cpp b/Libraries/LibJS/Runtime/Intl/SegmenterConstructor.cpp index 288838655c..7768cc020f 100644 --- a/Libraries/LibJS/Runtime/Intl/SegmenterConstructor.cpp +++ b/Libraries/LibJS/Runtime/Intl/SegmenterConstructor.cpp @@ -67,7 +67,7 @@ ThrowCompletionOr> SegmenterConstructor::construct(FunctionObjec auto granularity = TRY(get_option(vm, *options, vm.names.granularity, OptionType::String, { "grapheme"sv, "word"sv, "sentence"sv }, "grapheme"sv)); // 9. Set segmenter.[[SegmenterGranularity]] to granularity. - segmenter->set_segmenter_granularity(granularity.as_string().utf8_string()); + segmenter->set_segmenter_granularity(granularity.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16()); auto locale_segmenter = Unicode::Segmenter::create(segmenter->locale(), segmenter->segmenter_granularity()); segmenter->set_segmenter(move(locale_segmenter)); diff --git a/Libraries/LibJS/Runtime/JSONObject.cpp b/Libraries/LibJS/Runtime/JSONObject.cpp index 2b302e719c..a65eba362f 100644 --- a/Libraries/LibJS/Runtime/JSONObject.cpp +++ b/Libraries/LibJS/Runtime/JSONObject.cpp @@ -445,7 +445,7 @@ JS_DEFINE_NATIVE_FUNCTION(JSONObject::parse) auto reviver = vm.argument(1); // 1. Let jsonString be ? ToString(text). - auto json_string = TRY(text.to_string(vm)); + auto json_string = TRY(text.to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16(); // 2. Let parseResult be ? ParseJSON(jsonString). // 3. Let unfiltered be parseResult.[[Value]]. @@ -996,7 +996,7 @@ JS_DEFINE_NATIVE_FUNCTION(JSONObject::raw_json) auto& realm = *vm.current_realm(); // 1. Let jsonString be ? ToString(text). - auto json_string = TRY(vm.argument(0).to_string(vm)); + auto json_string = TRY(vm.argument(0).to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16(); // 2. Throw a SyntaxError exception if jsonString is the empty String, or if either the first or last code unit of // jsonString is any of 0x0009 (CHARACTER TABULATION), 0x000A (LINE FEED), 0x000D (CARRIAGE RETURN), or diff --git a/Libraries/LibJS/Runtime/NumberPrototype.cpp b/Libraries/LibJS/Runtime/NumberPrototype.cpp index d085f3bcbb..355004852b 100644 --- a/Libraries/LibJS/Runtime/NumberPrototype.cpp +++ b/Libraries/LibJS/Runtime/NumberPrototype.cpp @@ -276,7 +276,7 @@ JS_DEFINE_NATIVE_FUNCTION(NumberPrototype::to_exponential) // 4. If x is not finite, return Number::toString(x). if (!number_value.is_finite_number()) - return PrimitiveString::create(vm, MUST(number_value.to_string(vm))); + return PrimitiveString::create(vm, MUST(number_value.to_utf16_string(vm))); // 5. If f < 0 or f > 100, throw a RangeError exception. if (fraction_digits < 0 || fraction_digits > 100) @@ -408,7 +408,7 @@ JS_DEFINE_NATIVE_FUNCTION(NumberPrototype::to_fixed) // 6. If x is not finite, return Number::toString(x). if (!number_value.is_finite_number()) - return PrimitiveString::create(vm, TRY(number_value.to_string(vm))); + return PrimitiveString::create(vm, TRY(number_value.to_utf16_string(vm))); // 7. Set x to ℝ(x). auto number = number_value.as_double(); @@ -424,7 +424,7 @@ JS_DEFINE_NATIVE_FUNCTION(NumberPrototype::to_fixed) // 10. If x ≥ 10^21, then // a. Let m be ! ToString(𝔽(x)). if (number >= 1e+21) - return PrimitiveString::create(vm, MUST(number_value.to_string(vm))); + return PrimitiveString::create(vm, MUST(number_value.to_utf16_string(vm))); // 11. Else, // a. Let n be an integer for which n / (10^f) - x is as close to zero as possible. If there are two such n, pick the larger n. @@ -489,14 +489,14 @@ JS_DEFINE_NATIVE_FUNCTION(NumberPrototype::to_precision) // 2. If precision is undefined, return ! ToString(x). if (precision_value.is_undefined()) - return PrimitiveString::create(vm, MUST(number_value.to_string(vm))); + return PrimitiveString::create(vm, MUST(number_value.to_utf16_string(vm))); // 3. Let p be ? ToIntegerOrInfinity(precision). auto precision = TRY(precision_value.to_integer_or_infinity(vm)); // 4. If x is not finite, return Number::toString(x). if (!number_value.is_finite_number()) - return PrimitiveString::create(vm, MUST(number_value.to_string(vm))); + return PrimitiveString::create(vm, MUST(number_value.to_utf16_string(vm))); // 5. If p < 1 or p > 100, throw a RangeError exception. if ((precision < 1) || (precision > 100)) @@ -629,7 +629,7 @@ JS_DEFINE_NATIVE_FUNCTION(NumberPrototype::to_string) // 5. If radixMV = 10, return ! ToString(x). if (radix_mv == 10) - return PrimitiveString::create(vm, MUST(number_value.to_string(vm))); + return PrimitiveString::create(vm, MUST(number_value.to_utf16_string(vm))); // 6. Return the String representation of this Number value using the radix specified by radixMV. Letters a-z are used for digits with values 10 through 35. The precise algorithm is implementation-defined, however the algorithm should be a generalization of that specified in 6.1.6.1.20. if (number_value.is_positive_infinity()) diff --git a/Libraries/LibJS/Runtime/ObjectPrototype.cpp b/Libraries/LibJS/Runtime/ObjectPrototype.cpp index fadc42890e..ac6dfa14f9 100644 --- a/Libraries/LibJS/Runtime/ObjectPrototype.cpp +++ b/Libraries/LibJS/Runtime/ObjectPrototype.cpp @@ -193,7 +193,7 @@ JS_DEFINE_NATIVE_FUNCTION(ObjectPrototype::to_string) if (!to_string_tag.is_string()) tag = builtin_tag; else { - custom_tag = to_string_tag.as_string().utf8_string(); + custom_tag = to_string_tag.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16(); tag = custom_tag; } diff --git a/Libraries/LibJS/Runtime/PrimitiveString.cpp b/Libraries/LibJS/Runtime/PrimitiveString.cpp index 28f5e9d14f..3e5ad361e9 100644 --- a/Libraries/LibJS/Runtime/PrimitiveString.cpp +++ b/Libraries/LibJS/Runtime/PrimitiveString.cpp @@ -211,11 +211,6 @@ bool PrimitiveString::is_empty() const VERIFY_NOT_REACHED(); } -String PrimitiveString::utf8_string() const -{ - return utf16_string_view().to_utf8_but_should_be_ported_to_utf16(); -} - Utf16String PrimitiveString::utf16_string() const { resolve_if_needed(); diff --git a/Libraries/LibJS/Runtime/PrimitiveString.h b/Libraries/LibJS/Runtime/PrimitiveString.h index c04b94b123..41884677fd 100644 --- a/Libraries/LibJS/Runtime/PrimitiveString.h +++ b/Libraries/LibJS/Runtime/PrimitiveString.h @@ -47,8 +47,6 @@ public: bool is_empty() const; - [[nodiscard]] String utf8_string() const; - [[nodiscard]] Utf16String utf16_string() const; [[nodiscard]] Utf16View utf16_string_view() const; bool has_utf16_string() const { return m_utf16_string.has_value(); } diff --git a/Libraries/LibJS/Runtime/RegExpConstructor.cpp b/Libraries/LibJS/Runtime/RegExpConstructor.cpp index 00337f8f55..a1510cf829 100644 --- a/Libraries/LibJS/Runtime/RegExpConstructor.cpp +++ b/Libraries/LibJS/Runtime/RegExpConstructor.cpp @@ -257,10 +257,10 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpConstructor::escape) return vm.throw_completion(ErrorType::NotAString, string); // 2. Let escaped be the empty String. + auto code_point_list = string.as_string().utf16_string_view(); Utf16StringBuilder escaped(string.as_string().utf16_string_view().length_in_code_units()); // 3. Let cpList be StringToCodePoints(S). - auto code_point_list = string.as_string().utf16_string_view(); // 4. For each code point c of cpList, do for (auto code_point : code_point_list) { diff --git a/Libraries/LibJS/Runtime/RegExpPrototype.cpp b/Libraries/LibJS/Runtime/RegExpPrototype.cpp index 64c2630c25..ebfffa2f14 100644 --- a/Libraries/LibJS/Runtime/RegExpPrototype.cpp +++ b/Libraries/LibJS/Runtime/RegExpPrototype.cpp @@ -556,7 +556,7 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_match) // 4. Let flags be ? ToString(? Get(rx, "flags")). static auto& cache = *new Bytecode::StaticPropertyLookupCache; auto flags_value = TRY(regexp_object->get(vm.names.flags, cache)); - auto flags = TRY(flags_value.to_string(vm)); + auto flags = TRY(flags_value.to_utf16_string(vm)); // 5. If flags does not contain "g", then if (!flags.contains('g')) { @@ -600,7 +600,7 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_match) // 1. Let matchStr be ? ToString(? Get(result, "0")). auto match_value = TRY(result.get(0)); - auto match_str = TRY(match_value.to_string(vm)); + auto match_str = TRY(match_value.to_utf16_string(vm)); // 2. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(n)), matchStr). array->indexed_put(n, PrimitiveString::create(vm, match_str)); @@ -634,7 +634,7 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_match_all) // 5. Let flags be ? ToString(? Get(R, "flags")). static auto& cache = *new Bytecode::StaticPropertyLookupCache; auto flags_value = TRY(regexp_object->get(vm.names.flags, cache)); - auto flags = TRY(flags_value.to_string(vm)); + auto flags = TRY(flags_value.to_utf16_string(vm)); // Steps 9-12 are performed early so that flags can be moved. @@ -908,14 +908,14 @@ ThrowCompletionOr RegExpPrototype::symbol_replace_impl(VM& vm, Object& re // 6. If functionalReplace is false, then if (!replace_value.is_function()) { // a. Set replaceValue to ? ToString(replaceValue). - auto replace_string = TRY(replace_value.to_string(vm)); + auto replace_string = TRY(replace_value.to_utf16_string(vm)); replace_value = PrimitiveString::create(vm, move(replace_string)); } // 7. Let flags be ? ToString(? Get(rx, "flags")). static auto& cache = *new Bytecode::StaticPropertyLookupCache; auto flags_value = TRY(regexp_object.get(vm.names.flags, cache)); - auto flags = TRY(flags_value.to_string(vm)); + auto flags = TRY(flags_value.to_utf16_string(vm)); // 8. If flags contains "g", let global be true. Otherwise, let global be false. bool global = flags.contains('g'); @@ -953,7 +953,7 @@ ThrowCompletionOr RegExpPrototype::symbol_replace_impl(VM& vm, Object& re // 1. Let matchStr be ? ToString(? Get(result, "0")). auto match_value = TRY(result.get(vm, 0)); - auto match_str = TRY(match_value.to_string(vm)); + auto match_str = TRY(match_value.to_utf16_string(vm)); // 2. If matchStr is the empty String, then if (match_str.is_empty()) { @@ -1007,7 +1007,7 @@ ThrowCompletionOr RegExpPrototype::symbol_replace_impl(VM& vm, Object& re // ii. If capN is not undefined, then if (!capture.is_undefined()) { // 1. Set capN to ? ToString(capN). - capture = PrimitiveString::create(vm, TRY(capture.to_string(vm))); + capture = PrimitiveString::create(vm, TRY(capture.to_utf16_string(vm))); } // iii. Append capN as the last element of captures. @@ -1316,7 +1316,7 @@ ThrowCompletionOr RegExpPrototype::symbol_split_impl(VM& vm, Object& rege // 5. Let flags be ? ToString(? Get(rx, "flags")). static auto& cache = *new Bytecode::StaticPropertyLookupCache; auto flags_value = TRY(regexp_object.get(vm.names.flags, cache)); - auto flags = TRY(flags_value.to_string(vm)); + auto flags = TRY(flags_value.to_utf16_string(vm)); // 6. If flags contains "u" or flags contains "v", let unicodeMatching be true. // 7. Else, let unicodeMatching be false. @@ -1324,7 +1324,7 @@ ThrowCompletionOr RegExpPrototype::symbol_split_impl(VM& vm, Object& rege // 8. If flags contains "y", let newFlags be flags. // 9. Else, let newFlags be the string-concatenation of flags and "y". - auto new_flags = flags.bytes_as_string_view().find('y').has_value() ? move(flags) : MUST(String::formatted("{}y", flags)); + auto new_flags = flags.contains('y') ? move(flags) : Utf16String::formatted("{}y", flags); // 10. Let splitter be ? Construct(C, « rx, newFlags »). auto splitter = TRY(construct(vm, *constructor, ®exp_object, PrimitiveString::create(vm, move(new_flags)))); @@ -1544,12 +1544,12 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::to_string) // 3. Let pattern be ? ToString(? Get(R, "source")). static auto& cache = *new Bytecode::StaticPropertyLookupCache; auto source_attr = TRY(regexp_object->get(vm.names.source, cache)); - auto pattern = TRY(source_attr.to_string(vm)); + auto pattern = TRY(source_attr.to_utf16_string(vm)); // 4. Let flags be ? ToString(? Get(R, "flags")). static auto& cache2 = *new Bytecode::StaticPropertyLookupCache; auto flags_attr = TRY(regexp_object->get(vm.names.flags, cache2)); - auto flags = TRY(flags_attr.to_string(vm)); + auto flags = TRY(flags_attr.to_utf16_string(vm)); // 5. Let result be the string-concatenation of "/", pattern, "/", and flags. // 6. Return result. diff --git a/Libraries/LibJS/Runtime/StringPrototype.cpp b/Libraries/LibJS/Runtime/StringPrototype.cpp index dca8874a02..23447708a8 100644 --- a/Libraries/LibJS/Runtime/StringPrototype.cpp +++ b/Libraries/LibJS/Runtime/StringPrototype.cpp @@ -40,7 +40,7 @@ GC_DEFINE_ALLOCATOR(StringPrototype); static ThrowCompletionOr utf8_string_from(VM& vm) { auto this_value = TRY(require_object_coercible(vm, vm.this_value())); - return TRY(this_value.to_string(vm)); + return TRY(this_value.to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16(); } static ThrowCompletionOr> primitive_string_from(VM& vm) @@ -673,7 +673,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::match_all) auto flags_object = TRY(require_object_coercible(vm, flags)); // iii. If ? ToString(flags) does not contain "g", throw a TypeError exception. - auto flags_string = TRY(flags_object.to_string(vm)); + auto flags_string = TRY(flags_object.to_utf16_string(vm)); if (!flags_string.contains('g')) return vm.throw_completion(ErrorType::StringNonGlobalRegExp); } @@ -714,7 +714,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::normalize) } // 4. Else, let f be ? ToString(form). else { - form = TRY(form_value.to_string(vm)); + form = TRY(form_value.to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16(); } // 5. If f is not one of "NFC", "NFD", "NFKC", or "NFKD", throw a RangeError exception. @@ -953,7 +953,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::replace_all) auto flags_object = TRY(require_object_coercible(vm, flags)); // iii. If ? ToString(flags) does not contain "g", throw a TypeError exception. - if (!TRY(flags_object.to_string(vm)).contains('g')) + if (!TRY(flags_object.to_utf16_string(vm)).contains('g')) return vm.throw_completion(ErrorType::StringNonGlobalRegExp); } @@ -1479,23 +1479,23 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::to_well_formed) } // 22.1.3.32.1 TrimString ( string, where ), https://tc39.es/ecma262/#sec-trimstring -ThrowCompletionOr trim_string(VM& vm, Value input_value, TrimMode where) +ThrowCompletionOr trim_string(VM& vm, Value input_value, TrimMode where) { // 1. Let str be ? RequireObjectCoercible(string). auto input_string = TRY(require_object_coercible(vm, input_value)); // 2. Let S be ? ToString(str). - auto string = TRY(input_string.to_string(vm)); + auto string = TRY(input_string.to_utf16_string(vm)); // 3. If where is start, let T be the String value that is a copy of S with leading white space removed. // 4. Else if where is end, let T be the String value that is a copy of S with trailing white space removed. // 5. Else, // a. Assert: where is start+end. // b. Let T be the String value that is a copy of S with both leading and trailing white space removed. - auto trimmed_string = Utf8View(string).trim(whitespace_characters, where).as_string(); + auto trimmed_string = string.trim(whitespace_characters, where); // 6. Return T. - return MUST(String::from_utf8(trimmed_string)); + return trimmed_string; } // 22.1.3.32 String.prototype.trim ( ), https://tc39.es/ecma262/#sec-string.prototype.trim @@ -1537,7 +1537,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::symbol_iterator) 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_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16(); // 3. Let closure be a new Abstract Closure with no parameters that captures s and performs the following steps when called: // ... @@ -1605,7 +1605,7 @@ static ThrowCompletionOr create_html(VM& vm, Value string, StringView tag auto value_string = TRY(value.to_utf16_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 """. - auto escaped_value_string = value_string.replace("\""sv, """sv, ReplaceMode::All); + auto escaped_value_string = value_string.replace(u'"', """sv, ReplaceMode::All); // c. Set p1 to the string-concatenation of: // - p1 diff --git a/Libraries/LibJS/Runtime/StringPrototype.h b/Libraries/LibJS/Runtime/StringPrototype.h index acc0e32051..2998f32d7e 100644 --- a/Libraries/LibJS/Runtime/StringPrototype.h +++ b/Libraries/LibJS/Runtime/StringPrototype.h @@ -6,7 +6,7 @@ #pragma once -#include +#include #include namespace JS { @@ -20,8 +20,35 @@ struct CodePoint { Optional string_index_of(Utf16View const& string, Utf16View const& search_value, size_t from_index); Optional string_last_index_of(Utf16View const& string, Utf16View const& search_value, size_t from_index); CodePoint code_point_at(Utf16View const& string, size_t position); -static constexpr Utf8View whitespace_characters = Utf8View("\x09\x0A\x0B\x0C\x0D\x20\xC2\xA0\xE1\x9A\x80\xE2\x80\x80\xE2\x80\x81\xE2\x80\x82\xE2\x80\x83\xE2\x80\x84\xE2\x80\x85\xE2\x80\x86\xE2\x80\x87\xE2\x80\x88\xE2\x80\x89\xE2\x80\x8A\xE2\x80\xAF\xE2\x81\x9F\xE3\x80\x80\xE2\x80\xA8\xE2\x80\xA9\xEF\xBB\xBF"sv); -ThrowCompletionOr trim_string(VM&, Value string, TrimMode where); +static constexpr char16_t whitespace_character_code_units[] = { + u'\u0009', + u'\u000A', + u'\u000B', + u'\u000C', + u'\u000D', + u'\u0020', + u'\u00A0', + u'\u1680', + u'\u2000', + u'\u2001', + u'\u2002', + u'\u2003', + u'\u2004', + u'\u2005', + u'\u2006', + u'\u2007', + u'\u2008', + u'\u2009', + u'\u200A', + u'\u2028', + u'\u2029', + u'\u202F', + u'\u205F', + u'\u3000', + u'\uFEFF', +}; +static constexpr Utf16View whitespace_characters { whitespace_character_code_units, 25 }; +ThrowCompletionOr trim_string(VM&, Value string, TrimMode where); class StringPrototype final : public StringObject { JS_OBJECT(StringPrototype, StringObject); diff --git a/Libraries/LibJS/Runtime/SuppressedErrorConstructor.cpp b/Libraries/LibJS/Runtime/SuppressedErrorConstructor.cpp index bf97216374..5fef84a2dc 100644 --- a/Libraries/LibJS/Runtime/SuppressedErrorConstructor.cpp +++ b/Libraries/LibJS/Runtime/SuppressedErrorConstructor.cpp @@ -54,7 +54,7 @@ ThrowCompletionOr> SuppressedErrorConstructor::construct(Functio // 3. If message is not undefined, then if (!message.is_undefined()) { // a. Let msg be ? ToString(message). - auto msg = TRY(message.to_string(vm)); + auto msg = TRY(message.to_utf16_string(vm)); // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). suppressed_error->create_non_enumerable_data_property_or_throw(vm.names.message, PrimitiveString::create(vm, move(msg))); diff --git a/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp b/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp index e3712ac9d4..278f3dd509 100644 --- a/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp +++ b/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp @@ -90,7 +90,7 @@ ThrowCompletionOr get_temporal_overflow_option(VM& vm, Object const& o auto string_value = TRY(get_option(vm, options, vm.names.overflow, OptionType::String, { "constrain"sv, "reject"sv }, "constrain"sv)); // 2. If stringValue is "constrain", return CONSTRAIN. - if (string_value.as_string().utf8_string() == "constrain"sv) + if (string_value.as_string().utf16_string_view() == "constrain"sv) return Overflow::Constrain; // 3. Return REJECT. @@ -102,7 +102,7 @@ ThrowCompletionOr get_temporal_disambiguation_option(VM& vm, Obj { // 1. Let stringValue be ? GetOption(options, "disambiguation", STRING, « "compatible", "earlier", "later", "reject" », "compatible"). auto string_value = TRY(get_option(vm, options, vm.names.disambiguation, OptionType::String, { "compatible"sv, "earlier"sv, "later"sv, "reject"sv }, "compatible"sv)); - auto string_view = string_value.as_string().utf8_string(); + auto string_view = string_value.as_string().utf16_string_view(); // 2. If stringValue is "compatible", return COMPATIBLE. if (string_view == "compatible"sv) @@ -166,7 +166,7 @@ ThrowCompletionOr get_temporal_offset_option(VM& vm, Object const& // 5. Let stringValue be ? GetOption(options, "offset", STRING, « "prefer", "use", "ignore", "reject" », stringFallback). auto string_value = TRY(get_option(vm, options, vm.names.offset, OptionType::String, { "prefer"sv, "use"sv, "ignore"sv, "reject"sv }, string_fallback)); - auto string_view = string_value.as_string().utf8_string(); + auto string_view = string_value.as_string().utf16_string_view(); // 6. If stringValue is "prefer", return PREFER. if (string_view == "prefer"sv) @@ -189,7 +189,7 @@ ThrowCompletionOr get_temporal_show_calendar_name_option(VM& vm, O { // 1. Let stringValue be ? GetOption(options, "calendarName", STRING, « "auto", "always", "never", "critical" », "auto"). auto string_value = TRY(get_option(vm, options, vm.names.calendarName, OptionType::String, { "auto"sv, "always"sv, "never"sv, "critical"sv }, "auto"sv)); - auto string_view = string_value.as_string().utf8_string(); + auto string_view = string_value.as_string().utf16_string_view(); // 2. If stringValue is "always", return ALWAYS. if (string_view == "always"sv) @@ -212,7 +212,7 @@ ThrowCompletionOr get_temporal_show_time_zone_name_option(VM& { // 1. Let stringValue be ? GetOption(options, "timeZoneName", STRING, « "auto", "never", "critical" », "auto"). auto string_value = TRY(get_option(vm, options, vm.names.timeZoneName, OptionType::String, { "auto"sv, "never"sv, "critical"sv }, "auto"sv)); - auto string_view = string_value.as_string().utf8_string(); + auto string_view = string_value.as_string().utf16_string_view(); // 2. If stringValue is "never", return NEVER. if (string_view == "never"sv) @@ -231,7 +231,7 @@ ThrowCompletionOr get_temporal_show_offset_option(VM& vm, Object con { // 1. Let stringValue be ? GetOption(options, "offset", STRING, « "auto", "never" », "auto"). auto string_value = TRY(get_option(vm, options, vm.names.offset, OptionType::String, { "auto"sv, "never"sv }, "auto"sv)); - auto string_view = string_value.as_string().utf8_string(); + auto string_view = string_value.as_string().utf16_string_view(); // 2. If stringValue is "never", return never. if (string_view == "never"sv) @@ -246,7 +246,7 @@ ThrowCompletionOr get_direction_option(VM& vm, Object const& options) { // 1. Let stringValue be ? GetOption(options, "direction", STRING, « "next", "previous" », REQUIRED). auto string_value = TRY(get_option(vm, options, vm.names.direction, OptionType::String, { "next"sv, "previous"sv }, Required {})); - auto string_view = string_value.as_string().utf8_string(); + auto string_view = string_value.as_string().utf16_string_view(); // 2. If stringValue is "next", return NEXT. if (string_view == "next"sv) @@ -300,7 +300,7 @@ ThrowCompletionOr get_temporal_fractional_second_digits_option(VM& vm // 3. If digitsValue is not a Number, then if (!digits_value.is_number()) { // a. If ? ToString(digitsValue) is not "auto", throw a RangeError exception. - auto digits_value_string = TRY(digits_value.to_string(vm)); + auto digits_value_string = TRY(digits_value.to_utf16_string(vm)); if (digits_value_string != "auto"sv) return vm.throw_completion(ErrorType::OptionIsNotValidValue, digits_value, vm.names.fractionalSecondDigits); @@ -415,7 +415,7 @@ ThrowCompletionOr get_temporal_unit_valued_option(VM& vm, Object cons if (value.is_undefined()) return UnitValue { Unset {} }; - auto value_string = value.as_string().utf8_string(); + auto value_string = value.as_string().utf16_string_view(); // 8. If value is "auto", return AUTO. if (value_string == "auto"sv) @@ -544,7 +544,7 @@ ThrowCompletionOr get_temporal_relative_to_option(VM& vm, Object con return vm.throw_completion(ErrorType::NotAString, vm.names.relativeTo); // b. Let result be ? ParseISODateTime(value, « TemporalDateTimeString[+Zoned], TemporalDateTimeString[~Zoned] »). - auto result = TRY(parse_iso_date_time(vm, value.as_string().utf8_string(), { { Production::TemporalZonedDateTimeString, Production::TemporalDateTimeString } })); + auto result = TRY(parse_iso_date_time(vm, value.as_string().utf16_string_view(), { { Production::TemporalZonedDateTimeString, Production::TemporalDateTimeString } })); // c. Let offsetString be result.[[TimeZone]].[[OffsetString]]. offset_string = move(result.time_zone.offset_string); @@ -560,7 +560,8 @@ ThrowCompletionOr get_temporal_relative_to_option(VM& vm, Object con // f. Else, else { // i. Let timeZone be ? ToTemporalTimeZoneIdentifier(annotation). - time_zone = TRY(to_temporal_time_zone_identifier(vm, *annotation)); + auto utf16_annotation = Utf16String::from_utf8(*annotation); + time_zone = TRY(to_temporal_time_zone_identifier(vm, utf16_annotation)); // ii. If result.[[TimeZone]].[[Z]] is true, then if (result.time_zone.z_designator) { @@ -579,7 +580,8 @@ ThrowCompletionOr get_temporal_relative_to_option(VM& vm, Object con // v. If offsetString is not EMPTY, then if (offset_string.has_value()) { // 1. Let offsetParseResult be ParseText(StringToCodePoints(offsetString), UTCOffset[+SubMinutePrecision]). - auto offset_parse_result = parse_utc_offset(*offset_string, SubMinutePrecision::Yes); + auto utf16_offset_string = Utf16String::from_utf8(*offset_string); + auto offset_parse_result = parse_utc_offset(utf16_offset_string, SubMinutePrecision::Yes); // 2. Assert: offsetParseResult is a Parse Node. VERIFY(offset_parse_result.has_value()); @@ -618,7 +620,7 @@ ThrowCompletionOr get_temporal_relative_to_option(VM& vm, Object con // 8. If offsetBehaviour is OPTION, then if (offset_behavior == OffsetBehavior::Option) { // a. Let offsetNs be ! ParseDateTimeUTCOffset(offsetString). - offset_nanoseconds = parse_date_time_utc_offset(*offset_string); + offset_nanoseconds = parse_date_time_utc_offset(offset_string->bytes_as_string_view()); } // 9. Else, else { @@ -1095,7 +1097,7 @@ Crypto::SignedBigInteger round_number_to_increment_as_if_positive(Crypto::Signed } // 13.35 ParseISODateTime ( isoString, allowedFormats ), https://tc39.es/proposal-temporal/#sec-temporal-parseisodatetime -ThrowCompletionOr parse_iso_date_time(VM& vm, StringView iso_string, ReadonlySpan allowed_formats) +ThrowCompletionOr parse_iso_date_time(VM& vm, Utf16View iso_string, ReadonlySpan allowed_formats) { // 1. Let parseResult be EMPTY. Optional parse_result; @@ -1133,7 +1135,7 @@ ThrowCompletionOr parse_iso_date_time(VM& vm, StringView iso_ // i. If calendar is EMPTY, then if (!calendar.has_value()) { // i. Set calendar to CodePointsToString(value). - calendar = String::from_utf8_without_validation(value.bytes()); + calendar = value.to_utf8_but_should_be_ported_to_utf16(); // ii. If annotation contains an AnnotationCriticalFlag Parse Node, set calendarWasCritical to true. if (annotation.critical) @@ -1234,28 +1236,31 @@ ThrowCompletionOr parse_iso_date_time(VM& vm, StringView iso_ // 19. If fSeconds is not empty, then if (!fractional_seconds.is_empty()) { // a. Let fSecondsDigits be the substring of CodePointsToString(fSeconds) from 1. + auto parse_fractional_digits = [](Utf16View digits, size_t offset) { + double value = 0; + for (size_t i = 0; i < 3; ++i) { + value *= 10; + auto index = offset + i; + if (index < digits.length_in_code_units()) + value += parse_ascii_digit(static_cast(digits.code_unit_at(index))); + } + return value; + }; + auto fractional_seconds_digits = fractional_seconds.substring_view(1); // b. Let fSecondsDigitsExtended be the string-concatenation of fSecondsDigits and "000000000". - auto fractional_seconds_extended = MUST(String::formatted("{}000000000", fractional_seconds_digits)); - // c. Let millisecond be the substring of fSecondsDigitsExtended from 0 to 3. - auto millisecond = fractional_seconds_extended.bytes_as_string_view().substring_view(0, 3); + // f. Let millisecondMV be ℝ(StringToNumber(millisecond)). + millisecond_value = parse_fractional_digits(fractional_seconds_digits, 0); // d. Let microsecond be the substring of fSecondsDigitsExtended from 3 to 6. - auto microsecond = fractional_seconds_extended.bytes_as_string_view().substring_view(3, 3); + // g. Let microsecondMV be ℝ(StringToNumber(microsecond)). + microsecond_value = parse_fractional_digits(fractional_seconds_digits, 3); // e. Let nanosecond be the substring of fSecondsDigitsExtended from 6 to 9. - auto nanosecond = fractional_seconds_extended.bytes_as_string_view().substring_view(6, 3); - - // f. Let millisecondMV be ℝ(StringToNumber(millisecond)). - millisecond_value = string_to_number(millisecond); - - // g. Let microsecondMV be ℝ(StringToNumber(microsecond)). - microsecond_value = string_to_number(microsecond); - // h. Let nanosecondMV be ℝ(StringToNumber(nanosecond)). - nanosecond_value = string_to_number(nanosecond); + nanosecond_value = parse_fractional_digits(fractional_seconds_digits, 6); } // 20. Else, else { @@ -1286,7 +1291,7 @@ ThrowCompletionOr parse_iso_date_time(VM& vm, StringView iso_ if (parse_result->time_zone_identifier.has_value()) { // a. Let identifier be the source text matched by the TimeZoneIdentifier Parse Node contained within parseResult. // b. Set timeZoneResult.[[TimeZoneAnnotation]] to CodePointsToString(identifier). - time_zone_result.time_zone_annotation = String::from_utf8_without_validation(parse_result->time_zone_identifier->bytes()); + time_zone_result.time_zone_annotation = parse_result->time_zone_identifier->to_utf8_but_should_be_ported_to_utf16(); } // 26. If parseResult contains a UTCDesignator Parse Node, then @@ -1298,7 +1303,7 @@ ThrowCompletionOr parse_iso_date_time(VM& vm, StringView iso_ else if (parse_result->date_time_offset.has_value()) { // a. Let offset be the source text matched by the UTCOffset[+SubMinutePrecision] Parse Node contained within parseResult. // b. Set timeZoneResult.[[OffsetString]] to CodePointsToString(offset). - time_zone_result.offset_string = String::from_utf8_without_validation(parse_result->date_time_offset->source_text.bytes()); + time_zone_result.offset_string = parse_result->date_time_offset->source_text.to_utf8_but_should_be_ported_to_utf16(); } // 28. If yearAbsent is true, let yearReturn be EMPTY; else let yearReturn be yearMV. @@ -1311,7 +1316,7 @@ ThrowCompletionOr parse_iso_date_time(VM& vm, StringView iso_ } // 13.36 ParseTemporalCalendarString ( string ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporalcalendarstring -ThrowCompletionOr parse_temporal_calendar_string(VM& vm, String const& string) +ThrowCompletionOr parse_temporal_calendar_string(VM& vm, Utf16View string) { // 1. Let parseResult be Completion(ParseISODateTime(string, « TemporalDateTimeString[+Zoned], TemporalDateTimeString[~Zoned], // TemporalInstantString, TemporalTimeString, TemporalMonthDayString, TemporalYearMonthString »)). @@ -1341,14 +1346,14 @@ ThrowCompletionOr parse_temporal_calendar_string(VM& vm, String const& s // 4. If parseResult is a List of errors, throw a RangeError exception. if (!annotation_parse_result.has_value()) - return vm.throw_completion(ErrorType::TemporalInvalidCalendarString, string); + return vm.throw_completion(ErrorType::TemporalInvalidCalendarString, string.to_utf8_but_should_be_ported_to_utf16()); // 5. Return string. - return string; + return string.to_utf8_but_should_be_ported_to_utf16(); } // 13.37 ParseTemporalDurationString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporaldurationstring -ThrowCompletionOr> parse_temporal_duration_string(VM& vm, StringView iso_string) +ThrowCompletionOr> parse_temporal_duration_string(VM& vm, Utf16View iso_string) { // 1. Let duration be ParseText(StringToCodePoints(isoString), TemporalDurationString). auto parse_result = parse_iso8601(Production::TemporalDurationString, iso_string); @@ -1461,7 +1466,7 @@ ThrowCompletionOr> parse_temporal_duration_string(VM& vm, Stri auto fractional_hours_digits = fractional_hours.substring_view(1); // c. Let fHoursScale be the length of fHoursDigits. - auto fractional_hours_scale = fractional_hours_digits.length(); + auto fractional_hours_scale = fractional_hours_digits.length_in_code_units(); // d. Let minutesMV be ? ToIntegerWithTruncation(fHoursDigits) / 10**fHoursScale × 60. auto minutes_integer = TRY(to_integer_with_truncation(vm, fractional_hours_digits, ErrorType::TemporalInvalidDurationString, iso_string)); @@ -1484,7 +1489,7 @@ ThrowCompletionOr> parse_temporal_duration_string(VM& vm, Stri auto fractional_minutes_digits = fractional_minutes.substring_view(1); // c. Let fMinutesScale be the length of fMinutesDigits. - auto fractional_minutes_scale = fractional_minutes_digits.length(); + auto fractional_minutes_scale = fractional_minutes_digits.length_in_code_units(); // d. Let secondsMV be ? ToIntegerWithTruncation(fMinutesDigits) / 10**fMinutesScale × 60. auto seconds_integer = TRY(to_integer_with_truncation(vm, fractional_minutes_digits, ErrorType::TemporalInvalidDurationString, iso_string)); @@ -1508,7 +1513,7 @@ ThrowCompletionOr> parse_temporal_duration_string(VM& vm, Stri auto fractional_seconds_digits = fractional_seconds.substring_view(1); // b. Let fSecondsScale be the length of fSecondsDigits. - auto fractional_seconds_scale = fractional_seconds_digits.length(); + auto fractional_seconds_scale = fractional_seconds_digits.length_in_code_units(); // c. Let millisecondsMV be ? ToIntegerWithTruncation(fSecondsDigits) / 10**fSecondsScale × 1000. auto milliseconds_integer = TRY(to_integer_with_truncation(vm, fractional_seconds_digits, ErrorType::TemporalInvalidDurationString, iso_string)); @@ -1568,7 +1573,7 @@ ThrowCompletionOr> parse_temporal_duration_string(VM& vm, Stri } // 13.38 ParseTemporalTimeZoneString ( timeZoneString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporaltimezonestring -ThrowCompletionOr parse_temporal_time_zone_string(VM& vm, StringView time_zone_string) +ThrowCompletionOr parse_temporal_time_zone_string(VM& vm, Utf16View time_zone_string) { // 1. Let parseResult be ParseText(StringToCodePoints(timeZoneString), TimeZoneIdentifier). auto parse_result = parse_iso8601(Production::TimeZoneIdentifier, time_zone_string); @@ -1620,10 +1625,11 @@ ThrowCompletionOr to_offset_string(VM& vm, Value argument) return vm.throw_completion(ErrorType::TemporalInvalidTimeZoneString, offset); // 3. Perform ? ParseDateTimeUTCOffset(offset). - TRY(parse_date_time_utc_offset(vm, offset.as_string().utf8_string())); + auto offset_string = offset.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16(); + TRY(parse_date_time_utc_offset(vm, offset_string.bytes_as_string_view())); // 4. Return offset. - return offset.as_string().utf8_string(); + return offset_string; } // 13.42 ISODateToFields ( calendar, isoDate, type ), https://tc39.es/proposal-temporal/#sec-temporal-isodatetofields diff --git a/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h b/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h index 01a6892783..2fcd42df37 100644 --- a/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h +++ b/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h @@ -191,10 +191,10 @@ Crypto::SignedBigInteger apply_unsigned_rounding_mode(Crypto::SignedDivisionResu double round_number_to_increment(double, u64 increment, RoundingMode); Crypto::SignedBigInteger round_number_to_increment(Crypto::SignedBigInteger const&, Crypto::UnsignedBigInteger const& increment, RoundingMode); Crypto::SignedBigInteger round_number_to_increment_as_if_positive(Crypto::SignedBigInteger const&, Crypto::UnsignedBigInteger const& increment, RoundingMode); -ThrowCompletionOr parse_iso_date_time(VM&, StringView iso_string, ReadonlySpan allowed_formats); -ThrowCompletionOr parse_temporal_calendar_string(VM&, String const&); -ThrowCompletionOr> parse_temporal_duration_string(VM&, StringView iso_string); -ThrowCompletionOr parse_temporal_time_zone_string(VM&, StringView time_zone_string); +ThrowCompletionOr parse_iso_date_time(VM&, Utf16View iso_string, ReadonlySpan allowed_formats); +ThrowCompletionOr parse_temporal_calendar_string(VM&, Utf16View); +ThrowCompletionOr> parse_temporal_duration_string(VM&, Utf16View iso_string); +ThrowCompletionOr parse_temporal_time_zone_string(VM&, Utf16View time_zone_string); ThrowCompletionOr to_offset_string(VM&, Value argument); CalendarFields iso_date_to_fields(String const& calendar, ISODate, DateType); ThrowCompletionOr get_difference_settings(VM&, DurationOperation, Object const& options, UnitGroup, ReadonlySpan disallowed_units, Unit fallback_smallest_unit, Unit smallest_largest_default_unit); @@ -215,10 +215,10 @@ ThrowCompletionOr to_integer_with_truncation(VM& vm, Value argument, Err } // 13.40 ToIntegerWithTruncation ( argument ), https://tc39.es/proposal-temporal/#sec-tointegerwithtruncation -// AD-HOC: We often need to use this AO when we have a parsed StringView. This overload allows callers to avoid creating +// AD-HOC: We often need to use this AO when we have a parsed Utf16View. This overload allows callers to avoid creating // a PrimitiveString for the primary definition. template -ThrowCompletionOr to_integer_with_truncation(VM& vm, StringView argument, ErrorType const& error_type, Args&&... args) +ThrowCompletionOr to_integer_with_truncation(VM& vm, Utf16View argument, ErrorType const& error_type, Args&&... args) { // 1. Let number be ? ToNumber(argument). auto number = string_to_number(argument); diff --git a/Libraries/LibJS/Runtime/Temporal/Calendar.cpp b/Libraries/LibJS/Runtime/Temporal/Calendar.cpp index d0d7e00cd3..cbd58b852c 100644 --- a/Libraries/LibJS/Runtime/Temporal/Calendar.cpp +++ b/Libraries/LibJS/Runtime/Temporal/Calendar.cpp @@ -284,6 +284,12 @@ ThrowCompletionOr canonicalize_calendar(VM& vm, StringView id) return vm.throw_completion(ErrorType::TemporalInvalidCalendarIdentifier, id); } +ThrowCompletionOr canonicalize_calendar(VM& vm, Utf16View id) +{ + auto utf8_id = id.to_utf8_but_should_be_ported_to_utf16(); + return canonicalize_calendar(vm, utf8_id.bytes_as_string_view()); +} + // 12.1.2 AvailableCalendars ( ), https://tc39.es/proposal-temporal/#sec-availablecalendars // 1.1.1 AvailableCalendars ( ), https://tc39.es/proposal-intl-era-monthcode/#sup-availablecalendars Vector const& available_calendars() @@ -318,7 +324,8 @@ ThrowCompletionOr parse_month_code(VM& vm, Value argument) if (!month_code.is_string()) return vm.throw_completion(ErrorType::NotAString, month_code); - return parse_month_code(vm, month_code.as_string().utf8_string()); + auto month_code_string = month_code.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16(); + return parse_month_code(vm, month_code_string.bytes_as_string_view()); } // 12.2.1 ParseMonthCode ( argument ), https://tc39.es/proposal-temporal/#sec-temporal-parsemonthcode @@ -388,7 +395,7 @@ ThrowCompletionOr prepare_calendar_fields(VM& vm, String const& // v. Else if Conversion is TO-STRING, then case CalendarFieldConversion::ToString: // 1. Set value to ? ToString(value). - set_field_value(key, result, TRY(value.to_string(vm))); + set_field_value(key, result, TRY(value.to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16()); break; // vi. Else if Conversion is TO-TEMPORAL-TIME-ZONE-IDENTIFIER, then case CalendarFieldConversion::ToTemporalTimeZoneIdentifier: @@ -797,7 +804,7 @@ ThrowCompletionOr to_temporal_calendar_identifier(VM& vm, Value temporal return vm.throw_completion(ErrorType::TemporalInvalidCalendar); // 3. Let identifier be ? ParseTemporalCalendarString(temporalCalendarLike). - auto identifier = TRY(parse_temporal_calendar_string(vm, temporal_calendar_like.as_string().utf8_string())); + auto identifier = TRY(parse_temporal_calendar_string(vm, temporal_calendar_like.as_string().utf16_string_view())); // 4. Return ? CanonicalizeCalendar(identifier). return TRY(canonicalize_calendar(vm, identifier)); diff --git a/Libraries/LibJS/Runtime/Temporal/Calendar.h b/Libraries/LibJS/Runtime/Temporal/Calendar.h index 2157be4f46..5836e8ba4e 100644 --- a/Libraries/LibJS/Runtime/Temporal/Calendar.h +++ b/Libraries/LibJS/Runtime/Temporal/Calendar.h @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -97,6 +98,7 @@ struct BalancedDate { }; ThrowCompletionOr canonicalize_calendar(VM&, StringView id); +ThrowCompletionOr canonicalize_calendar(VM&, Utf16View id); Vector const& available_calendars(); ThrowCompletionOr parse_month_code(VM&, Value argument); diff --git a/Libraries/LibJS/Runtime/Temporal/Duration.cpp b/Libraries/LibJS/Runtime/Temporal/Duration.cpp index 922c4f9df3..5265ddd18c 100644 --- a/Libraries/LibJS/Runtime/Temporal/Duration.cpp +++ b/Libraries/LibJS/Runtime/Temporal/Duration.cpp @@ -361,7 +361,7 @@ ThrowCompletionOr> to_temporal_duration(VM& vm, Value item) return vm.throw_completion(ErrorType::NotAString, item); // b. Return ? ParseTemporalDurationString(item). - return TRY(parse_temporal_duration_string(vm, item.as_string().utf8_string())); + return TRY(parse_temporal_duration_string(vm, item.as_string().utf16_string_view())); } // 3. Let result be a new Partial Duration Record with each field set to 0. diff --git a/Libraries/LibJS/Runtime/Temporal/ISO8601.cpp b/Libraries/LibJS/Runtime/Temporal/ISO8601.cpp index a9a192e81b..86f61f6bfe 100644 --- a/Libraries/LibJS/Runtime/Temporal/ISO8601.cpp +++ b/Libraries/LibJS/Runtime/Temporal/ISO8601.cpp @@ -74,16 +74,16 @@ static bool is_valid_date(ParseResult const& result) // 13.31 RFC 9557 / ISO 8601 grammar, https://tc39.es/proposal-temporal/#sec-temporal-iso8601grammar class ISO8601Parser { public: - explicit ISO8601Parser(StringView input) + explicit ISO8601Parser(Utf16View input) : m_input(input) , m_state({ - .lexer = GenericLexer { input }, + .lexer = Utf16GenericLexer { input }, .parse_result = {}, }) { } - [[nodiscard]] GenericLexer const& lexer() const { return m_state.lexer; } + [[nodiscard]] Utf16GenericLexer const& lexer() const { return m_state.lexer; } [[nodiscard]] ParseResult const& parse_result() const { return m_state.parse_result; } // https://tc39.es/proposal-temporal/#prod-TemporalDateTimeString @@ -612,8 +612,8 @@ public: { StateTransaction transaction { *this }; - Optional key; - Optional value; + Optional key; + Optional value; // Annotation ::: // [ AnnotationCriticalFlag[opt] AnnotationKey = AnnotationValue ] @@ -1070,7 +1070,7 @@ public: } // https://tc39.es/ecma262/#prod-DecimalDigits - [[nodiscard]] bool parse_decimal_digits(Separator separator, Optional& result) + [[nodiscard]] bool parse_decimal_digits(Separator separator, Optional& result) { StateTransaction transaction { *this }; @@ -1258,7 +1258,7 @@ private: return false; if constexpr (IsSame) - storage = transaction.parsed_string_view()[0]; + storage = static_cast(transaction.parsed_string_view().code_unit_at(0)); else storage = transaction.parsed_string_view(); @@ -1267,7 +1267,7 @@ private: } struct State { - GenericLexer lexer; + Utf16GenericLexer lexer; ParseResult parse_result; }; @@ -1286,7 +1286,7 @@ private: } void commit() { m_commit = true; } - StringView parsed_string_view() const + Utf16View parsed_string_view() const { return m_parser.m_input.substring_view(m_start_index, m_parser.m_state.lexer.tell() - m_start_index); } @@ -1298,7 +1298,7 @@ private: bool m_commit { false }; }; - StringView m_input; + Utf16View m_input; State m_state; }; @@ -1314,7 +1314,7 @@ private: __JS_ENUMERATE(TemporalZonedDateTimeString, parse_temporal_zoned_date_time_string) \ __JS_ENUMERATE(TimeZoneIdentifier, parse_time_zone_identifier) -Optional parse_iso8601(Production production, StringView input) +Optional parse_iso8601(Production production, Utf16View input) { ISO8601Parser parser { input }; @@ -1337,7 +1337,7 @@ Optional parse_iso8601(Production production, StringView input) return parser.parse_result(); } -Optional parse_utc_offset(StringView input, SubMinutePrecision sub_minute_precision) +Optional parse_utc_offset(Utf16View input, SubMinutePrecision sub_minute_precision) { ISO8601Parser parser { input }; diff --git a/Libraries/LibJS/Runtime/Temporal/ISO8601.h b/Libraries/LibJS/Runtime/Temporal/ISO8601.h index fe8887d856..495675b1aa 100644 --- a/Libraries/LibJS/Runtime/Temporal/ISO8601.h +++ b/Libraries/LibJS/Runtime/Temporal/ISO8601.h @@ -8,53 +8,53 @@ #pragma once #include -#include +#include #include namespace JS::Temporal { struct Annotation { bool critical { false }; - StringView key; - StringView value; + Utf16View key; + Utf16View value; }; struct TimeZoneOffset { Optional sign; - Optional hours; - Optional minutes; - Optional seconds; - Optional fraction; - StringView source_text; + Optional hours; + Optional minutes; + Optional seconds; + Optional fraction; + Utf16View source_text; }; struct ParseResult { Optional sign; - Optional date_year; - Optional date_month; - Optional date_day; - Optional time_hour; - Optional time_minute; - Optional time_second; - Optional time_fraction; + Optional date_year; + Optional date_month; + Optional date_day; + Optional time_hour; + Optional time_minute; + Optional time_second; + Optional time_fraction; Optional date_time_offset; - Optional utc_designator; - Optional time_zone_identifier; - Optional time_zone_iana_name; + Optional utc_designator; + Optional time_zone_identifier; + Optional time_zone_iana_name; Optional time_zone_offset; - Optional duration_years; - Optional duration_months; - Optional duration_weeks; - Optional duration_days; - Optional duration_hours; - Optional duration_hours_fraction; - Optional duration_minutes; - Optional duration_minutes_fraction; - Optional duration_seconds; - Optional duration_seconds_fraction; + Optional duration_years; + Optional duration_months; + Optional duration_weeks; + Optional duration_days; + Optional duration_hours; + Optional duration_hours_fraction; + Optional duration_minutes; + Optional duration_minutes_fraction; + Optional duration_seconds; + Optional duration_seconds_fraction; Vector annotations; }; @@ -72,13 +72,13 @@ enum class Production { TimeZoneIdentifier, }; -Optional parse_iso8601(Production, StringView); +Optional parse_iso8601(Production, Utf16View); enum class SubMinutePrecision { No, Yes, }; -Optional parse_utc_offset(StringView, SubMinutePrecision); +Optional parse_utc_offset(Utf16View, SubMinutePrecision); } diff --git a/Libraries/LibJS/Runtime/Temporal/Instant.cpp b/Libraries/LibJS/Runtime/Temporal/Instant.cpp index ad753e667a..d1ec919f3e 100644 --- a/Libraries/LibJS/Runtime/Temporal/Instant.cpp +++ b/Libraries/LibJS/Runtime/Temporal/Instant.cpp @@ -111,7 +111,7 @@ ThrowCompletionOr> to_temporal_instant(VM& vm, Value item) return vm.throw_completion(ErrorType::TemporalInvalidInstantString, item); // 3. Let parsed be ? ParseISODateTime(item, « TemporalInstantString »). - auto parsed = TRY(parse_iso_date_time(vm, item.as_string().utf8_string(), { { Production::TemporalInstantString } })); + auto parsed = TRY(parse_iso_date_time(vm, item.as_string().utf16_string_view(), { { Production::TemporalInstantString } })); // 4. Assert: Either parsed.[[TimeZone]].[[OffsetString]] is not empty or parsed.[[TimeZone]].[[Z]] is true, but not both. auto const& offset_string = parsed.time_zone.offset_string; diff --git a/Libraries/LibJS/Runtime/Temporal/PlainDate.cpp b/Libraries/LibJS/Runtime/Temporal/PlainDate.cpp index d9546b80e2..5142c12921 100644 --- a/Libraries/LibJS/Runtime/Temporal/PlainDate.cpp +++ b/Libraries/LibJS/Runtime/Temporal/PlainDate.cpp @@ -135,7 +135,7 @@ ThrowCompletionOr> to_temporal_date(VM& vm, Value item, Value return vm.throw_completion(ErrorType::TemporalInvalidPlainDate); // 4. Let result be ? ParseISODateTime(item, « TemporalDateTimeString[~Zoned] »). - auto result = TRY(parse_iso_date_time(vm, item.as_string().utf8_string(), { { Production::TemporalDateTimeString } })); + auto result = TRY(parse_iso_date_time(vm, item.as_string().utf16_string_view(), { { Production::TemporalDateTimeString } })); // 5. Let calendar be result.[[Calendar]]. // 6. If calendar is empty, set calendar to "iso8601". diff --git a/Libraries/LibJS/Runtime/Temporal/PlainDateConstructor.cpp b/Libraries/LibJS/Runtime/Temporal/PlainDateConstructor.cpp index 5be5d6cabf..30998e8d5f 100644 --- a/Libraries/LibJS/Runtime/Temporal/PlainDateConstructor.cpp +++ b/Libraries/LibJS/Runtime/Temporal/PlainDateConstructor.cpp @@ -72,7 +72,7 @@ ThrowCompletionOr> PlainDateConstructor::construct(FunctionObjec return vm.throw_completion(ErrorType::NotAString, "calendar"sv); // 7. Set calendar to ? CanonicalizeCalendar(calendar). - auto calendar = TRY(canonicalize_calendar(vm, calendar_value.as_string().utf8_string())); + auto calendar = TRY(canonicalize_calendar(vm, calendar_value.as_string().utf16_string_view())); // 8. If IsValidISODate(y, m, d) is false, throw a RangeError exception. if (!is_valid_iso_date(year, month, day)) diff --git a/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp b/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp index 5ac15f48a6..1cbc378f83 100644 --- a/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp +++ b/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp @@ -175,7 +175,7 @@ ThrowCompletionOr> to_temporal_date_time(VM& vm, Value it return vm.throw_completion(ErrorType::TemporalInvalidPlainDateTime); // 4. Let result be ? ParseISODateTime(item, « TemporalDateTimeString[~Zoned] »). - auto result = TRY(parse_iso_date_time(vm, item.as_string().utf8_string(), { { Production::TemporalDateTimeString } })); + auto result = TRY(parse_iso_date_time(vm, item.as_string().utf16_string_view(), { { Production::TemporalDateTimeString } })); // 5. If result.[[Time]] is START-OF-DAY, let time be MidnightTimeRecord(); else let time be result.[[Time]]. auto time = result.time.has() ? midnight_time_record() : result.time.get