LibJS: Remove primitive string UTF-8 paths

Move the remaining LibJS primitive string users to UTF-16 views and
strings. Remove the primitive string UTF-8 accessors and byte-string
coercion paths so new callers cannot rely on the old storage model.
This commit is contained in:
Andreas Kling 2026-06-21 19:03:01 +02:00 committed by Andreas Kling
parent 13969b6bd4
commit ee37bb5a9c
105 changed files with 492 additions and 431 deletions

View file

@ -401,7 +401,7 @@ inline ThrowCompletionOr<void> put_by_property_key(VM& vm, Value base, Value thi
if (!succeeded && strict == Strict::Yes) [[unlikely]] {
if (base.is_object())
return vm.throw_completion<TypeError>(ErrorType::ReferenceNullishSetProperty, name, base);
return vm.throw_completion<TypeError>(ErrorType::ReferencePrimitiveSetProperty, name, base.typeof_(vm)->utf8_string(), base);
return vm.throw_completion<TypeError>(ErrorType::ReferencePrimitiveSetProperty, name, base.typeof_(vm)->utf16_string_view().to_utf8_but_should_be_ported_to_utf16(), base);
}
break;
}

View file

@ -449,7 +449,7 @@ ThrowCompletionOr<Value> Console::dirxml()
static ThrowCompletionOr<String> 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<String> Console::value_vector_to_string(GC::RootVector<Value>
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<GC::RootVector<Value>> 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<GC::RootVector<Value>> 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.

View file

@ -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()) {

View file

@ -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();
}

View file

@ -646,7 +646,7 @@ ThrowCompletionOr<Value> 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<Utf16String> 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<size_t>(string_to_number(utf8_digits));
auto index = static_cast<size_t>(string_to_number(digits));
// iv. Assert: 0 ≤ index ≤ 99.
VERIFY(index <= 99);
@ -1380,8 +1379,7 @@ ThrowCompletionOr<Utf16String> 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<size_t>(string_to_number(utf8_digits));
index = static_cast<size_t>(string_to_number(digits));
}
// vii. Let ref be the substring of templateRemainder from 0 to 1 + digitCount.
@ -1958,8 +1956,12 @@ ThrowCompletionOr<Value> 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<RangeError>(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<RangeError>(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<RoundingMode> 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<RoundingMode>(allowed_strings.first_index_of(string_value.as_string().utf8_string()).value());
auto string = string_value.as_string().utf16_string_view();
Optional<size_t> 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<RoundingMode>(*index);
}
// 14.5.2.4 GetRoundingIncrementOption ( options ), https://tc39.es/proposal-temporal/#sec-temporal-getroundingincrementoption

View file

@ -55,7 +55,7 @@ ThrowCompletionOr<GC::Ref<Object>> 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));

View file

@ -257,10 +257,10 @@ ThrowCompletionOr<double> 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));

View file

@ -8,6 +8,7 @@
#include <AK/NeverDestroyed.h>
#include <AK/NumericLimits.h>
#include <AK/Time.h>
#include <AK/Utf16String.h>
#include <AK/Utf16StringBuilder.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Date.h>
@ -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<double> 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<char>(parsed_fraction.code_unit_at(i)));
}
}
// 17. Return sign × (((hours × 60 + minutes) × 60 + seconds) × 10^9 + nanoseconds).

View file

@ -9,6 +9,7 @@
*/
#include <AK/Time.h>
#include <AK/Utf16View.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Date.h>
#include <LibJS/Runtime/DateConstructor.h>
@ -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<GC::Ref<Object>> 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.

View file

@ -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);

View file

@ -115,11 +115,11 @@ ThrowCompletionOr<GC::Ref<ECMAScriptFunctionObject>> 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();

View file

@ -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<double>(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<u32> { *trimmed_string.code_points().begin() };
auto first_code_point = trimmed_string.is_empty() ? OptionalNone {} : Optional<u32> { *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;

View file

@ -307,7 +307,7 @@ ThrowCompletionOr<Vector<String>> 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<String> requested_locales, LocaleOpti
Optional<MatchedLocale> 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<ResolvedOptions> 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<GC::Ref<Array>> filter_locales(VM& vm, ReadonlySpan<String> re
Optional<MatchedLocale> 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<StringOrBoolean> 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<RangeError>(ErrorType::OptionIsNotValidValue, value_string, property.as_string());
return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, value_string_view.to_utf8_but_should_be_ported_to_utf16(), property.as_string());
// 7. Return value.
return StringOrBoolean { *it };

View file

@ -69,7 +69,7 @@ ThrowCompletionOr<GC::Ref<Object>> 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<GC::Ref<Object>> CollatorConstructor::construct(FunctionObject
Optional<Unicode::Sensitivity> 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

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Utf16String.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/Date.h>
@ -191,7 +192,7 @@ ThrowCompletionOr<GC::Ref<DateTimeFormat>> create_date_time_format(VM& vm, Funct
return vm.throw_completion<TypeError>(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<GC::Ref<DateTimeFormat>> 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<GC::Ref<DateTimeFormat>> create_date_time_format(VM& vm, Funct
// d. Set formatOptions.[[<prop>]] 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<GC::Ref<DateTimeFormat>> 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]].[[<resolvedCalendar>]].

View file

@ -64,7 +64,7 @@ ThrowCompletionOr<GC::Ref<Object>> 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<GC::Ref<Object>> DisplayNamesConstructor::construct(FunctionOb
return vm.throw_completion<TypeError>(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<GC::Ref<Object>> 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.[[<languageDisplay>]].
// c. Assert: typeFields is a Record (see 12.2.3).

View file

@ -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 [[<code>]], return fields.[[<code>]].

View file

@ -269,7 +269,7 @@ ThrowCompletionOr<DurationFormat::DurationUnitOptions> 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<DurationFormat::DurationUnitOptions> 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));

View file

@ -83,7 +83,7 @@ ThrowCompletionOr<GC::Ref<Object>> 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<DurationFormat::ValueStyle> previous_style;

View file

@ -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<Variant<ReadonlySpan<StringView>, ReadonlySpan<String>>> list;

View file

@ -66,13 +66,13 @@ ThrowCompletionOr<GC::Ref<Object>> 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.[[<type>]].

View file

@ -37,10 +37,10 @@ static ThrowCompletionOr<Optional<String>> 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<RangeError>(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<GC::Ref<Object>> 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<GC::Ref<Object>> 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

View file

@ -222,7 +222,7 @@ ThrowCompletionOr<MathematicalValue> 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.

View file

@ -78,7 +78,7 @@ ThrowCompletionOr<GC::Ref<Object>> 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<GC::Ref<Object>> 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<GC::Ref<Object>> 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<void> 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<void> 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<void> 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<void> 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<RangeError>(ErrorType::OptionIsNotValidValue, currency, "currency"sv);
}
@ -413,7 +413,7 @@ ThrowCompletionOr<void> 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<RangeError>(ErrorType::OptionIsNotValidValue, unit, "unit"sv);
}
@ -423,22 +423,22 @@ ThrowCompletionOr<void> 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.

View file

@ -67,13 +67,13 @@ ThrowCompletionOr<GC::Ref<Object>> 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<GC::Ref<Object>> 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).

View file

@ -75,13 +75,13 @@ ThrowCompletionOr<GC::Ref<Object>> 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]]).

View file

@ -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));
}
}

View file

@ -67,7 +67,7 @@ ThrowCompletionOr<GC::Ref<Object>> 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));

View file

@ -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

View file

@ -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())

View file

@ -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;
}

View file

@ -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();

View file

@ -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(); }

View file

@ -257,10 +257,10 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpConstructor::escape)
return vm.throw_completion<TypeError>(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) {

View file

@ -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<Value> 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<Value> 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<Value> 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<Value> 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<Value> 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, &regexp_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.

View file

@ -40,7 +40,7 @@ GC_DEFINE_ALLOCATOR(StringPrototype);
static ThrowCompletionOr<String> 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<GC::Ref<PrimitiveString>> 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<TypeError>(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<TypeError>(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<String> trim_string(VM& vm, Value input_value, TrimMode where)
ThrowCompletionOr<Utf16String> 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<Value> 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 "&quot;".
auto escaped_value_string = value_string.replace("\""sv, "&quot;"sv, ReplaceMode::All);
auto escaped_value_string = value_string.replace(u'"', "&quot;"sv, ReplaceMode::All);
// c. Set p1 to the string-concatenation of:
// - p1

View file

@ -6,7 +6,7 @@
#pragma once
#include <AK/Utf8View.h>
#include <AK/Utf16View.h>
#include <LibJS/Runtime/StringObject.h>
namespace JS {
@ -20,8 +20,35 @@ struct CodePoint {
Optional<size_t> string_index_of(Utf16View const& string, Utf16View const& search_value, size_t from_index);
Optional<size_t> 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<String> 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<Utf16String> trim_string(VM&, Value string, TrimMode where);
class StringPrototype final : public StringObject {
JS_OBJECT(StringPrototype, StringObject);

View file

@ -54,7 +54,7 @@ ThrowCompletionOr<GC::Ref<Object>> 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)));

View file

@ -90,7 +90,7 @@ ThrowCompletionOr<Overflow> 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<Disambiguation> 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<OffsetOption> 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<ShowCalendar> 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<ShowTimeZoneName> 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<ShowOffset> 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<Direction> 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<Precision> 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<RangeError>(ErrorType::OptionIsNotValidValue, digits_value, vm.names.fractionalSecondDigits);
@ -415,7 +415,7 @@ ThrowCompletionOr<UnitValue> 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<RelativeTo> get_temporal_relative_to_option(VM& vm, Object con
return vm.throw_completion<TypeError>(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<RelativeTo> 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<RelativeTo> 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<RelativeTo> 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<ParsedISODateTime> parse_iso_date_time(VM& vm, StringView iso_string, ReadonlySpan<Production> allowed_formats)
ThrowCompletionOr<ParsedISODateTime> parse_iso_date_time(VM& vm, Utf16View iso_string, ReadonlySpan<Production> allowed_formats)
{
// 1. Let parseResult be EMPTY.
Optional<ParseResult> parse_result;
@ -1133,7 +1135,7 @@ ThrowCompletionOr<ParsedISODateTime> 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<ParsedISODateTime> 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<char>(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<ParsedISODateTime> 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<ParsedISODateTime> 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<ParsedISODateTime> parse_iso_date_time(VM& vm, StringView iso_
}
// 13.36 ParseTemporalCalendarString ( string ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporalcalendarstring
ThrowCompletionOr<String> parse_temporal_calendar_string(VM& vm, String const& string)
ThrowCompletionOr<String> 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<String> 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<RangeError>(ErrorType::TemporalInvalidCalendarString, string);
return vm.throw_completion<RangeError>(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<GC::Ref<Duration>> parse_temporal_duration_string(VM& vm, StringView iso_string)
ThrowCompletionOr<GC::Ref<Duration>> 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<GC::Ref<Duration>> 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<GC::Ref<Duration>> 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<GC::Ref<Duration>> 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<GC::Ref<Duration>> parse_temporal_duration_string(VM& vm, Stri
}
// 13.38 ParseTemporalTimeZoneString ( timeZoneString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporaltimezonestring
ThrowCompletionOr<ParsedTimeZoneIdentifier> parse_temporal_time_zone_string(VM& vm, StringView time_zone_string)
ThrowCompletionOr<ParsedTimeZoneIdentifier> 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<String> to_offset_string(VM& vm, Value argument)
return vm.throw_completion<TypeError>(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

View file

@ -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<ParsedISODateTime> parse_iso_date_time(VM&, StringView iso_string, ReadonlySpan<Production> allowed_formats);
ThrowCompletionOr<String> parse_temporal_calendar_string(VM&, String const&);
ThrowCompletionOr<GC::Ref<Duration>> parse_temporal_duration_string(VM&, StringView iso_string);
ThrowCompletionOr<ParsedTimeZoneIdentifier> parse_temporal_time_zone_string(VM&, StringView time_zone_string);
ThrowCompletionOr<ParsedISODateTime> parse_iso_date_time(VM&, Utf16View iso_string, ReadonlySpan<Production> allowed_formats);
ThrowCompletionOr<String> parse_temporal_calendar_string(VM&, Utf16View);
ThrowCompletionOr<GC::Ref<Duration>> parse_temporal_duration_string(VM&, Utf16View iso_string);
ThrowCompletionOr<ParsedTimeZoneIdentifier> parse_temporal_time_zone_string(VM&, Utf16View time_zone_string);
ThrowCompletionOr<String> to_offset_string(VM&, Value argument);
CalendarFields iso_date_to_fields(String const& calendar, ISODate, DateType);
ThrowCompletionOr<DifferenceSettings> get_difference_settings(VM&, DurationOperation, Object const& options, UnitGroup, ReadonlySpan<Unit> disallowed_units, Unit fallback_smallest_unit, Unit smallest_largest_default_unit);
@ -215,10 +215,10 @@ ThrowCompletionOr<double> 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<typename... Args>
ThrowCompletionOr<double> to_integer_with_truncation(VM& vm, StringView argument, ErrorType const& error_type, Args&&... args)
ThrowCompletionOr<double> 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);

View file

@ -284,6 +284,12 @@ ThrowCompletionOr<String> canonicalize_calendar(VM& vm, StringView id)
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidCalendarIdentifier, id);
}
ThrowCompletionOr<String> 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<String> const& available_calendars()
@ -318,7 +324,8 @@ ThrowCompletionOr<MonthCode> parse_month_code(VM& vm, Value argument)
if (!month_code.is_string())
return vm.throw_completion<TypeError>(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<CalendarFields> 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<String> to_temporal_calendar_identifier(VM& vm, Value temporal
return vm.throw_completion<TypeError>(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));

View file

@ -11,6 +11,7 @@
#include <AK/Optional.h>
#include <AK/String.h>
#include <AK/Utf16View.h>
#include <AK/Vector.h>
#include <LibJS/Forward.h>
#include <LibJS/Runtime/Completion.h>
@ -97,6 +98,7 @@ struct BalancedDate {
};
ThrowCompletionOr<String> canonicalize_calendar(VM&, StringView id);
ThrowCompletionOr<String> canonicalize_calendar(VM&, Utf16View id);
Vector<String> const& available_calendars();
ThrowCompletionOr<MonthCode> parse_month_code(VM&, Value argument);

View file

@ -361,7 +361,7 @@ ThrowCompletionOr<GC::Ref<Duration>> to_temporal_duration(VM& vm, Value item)
return vm.throw_completion<TypeError>(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.

View file

@ -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<StringView> key;
Optional<StringView> value;
Optional<Utf16View> key;
Optional<Utf16View> 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<StringView>& result)
[[nodiscard]] bool parse_decimal_digits(Separator separator, Optional<Utf16View>& result)
{
StateTransaction transaction { *this };
@ -1258,7 +1258,7 @@ private:
return false;
if constexpr (IsSame<T, char>)
storage = transaction.parsed_string_view()[0];
storage = static_cast<char>(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<ParseResult> parse_iso8601(Production production, StringView input)
Optional<ParseResult> parse_iso8601(Production production, Utf16View input)
{
ISO8601Parser parser { input };
@ -1337,7 +1337,7 @@ Optional<ParseResult> parse_iso8601(Production production, StringView input)
return parser.parse_result();
}
Optional<TimeZoneOffset> parse_utc_offset(StringView input, SubMinutePrecision sub_minute_precision)
Optional<TimeZoneOffset> parse_utc_offset(Utf16View input, SubMinutePrecision sub_minute_precision)
{
ISO8601Parser parser { input };

View file

@ -8,53 +8,53 @@
#pragma once
#include <AK/Optional.h>
#include <AK/StringView.h>
#include <AK/Utf16View.h>
#include <AK/Vector.h>
namespace JS::Temporal {
struct Annotation {
bool critical { false };
StringView key;
StringView value;
Utf16View key;
Utf16View value;
};
struct TimeZoneOffset {
Optional<char> sign;
Optional<StringView> hours;
Optional<StringView> minutes;
Optional<StringView> seconds;
Optional<StringView> fraction;
StringView source_text;
Optional<Utf16View> hours;
Optional<Utf16View> minutes;
Optional<Utf16View> seconds;
Optional<Utf16View> fraction;
Utf16View source_text;
};
struct ParseResult {
Optional<char> sign;
Optional<StringView> date_year;
Optional<StringView> date_month;
Optional<StringView> date_day;
Optional<StringView> time_hour;
Optional<StringView> time_minute;
Optional<StringView> time_second;
Optional<StringView> time_fraction;
Optional<Utf16View> date_year;
Optional<Utf16View> date_month;
Optional<Utf16View> date_day;
Optional<Utf16View> time_hour;
Optional<Utf16View> time_minute;
Optional<Utf16View> time_second;
Optional<Utf16View> time_fraction;
Optional<TimeZoneOffset> date_time_offset;
Optional<StringView> utc_designator;
Optional<StringView> time_zone_identifier;
Optional<StringView> time_zone_iana_name;
Optional<Utf16View> utc_designator;
Optional<Utf16View> time_zone_identifier;
Optional<Utf16View> time_zone_iana_name;
Optional<TimeZoneOffset> time_zone_offset;
Optional<StringView> duration_years;
Optional<StringView> duration_months;
Optional<StringView> duration_weeks;
Optional<StringView> duration_days;
Optional<StringView> duration_hours;
Optional<StringView> duration_hours_fraction;
Optional<StringView> duration_minutes;
Optional<StringView> duration_minutes_fraction;
Optional<StringView> duration_seconds;
Optional<StringView> duration_seconds_fraction;
Optional<Utf16View> duration_years;
Optional<Utf16View> duration_months;
Optional<Utf16View> duration_weeks;
Optional<Utf16View> duration_days;
Optional<Utf16View> duration_hours;
Optional<Utf16View> duration_hours_fraction;
Optional<Utf16View> duration_minutes;
Optional<Utf16View> duration_minutes_fraction;
Optional<Utf16View> duration_seconds;
Optional<Utf16View> duration_seconds_fraction;
Vector<Annotation> annotations;
};
@ -72,13 +72,13 @@ enum class Production {
TimeZoneIdentifier,
};
Optional<ParseResult> parse_iso8601(Production, StringView);
Optional<ParseResult> parse_iso8601(Production, Utf16View);
enum class SubMinutePrecision {
No,
Yes,
};
Optional<TimeZoneOffset> parse_utc_offset(StringView, SubMinutePrecision);
Optional<TimeZoneOffset> parse_utc_offset(Utf16View, SubMinutePrecision);
}

View file

@ -111,7 +111,7 @@ ThrowCompletionOr<GC::Ref<Instant>> to_temporal_instant(VM& vm, Value item)
return vm.throw_completion<TypeError>(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;

View file

@ -135,7 +135,7 @@ ThrowCompletionOr<GC::Ref<PlainDate>> to_temporal_date(VM& vm, Value item, Value
return vm.throw_completion<TypeError>(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".

View file

@ -72,7 +72,7 @@ ThrowCompletionOr<GC::Ref<Object>> PlainDateConstructor::construct(FunctionObjec
return vm.throw_completion<TypeError>(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))

View file

@ -175,7 +175,7 @@ ThrowCompletionOr<GC::Ref<PlainDateTime>> to_temporal_date_time(VM& vm, Value it
return vm.throw_completion<TypeError>(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<ParsedISODateTime::StartOfDay>() ? midnight_time_record() : result.time.get<Time>();

View file

@ -101,7 +101,7 @@ ThrowCompletionOr<GC::Ref<Object>> PlainDateTimeConstructor::construct(FunctionO
return vm.throw_completion<TypeError>(ErrorType::NotAString, "calendar"sv);
// 13. 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()));
// 14. If IsValidISODate(isoYear, isoMonth, isoDay) is false, throw a RangeError exception.
if (!is_valid_iso_date(iso_year, iso_month, iso_day))

View file

@ -69,7 +69,7 @@ ThrowCompletionOr<GC::Ref<PlainMonthDay>> to_temporal_month_day(VM& vm, Value it
return vm.throw_completion<TypeError>(ErrorType::TemporalInvalidPlainMonthDay);
// 4. Let result be ? ParseISODateTime(item, « TemporalMonthDayString »).
auto parse_result = TRY(parse_iso_date_time(vm, item.as_string().utf8_string(), { { Production::TemporalMonthDayString } }));
auto parse_result = TRY(parse_iso_date_time(vm, item.as_string().utf16_string_view(), { { Production::TemporalMonthDayString } }));
// 5. Let calendar be result.[[Calendar]].
// 6. If calendar is empty, set calendar to "iso8601".

View file

@ -76,7 +76,7 @@ ThrowCompletionOr<GC::Ref<Object>> PlainMonthDayConstructor::construct(FunctionO
return vm.throw_completion<TypeError>(ErrorType::NotAString, calendar_value);
// 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. Let y be ? ToIntegerWithTruncation(referenceISOYear).
auto year = TRY(to_integer_with_truncation(vm, reference_iso_year, ErrorType::TemporalInvalidPlainMonthDay));

View file

@ -159,7 +159,7 @@ ThrowCompletionOr<GC::Ref<PlainTime>> to_temporal_time(VM& vm, Value item, Value
return vm.throw_completion<TypeError>(ErrorType::TemporalInvalidPlainTime);
// b. Let parseResult be ? ParseISODateTime(item, « TemporalTimeString »).
auto parse_result = TRY(parse_iso_date_time(vm, item.as_string().utf8_string(), { { Production::TemporalTimeString } }));
auto parse_result = TRY(parse_iso_date_time(vm, item.as_string().utf16_string_view(), { { Production::TemporalTimeString } }));
// c. Assert: parseResult.[[Time]] is not START-OF-DAY.
VERIFY(!parse_result.time.has<ParsedISODateTime::StartOfDay>());

View file

@ -72,7 +72,7 @@ ThrowCompletionOr<GC::Ref<PlainYearMonth>> to_temporal_year_month(VM& vm, Value
return vm.throw_completion<TypeError>(ErrorType::TemporalInvalidPlainYearMonth);
// 4. Let result be ? ParseISODateTime(item, « TemporalYearMonthString »).
auto parse_result = TRY(parse_iso_date_time(vm, item.as_string().utf8_string(), { { Production::TemporalYearMonthString } }));
auto parse_result = TRY(parse_iso_date_time(vm, item.as_string().utf16_string_view(), { { Production::TemporalYearMonthString } }));
// 5. Let calendar be result.[[Calendar]].
// 6. If calendar is empty, set calendar to "iso8601".

View file

@ -76,7 +76,7 @@ ThrowCompletionOr<GC::Ref<Object>> PlainYearMonthConstructor::construct(Function
return vm.throw_completion<TypeError>(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. Let ref be ? ToIntegerWithTruncation(referenceISODay).
auto reference = TRY(to_integer_with_truncation(vm, reference_iso_day, ErrorType::TemporalInvalidPlainYearMonth));

View file

@ -7,6 +7,7 @@
*/
#include <AK/NeverDestroyed.h>
#include <AK/Utf16String.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Date.h>
#include <LibJS/Runtime/Intl/AbstractOperations.h>
@ -216,11 +217,11 @@ ThrowCompletionOr<String> to_temporal_time_zone_identifier(VM& vm, Value tempora
if (!temporal_time_zone_like.is_string())
return vm.throw_completion<TypeError>(ErrorType::TemporalInvalidTimeZoneName, temporal_time_zone_like);
return to_temporal_time_zone_identifier(vm, temporal_time_zone_like.as_string().utf8_string());
return to_temporal_time_zone_identifier(vm, temporal_time_zone_like.as_string().utf16_string_view());
}
// 11.1.8 ToTemporalTimeZoneIdentifier ( temporalTimeZoneLike ), https://tc39.es/proposal-temporal/#sec-temporal-totemporaltimezoneidentifier
ThrowCompletionOr<String> to_temporal_time_zone_identifier(VM& vm, StringView temporal_time_zone_like)
ThrowCompletionOr<String> to_temporal_time_zone_identifier(VM& vm, Utf16View temporal_time_zone_like)
{
// 3. Let parseResult be ? ParseTemporalTimeZoneString(temporalTimeZoneLike).
auto parse_result = TRY(parse_temporal_time_zone_string(vm, temporal_time_zone_like));
@ -480,8 +481,10 @@ bool time_zone_equals(StringView one, StringView two)
// NB: IsOffsetTimeZoneIdentifier simply invokes parse_utc_offset and returns whether it has a value. We do this
// manually here so that we can handle the offset minutes assertion below without any extra performance penalty.
auto time_zone_offset_one = parse_utc_offset(one, SubMinutePrecision::No);
auto time_zone_offset_two = parse_utc_offset(two, SubMinutePrecision::No);
auto utf16_one = Utf16String::from_utf8(one);
auto utf16_two = Utf16String::from_utf8(two);
auto time_zone_offset_one = parse_utc_offset(utf16_one, SubMinutePrecision::No);
auto time_zone_offset_two = parse_utc_offset(utf16_two, SubMinutePrecision::No);
// 2. If IsOffsetTimeZoneIdentifier(one) is false and IsOffsetTimeZoneIdentifier(two) is false, then
if (!time_zone_offset_one.has_value() && !time_zone_offset_two.has_value()) {
@ -524,7 +527,8 @@ ThrowCompletionOr<ParsedTimeZoneIdentifier> parse_time_zone_identifier(VM& vm, S
return *result;
// 1. Let parseResult be ParseText(StringToCodePoints(identifier), TimeZoneIdentifier).
auto parse_result = parse_iso8601(Production::TimeZoneIdentifier, identifier);
auto utf16_identifier = Utf16String::from_utf8(identifier);
auto parse_result = parse_iso8601(Production::TimeZoneIdentifier, utf16_identifier);
// 2. If parseResult is a List of errors, throw a RangeError exception.
if (!parse_result.has_value())
@ -536,13 +540,19 @@ ThrowCompletionOr<ParsedTimeZoneIdentifier> parse_time_zone_identifier(VM& vm, S
return result;
}
ThrowCompletionOr<ParsedTimeZoneIdentifier> parse_time_zone_identifier(VM& vm, Utf16View identifier)
{
return parse_time_zone_identifier(vm, identifier.to_utf8_but_should_be_ported_to_utf16());
}
// 11.1.16 ParseTimeZoneIdentifier ( identifier ), https://tc39.es/proposal-temporal/#sec-parsetimezoneidentifier
ParsedTimeZoneIdentifier const& parse_time_zone_identifier(String const& identifier)
{
// OPTIMIZATION: Some callers can assume that parsing will succeed.
return time_zone_id_cache().ensure(identifier, [&]() {
// 1. Let parseResult be ParseText(StringToCodePoints(identifier), TimeZoneIdentifier).
auto parse_result = parse_iso8601(Production::TimeZoneIdentifier, identifier);
auto utf16_identifier = Utf16String::from_utf8(identifier);
auto parse_result = parse_iso8601(Production::TimeZoneIdentifier, utf16_identifier);
VERIFY(parse_result.has_value());
return parse_time_zone_identifier(*parse_result);
@ -560,7 +570,7 @@ ParsedTimeZoneIdentifier parse_time_zone_identifier(ParseResult const& parse_res
// b. NOTE: name is syntactically valid, but does not necessarily conform to IANA Time Zone Database naming
// guidelines or correspond with an available named time zone identifier.
// c. Return Time Zone Identifier Parse Record { [[Name]]: CodePointsToString(name), [[OffsetMinutes]]: EMPTY }.
return ParsedTimeZoneIdentifier { .name = String::from_utf8_without_validation(parse_result.time_zone_iana_name->bytes()), .offset_minutes = {} };
return ParsedTimeZoneIdentifier { .name = parse_result.time_zone_iana_name->to_utf8_but_should_be_ported_to_utf16(), .offset_minutes = {} };
}
// 4. Assert: parseResult contains a UTCOffset[~SubMinutePrecision] Parse Node.
@ -568,7 +578,7 @@ ParsedTimeZoneIdentifier parse_time_zone_identifier(ParseResult const& parse_res
// 5. Let offset be the source text matched by the UTCOffset[~SubMinutePrecision] Parse Node contained within parseResult.
// 6. Let offsetNanoseconds be ! ParseDateTimeUTCOffset(CodePointsToString(offset)).
auto offset_nanoseconds = parse_date_time_utc_offset(parse_result.time_zone_offset->source_text);
auto offset_nanoseconds = parse_date_time_utc_offset(*parse_result.time_zone_offset);
// 7. Let offsetMinutes be offsetNanoseconds / (60 × 10**9).
auto offset_minutes = offset_nanoseconds / 60'000'000'000;

View file

@ -28,13 +28,14 @@ String format_date_time_utc_offset_rounded(i64 offset_nanoseconds);
ThrowCompletionOr<String> to_temporal_time_zone_identifier(VM&, Value temporal_time_zone_like);
i64 get_offset_nanoseconds_for(String const& time_zone, Crypto::SignedBigInteger const& epoch_nanoseconds);
ISODateTime get_iso_date_time_for(String const& time_zone, Crypto::SignedBigInteger const& epoch_nanoseconds);
ThrowCompletionOr<String> to_temporal_time_zone_identifier(VM&, StringView temporal_time_zone_like);
ThrowCompletionOr<String> to_temporal_time_zone_identifier(VM&, Utf16View temporal_time_zone_like);
ThrowCompletionOr<Crypto::SignedBigInteger> get_epoch_nanoseconds_for(VM&, String const& time_zone, ISODateTime const&, Disambiguation);
ThrowCompletionOr<Crypto::SignedBigInteger> disambiguate_possible_epoch_nanoseconds(VM&, Vector<Crypto::SignedBigInteger> possible_epoch_ns, String const& time_zone, ISODateTime const&, Disambiguation);
ThrowCompletionOr<Vector<Crypto::SignedBigInteger>> get_possible_epoch_nanoseconds(VM&, String const& time_zone, ISODateTime const&);
ThrowCompletionOr<Crypto::SignedBigInteger> get_start_of_day(VM&, String const& time_zone, ISODate);
bool time_zone_equals(StringView one, StringView two);
ThrowCompletionOr<ParsedTimeZoneIdentifier> parse_time_zone_identifier(VM&, String const& identifier);
ThrowCompletionOr<ParsedTimeZoneIdentifier> parse_time_zone_identifier(VM&, Utf16View identifier);
ParsedTimeZoneIdentifier const& parse_time_zone_identifier(String const& identifier);
ParsedTimeZoneIdentifier parse_time_zone_identifier(ParseResult const&);

View file

@ -6,6 +6,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Utf16String.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Date.h>
#include <LibJS/Runtime/Temporal/Calendar.h>
@ -213,7 +214,7 @@ ThrowCompletionOr<GC::Ref<ZonedDateTime>> to_temporal_zoned_date_time(VM& vm, Va
return vm.throw_completion<TypeError>(ErrorType::TemporalInvalidZonedDateTimeString, item);
// b. Let result be ? ParseISODateTime(item, « TemporalDateTimeString[+Zoned] »).
auto result = TRY(parse_iso_date_time(vm, item.as_string().utf8_string(), { { Production::TemporalZonedDateTimeString } }));
auto result = TRY(parse_iso_date_time(vm, item.as_string().utf16_string_view(), { { Production::TemporalZonedDateTimeString } }));
// c. Let annotation be result.[[TimeZone]].[[TimeZoneAnnotation]].
auto annotation = move(result.time_zone.time_zone_annotation);
@ -222,7 +223,8 @@ ThrowCompletionOr<GC::Ref<ZonedDateTime>> to_temporal_zoned_date_time(VM& vm, Va
VERIFY(annotation.has_value());
// e. 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));
// f. Let offsetString be result.[[TimeZone]].[[OffsetString]].
offset_string = move(result.time_zone.offset_string);
@ -246,7 +248,8 @@ ThrowCompletionOr<GC::Ref<ZonedDateTime>> to_temporal_zoned_date_time(VM& vm, Va
// l. If offsetString is not EMPTY, then
if (offset_string.has_value()) {
// i. 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);
// ii. Assert: offsetParseResult is a Parse Node.
VERIFY(offset_parse_result.has_value());

View file

@ -69,7 +69,7 @@ ThrowCompletionOr<GC::Ref<Object>> ZonedDateTimeConstructor::construct(FunctionO
return vm.throw_completion<TypeError>(ErrorType::NotAString, time_zone_value);
// 5. Let timeZoneParse be ? ParseTimeZoneIdentifier(timeZone).
auto time_zone_parse = TRY(parse_time_zone_identifier(vm, time_zone_value.as_string().utf8_string()));
auto time_zone_parse = TRY(parse_time_zone_identifier(vm, time_zone_value.as_string().utf16_string_view()));
String time_zone;
@ -100,7 +100,7 @@ ThrowCompletionOr<GC::Ref<Object>> ZonedDateTimeConstructor::construct(FunctionO
return vm.throw_completion<TypeError>(ErrorType::NotAString, calendar_value);
// 10. 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()));
// 11. Return ? CreateTemporalZonedDateTime(epochNanoseconds, timeZone, calendar, NewTarget).
return TRY(create_temporal_zoned_date_time(vm, epoch_nanoseconds, move(time_zone), move(calendar), new_target));

View file

@ -116,7 +116,7 @@ JS_DEFINE_NATIVE_FUNCTION(Uint8ArrayConstructorHelpers::from_base64)
}
// 9. Let result be FromBase64(string, alphabet, lastChunkHandling).
auto result = JS::from_base64(vm, string_value.as_string().utf8_string(), alphabet, last_chunk_handling);
auto result = JS::from_base64(vm, string_value.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16(), alphabet, last_chunk_handling);
// 10. If result.[[Error]] is not NONE, then
if (result.error.has_value()) {
@ -152,7 +152,7 @@ JS_DEFINE_NATIVE_FUNCTION(Uint8ArrayConstructorHelpers::from_hex)
return vm.throw_completion<TypeError>(ErrorType::NotAString, string_value);
// 2. Let result be FromHex(string).
auto result = JS::from_hex(vm, string_value.as_string().utf8_string());
auto result = JS::from_hex(vm, string_value.as_string().utf16_string_view());
// 3. If result.[[Error]] is not NONE, then
if (result.error.has_value()) {
@ -220,7 +220,7 @@ JS_DEFINE_NATIVE_FUNCTION(Uint8ArrayPrototypeHelpers::set_from_base64)
auto byte_length = typed_array_length(typed_array_record);
// 14. Let result be FromBase64(string, alphabet, lastChunkHandling, byteLength).
auto result = JS::from_base64(vm, string_value.as_string().utf8_string(), alphabet, last_chunk_handling, byte_length);
auto result = JS::from_base64(vm, string_value.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16(), alphabet, last_chunk_handling, byte_length);
// 15. Let bytes be result.[[Bytes]].
auto bytes = move(result.bytes);
@ -280,7 +280,7 @@ JS_DEFINE_NATIVE_FUNCTION(Uint8ArrayPrototypeHelpers::set_from_hex)
auto byte_length = typed_array_length(typed_array_record);
// 7. Let result be FromHex(string, byteLength).
auto result = JS::from_hex(vm, string_value.as_string().utf8_string(), byte_length);
auto result = JS::from_hex(vm, string_value.as_string().utf16_string_view(), byte_length);
// 8. Let bytes be result.[[Bytes]].
auto bytes = move(result.bytes);
@ -524,14 +524,14 @@ DecodeResult from_base64(VM& vm, StringView string, Alphabet alphabet, AK::LastC
}
// 23.3.3.8 FromHex ( string [ , maxLength ] ), https://tc39.es/ecma262/#sec-fromhex
DecodeResult from_hex(VM& vm, StringView string, Optional<size_t> max_length)
DecodeResult from_hex(VM& vm, Utf16View string, Optional<size_t> max_length)
{
// 1. If maxLength is not present, set maxLength to 2**53 - 1.
if (!max_length.has_value())
max_length = MAX_ARRAY_LIKE_INDEX;
// 2. Let length be the length of string.
auto length = string.length();
auto length = string.length_in_code_units();
// 3. Let bytes be a new empty List.
ByteBuffer bytes;

View file

@ -10,6 +10,7 @@
#include <AK/ByteBuffer.h>
#include <AK/Optional.h>
#include <AK/StringView.h>
#include <AK/Utf16View.h>
#include <LibGC/Ptr.h>
#include <LibJS/Forward.h>
#include <LibJS/Runtime/Completion.h>
@ -53,6 +54,6 @@ ThrowCompletionOr<ByteBuffer> get_uint8_array_bytes(VM&, TypedArrayBase const&);
ThrowCompletionOr<ReadonlyBytes> get_uint8_array_bytes_view(VM&, TypedArrayBase const&);
void set_uint8_array_bytes(TypedArrayBase&, ReadonlyBytes);
DecodeResult from_base64(VM&, StringView string, Alphabet alphabet, AK::LastChunkHandling last_chunk_handling, Optional<size_t> max_length = {});
DecodeResult from_hex(VM&, StringView string, Optional<size_t> max_length = {});
DecodeResult from_hex(VM&, Utf16View string, Optional<size_t> max_length = {});
}

View file

@ -7,6 +7,7 @@
*/
#include <AK/AllOf.h>
#include <AK/Array.h>
#include <AK/Assertions.h>
#include <AK/ByteString.h>
#include <AK/CharacterTypes.h>
@ -426,7 +427,7 @@ String Value::to_string_without_side_effects() const
case INT32_TAG:
return String::number(as_i32());
case STRING_TAG:
return as_string().utf8_string();
return as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16();
case SYMBOL_TAG:
return as_symbol().descriptive_string().to_utf8_but_should_be_ported_to_utf16();
case BIGINT_TAG:
@ -487,55 +488,10 @@ ThrowCompletionOr<GC::Ref<PrimitiveString>> Value::to_primitive_string(VM& vm)
return PrimitiveString::create(vm, move(string));
}
// 7.1.17 ToString ( argument ), https://tc39.es/ecma262/#sec-tostring
ThrowCompletionOr<String> Value::to_string(VM& vm) const
{
if (is_double())
return number_to_string(m_value.as_double);
switch (m_value.tag) {
// 1. If argument is a String, return argument.
case STRING_TAG:
return as_string().utf8_string();
// 2. If argument is a Symbol, throw a TypeError exception.
case SYMBOL_TAG:
return vm.throw_completion<TypeError>(ErrorType::Convert, "symbol", "string");
// 3. If argument is undefined, return "undefined".
case UNDEFINED_TAG:
return "undefined"_string;
// 4. If argument is null, return "null".
case NULL_TAG:
return "null"_string;
// 5. If argument is true, return "true".
// 6. If argument is false, return "false".
case BOOLEAN_TAG:
return as_bool() ? "true"_string : "false"_string;
// 7. If argument is a Number, return Number::toString(argument, 10).
case INT32_TAG:
return String::number(as_i32());
// 8. If argument is a BigInt, return BigInt::toString(argument, 10).
case BIGINT_TAG:
return TRY_OR_THROW_OOM(vm, as_bigint().big_integer().to_base(10));
// 9. Assert: argument is an Object.
case OBJECT_TAG: {
// 10. Let primValue be ? ToPrimitive(argument, string).
auto primitive_value = TRY(to_primitive(vm, PreferredType::String));
// 11. Assert: primValue is not an Object.
VERIFY(!primitive_value.is_object());
// 12. Return ? ToString(primValue).
return primitive_value.to_string(vm);
}
default:
VERIFY_NOT_REACHED();
}
}
// 7.1.17 ToString ( argument ), https://tc39.es/ecma262/#sec-tostring
ThrowCompletionOr<ByteString> Value::to_byte_string(VM& vm) const
{
return TRY(to_string(vm)).to_byte_string();
return TRY(to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16().to_byte_string();
}
// 7.1.17 ToString ( argument ), https://tc39.es/ecma262/#sec-tostring
@ -746,17 +702,47 @@ constexpr bool is_ascii_number(u32 code_point)
return is_ascii_digit(code_point) || code_point == '.' || (code_point == 'e' || code_point == 'E') || code_point == '+' || code_point == '-';
}
static constexpr AK::Array js_whitespace_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 js_whitespace { js_whitespace_code_units.data(), js_whitespace_code_units.size() };
struct NumberParseResult {
StringView literal;
Utf16View literal;
u8 base;
};
static Optional<NumberParseResult> parse_number_text(StringView text)
static Optional<NumberParseResult> parse_number_text(Utf16View text)
{
NumberParseResult result {};
auto check_prefix = [&](auto lower_prefix, auto upper_prefix) {
if (text.length() <= 2)
auto check_prefix = [&](Utf16View lower_prefix, Utf16View upper_prefix) {
if (text.length_in_code_units() <= 2)
return false;
if (!text.starts_with(lower_prefix) && !text.starts_with(upper_prefix))
return false;
@ -794,10 +780,10 @@ static Optional<NumberParseResult> parse_number_text(StringView text)
}
// 7.1.4.1.1 StringToNumber ( str ), https://tc39.es/ecma262/#sec-stringtonumber
double string_to_number(StringView string)
double string_to_number(Utf16View string)
{
// 1. Let text be StringToCodePoints(str).
auto text = Utf8View(string).trim(whitespace_characters, AK::TrimMode::Both).as_string();
auto text = string.trim(js_whitespace);
// 2. Let literal be ParseText(text, StringNumericLiteral).
if (text.is_empty())
@ -815,7 +801,7 @@ double string_to_number(StringView string)
// 4. Return StringNumericValue of literal.
if (result->base != 10) {
auto bigint = MUST(Crypto::UnsignedBigInteger::from_base(result->base, result->literal));
auto bigint = MUST(Crypto::UnsignedBigInteger::from_base(result->base, result->literal.to_utf8_but_should_be_ported_to_utf16()));
return bigint.to_double();
}
@ -852,7 +838,7 @@ ThrowCompletionOr<Value> Value::to_number_slow_case(VM& vm) const
return Value(as_bool() ? 1 : 0);
// 6. If argument is a String, return StringToNumber(argument).
case STRING_TAG:
return string_to_number(as_string().utf8_string());
return string_to_number(as_string().utf16_string_view());
// 7. Assert: argument is an Object.
case OBJECT_TAG: {
// 8. Let primValue be ? ToPrimitive(argument, number).
@ -869,7 +855,7 @@ ThrowCompletionOr<Value> Value::to_number_slow_case(VM& vm) const
}
}
static Optional<BigInt*> string_to_bigint(VM& vm, StringView string);
static Optional<BigInt*> string_to_bigint(VM& vm, Utf16View string);
// 7.1.13 ToBigInt ( argument ), https://tc39.es/ecma262/#sec-tobigint
ThrowCompletionOr<GC::Ref<BigInt>> Value::to_bigint(VM& vm) const
@ -906,7 +892,7 @@ ThrowCompletionOr<GC::Ref<BigInt>> Value::to_bigint(VM& vm) const
return primitive.as_bigint();
case STRING_TAG: {
// 1. Let n be ! StringToBigInt(prim).
auto bigint = string_to_bigint(vm, primitive.as_string().utf8_string());
auto bigint = string_to_bigint(vm, primitive.as_string().utf16_string_view());
// 2. If n is undefined, throw a SyntaxError exception.
if (!bigint.has_value())
@ -925,17 +911,17 @@ ThrowCompletionOr<GC::Ref<BigInt>> Value::to_bigint(VM& vm) const
}
struct BigIntParseResult {
StringView literal;
Utf16View literal;
u8 base { 10 };
bool is_negative { false };
};
static Optional<BigIntParseResult> parse_bigint_text(StringView text)
static Optional<BigIntParseResult> parse_bigint_text(Utf16View text)
{
BigIntParseResult result {};
auto parse_for_prefixed_base = [&](auto lower_prefix, auto upper_prefix, auto validator) {
if (text.length() <= 2)
auto parse_for_prefixed_base = [&](Utf16View lower_prefix, Utf16View upper_prefix, auto validator) {
if (text.length_in_code_units() <= 2)
return false;
if (!text.starts_with(lower_prefix) && !text.starts_with(upper_prefix))
return false;
@ -970,10 +956,10 @@ static Optional<BigIntParseResult> parse_bigint_text(StringView text)
}
// 7.1.14 StringToBigInt ( str ), https://tc39.es/ecma262/#sec-stringtobigint
static Optional<BigInt*> string_to_bigint(VM& vm, StringView string)
static Optional<BigInt*> string_to_bigint(VM& vm, Utf16View string)
{
// 1. Let text be StringToCodePoints(str).
auto text = Utf8View(string).trim(whitespace_characters, AK::TrimMode::Both).as_string();
auto text = string.trim(js_whitespace);
// 2. Let literal be ParseText(text, StringIntegerLiteral).
auto result = parse_bigint_text(text);
@ -984,7 +970,7 @@ static Optional<BigInt*> string_to_bigint(VM& vm, StringView string)
// 4. Let mv be the MV of literal.
// 5. Assert: mv is an integer.
auto bigint = MUST(Crypto::SignedBigInteger::from_base(result->base, result->literal));
auto bigint = MUST(Crypto::SignedBigInteger::from_base(result->base, result->literal.to_utf8_but_should_be_ported_to_utf16()));
if (result->is_negative && (bigint != bigint_zero()))
bigint.negate();
@ -2431,7 +2417,7 @@ ThrowCompletionOr<bool> is_loosely_equal(VM& vm, Value lhs, Value rhs)
// 7. If Type(x) is BigInt and Type(y) is String, then
if (lhs.is_bigint() && rhs.is_string()) {
// a. Let n be StringToBigInt(y).
auto bigint = string_to_bigint(vm, rhs.as_string().utf8_string());
auto bigint = string_to_bigint(vm, rhs.as_string().utf16_string_view());
// b. If n is undefined, return false.
if (!bigint.has_value())
@ -2530,7 +2516,7 @@ ThrowCompletionOr<TriState> is_less_than(VM& vm, Value lhs, Value rhs, bool left
// a. If px is a BigInt and py is a String, then
if (x_primitive.is_bigint() && y_primitive.is_string()) {
// i. Let ny be StringToBigInt(py).
auto y_bigint = string_to_bigint(vm, y_primitive.as_string().utf8_string());
auto y_bigint = string_to_bigint(vm, y_primitive.as_string().utf16_string_view());
// ii. If ny is undefined, return undefined.
if (!y_bigint.has_value())
@ -2545,7 +2531,7 @@ ThrowCompletionOr<TriState> is_less_than(VM& vm, Value lhs, Value rhs, bool left
// b. If px is a String and py is a BigInt, then
if (x_primitive.is_string() && y_primitive.is_bigint()) {
// i. Let nx be StringToBigInt(px).
auto x_bigint = string_to_bigint(vm, x_primitive.as_string().utf8_string());
auto x_bigint = string_to_bigint(vm, x_primitive.as_string().utf16_string_view());
// ii. If nx is undefined, return undefined.
if (!x_bigint.has_value())

View file

@ -18,6 +18,7 @@
#include <AK/SourceLocation.h>
#include <AK/String.h>
#include <AK/Types.h>
#include <AK/Utf16View.h>
#include <LibGC/NanBoxedValue.h>
#include <LibGC/Ptr.h>
#include <LibGC/Root.h>
@ -401,7 +402,6 @@ public:
u64 encoded() const { return m_value.encoded; }
ThrowCompletionOr<String> to_string(VM&) const;
ThrowCompletionOr<ByteString> to_byte_string(VM&) const;
ThrowCompletionOr<Utf16String> to_utf16_string(VM&) const;
ThrowCompletionOr<GC::Ref<PrimitiveString>> to_primitive_string(VM&);
@ -597,7 +597,7 @@ JS_API void number_to_string(StringBuilder&, double, NumberToStringMode = Number
[[nodiscard]] JS_API String number_to_string(double, NumberToStringMode = NumberToStringMode::WithExponent);
[[nodiscard]] JS_API Utf16String number_to_utf16_string(double, NumberToStringMode = NumberToStringMode::WithExponent);
[[nodiscard]] ByteString number_to_byte_string(double, NumberToStringMode = NumberToStringMode::WithExponent);
double string_to_number(StringView);
double string_to_number(Utf16View);
inline bool Value::operator==(Value const& value) const { return same_value(*this, value); }

View file

@ -19,7 +19,7 @@ struct ValueTraits : public Traits<Value> {
{
VERIFY(!value.is_special_empty_value());
if (value.is_string())
return value.as_string().utf8_string().hash();
return value.as_string().utf16_string_view().hash();
if (value.is_bigint())
return value.as_bigint().big_integer().hash();

View file

@ -208,7 +208,7 @@ inline JS_DEFINE_NATIVE_FUNCTION(TestRunnerGlobalObject::report_test)
return JS::js_undefined();
auto test_name_value = vm.argument(0);
auto test_name = TRY(test_name_value.to_string(vm));
auto test_name = TRY(test_name_value.to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16();
auto state_value = vm.argument(1);
self.on_test_reported(test_name, state_value);
return JS::js_undefined();
@ -293,7 +293,7 @@ inline Vector<ByteString> TestRunner::get_test_paths() const
inline void print_test_timings(String test_name, JS::Value state_value)
{
if (state_value.is_string()) {
auto state_string = state_value.as_string().utf8_string();
auto state_string = state_value.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16();
if (state_string == "pass"sv) {
print_modifiers({ FG_BOLD });
out("Finished: ");

View file

@ -69,26 +69,26 @@ static WebIDL::ExceptionOr<KeyframeType<AL>> process_a_keyframe_like_object(JS::
return Optional<double> {};
auto double_value = TRY(value.to_double(vm));
if (isnan(double_value) || isinf(double_value))
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("Invalid offset value: {}", TRY(value.to_string(vm)))) };
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("Invalid offset value: {}", TRY(value.to_utf16_string(vm)))) };
return double_value;
};
Function<WebIDL::ExceptionOr<String>(JS::Value)> to_string = [&vm](JS::Value value) -> WebIDL::ExceptionOr<String> {
return TRY(value.to_string(vm));
return TRY(value.to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16();
};
Function<WebIDL::ExceptionOr<Bindings::CompositeOperationOrAuto>(JS::Value)> to_composite_operation = [&vm](JS::Value value) -> WebIDL::ExceptionOr<Bindings::CompositeOperationOrAuto> {
if (value.is_undefined())
return Bindings::CompositeOperationOrAuto::Auto;
auto string_value = TRY(value.to_string(vm));
if (string_value == "replace")
auto string_value = TRY(value.to_utf16_string(vm));
if (string_value == "replace"sv)
return Bindings::CompositeOperationOrAuto::Replace;
if (string_value == "add")
if (string_value == "add"sv)
return Bindings::CompositeOperationOrAuto::Add;
if (string_value == "accumulate")
if (string_value == "accumulate"sv)
return Bindings::CompositeOperationOrAuto::Accumulate;
if (string_value == "auto")
if (string_value == "auto"sv)
return Bindings::CompositeOperationOrAuto::Auto;
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Invalid composite value"sv };
@ -158,7 +158,7 @@ static WebIDL::ExceptionOr<KeyframeType<AL>> process_a_keyframe_like_object(JS::
if (!input_property.is_string())
continue;
auto name = input_property.as_string().utf8_string();
auto name = input_property.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16();
// Handle the two special cases
if (name == "cssFloat"sv || name == "cssOffset"sv) {
@ -204,7 +204,7 @@ static WebIDL::ExceptionOr<KeyframeType<AL>> process_a_keyframe_like_object(JS::
else {
// Let property values be the result of converting raw value to a DOMString using the procedure for
// converting an ECMAScript value to a DOMString [WEBIDL].
property_values = TRY(raw_value.to_string(vm));
property_values = TRY(raw_value.to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16();
}
// 4. Calculate the normalized property name as the result of applying the IDL attribute name to animation

View file

@ -61,7 +61,7 @@ JS::ThrowCompletionOr<GC::Ref<JS::Object>> AudioConstructor::construct(FunctionO
// 4. If src is given, then set an attribute value for audio using "src" and src.
// (This will cause the user agent to invoke the object's resource selection algorithm before returning.)
if (!src_value.is_undefined()) {
auto src = TRY(src_value.to_string(vm));
auto src = TRY(src_value.to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16();
audio->set_attribute_value(HTML::AttributeNames::src, move(src));
}

View file

@ -402,7 +402,7 @@ void initialize_main_thread_vm(AgentType type)
auto specifier = vm.argument(0);
// 1. Set specifier to ? ToString(specifier).
auto specifier_string = TRY(specifier.to_string(vm));
auto specifier_string = TRY(specifier.to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16();
// 2. Let url be the result of resolving a module specifier given moduleScript and specifier.
auto url = TRY(Bindings::throw_dom_exception_if_needed(vm, [&] {

View file

@ -72,7 +72,7 @@ JS::ThrowCompletionOr<GC::Ref<JS::Object>> OptionConstructor::construct(Function
// 4. If value is given, then set an attribute value for option using "value" and value.
if (!vm.argument(1).is_undefined()) {
auto value = TRY(vm.argument(1).to_string(vm));
auto value = TRY(vm.argument(1).to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16();
option_element->set_attribute_value(HTML::AttributeNames::value, value);
}

View file

@ -73,7 +73,7 @@ OrderedHashMap<FlyString, Vector<u32>> CSSFontFeatureValuesMap::to_ordered_hash_
OrderedHashMap<FlyString, Vector<u32>> result;
for (auto const& entry : *m_map_entries) {
auto key = MUST(entry.key.to_string(vm()));
auto key = MUST(entry.key.to_utf16_string(vm())).to_utf8_but_should_be_ported_to_utf16();
auto const& array = as<JS::Array>(entry.value.as_object());
auto array_length = MUST(MUST(array.get(vm().names.length)).to_length(vm()));

View file

@ -90,7 +90,7 @@ static WebIDL::ExceptionOr<CSSUnparsedSegment> unparsed_segment_from_js_value(JS
{
if (auto variable_reference = value.as_if<CSSVariableReferenceValue>())
return GC::Ref { *variable_reference };
return TRY(value.to_string(vm));
return TRY(value.to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16();
}
// https://drafts.css-houdini.org/css-typed-om-1/#ref-for-dfn-set-the-value-of-an-existing-indexed-property

View file

@ -456,7 +456,7 @@ GC::Ref<WebIDL::Promise> Clipboard::write(GC::RootVector<GC::Ref<ClipboardItem>>
// 1. If v is a DOMString, then follow the below steps:
if (value.is_string()) {
// 1. Let dataAsBytes be the result of UTF-8 encoding v.
auto const& data_as_bytes = value.as_string().utf8_string();
auto const& data_as_bytes = value.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16();
// 2. Let blobData be a Blob created using dataAsBytes with its type set to representations MIME type.
auto blob_data = FileAPI::Blob::create(realm, MUST(ByteBuffer::copy(data_as_bytes.bytes())), move(mime_type));

View file

@ -144,7 +144,7 @@ WebIDL::ExceptionOr<GC::Ref<WebIDL::Promise>> ClipboardItem::get_type(String con
// 1. If v is a DOMString, then follow the below steps:
if (value.is_string()) {
// 1. Let dataAsBytes be the result of UTF-8 encoding v.
auto utf8_string = value.as_string().utf8_string();
auto utf8_string = value.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16();
auto data_as_bytes = MUST(ByteBuffer::copy(utf8_string.bytes()));
// 2. Let blobData be a Blob created using dataAsBytes with its type set to mimeType, serialized.

View file

@ -51,7 +51,7 @@ static JS::ThrowCompletionOr<HashAlgorithmIdentifier> hash_algorithm_identifier_
auto maybe_normalized_algorithm = [&]() -> WebIDL::ExceptionOr<NormalizedAlgorithmAndParameter> {
if (hash_value.is_string()) {
auto const hash_string = TRY(hash_value.to_string(vm));
auto const hash_string = TRY(hash_value.to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16();
return normalize_an_algorithm(*realm, hash_string, "digest"_string);
}
if (hash_value.is_object()) {
@ -520,7 +520,7 @@ JS::ThrowCompletionOr<NonnullOwnPtr<AlgorithmParams>> EcKeyGenParams::from_value
auto& object = value.as_object();
auto curve_value = TRY(object.get("namedCurve"_utf16_fly_string));
auto curve = TRY(curve_value.to_string(vm));
auto curve = TRY(curve_value.to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16();
return adopt_own<AlgorithmParams>(*new EcKeyGenParams { curve });
}
@ -574,7 +574,7 @@ JS::ThrowCompletionOr<NonnullOwnPtr<AlgorithmParams>> EcKeyImportParams::from_va
auto& object = value.as_object();
auto named_curve_value = TRY(object.get("namedCurve"_utf16_fly_string));
auto named_curve = TRY(named_curve_value.to_string(vm));
auto named_curve = TRY(named_curve_value.to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16();
return adopt_own<AlgorithmParams>(*new EcKeyImportParams { named_curve });
}

View file

@ -51,7 +51,7 @@ struct HashAlgorithmIdentifier : public AlgorithmIdentifier {
[](String const& name) -> JS::ThrowCompletionOr<String> { return name; },
[&](GC::Root<JS::Object> const& obj) -> JS::ThrowCompletionOr<String> {
auto name_property = TRY(obj->get("name"_utf16_fly_string));
return name_property.to_string(vm);
return TRY(name_property.to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16();
});
return value;

View file

@ -447,7 +447,7 @@ String CryptoKey::algorithm_name() const
{
if (m_algorithm_name.is_empty()) {
auto name = MUST(m_algorithm_cached->get("name"_utf16_fly_string));
m_algorithm_name = MUST(name.to_string(vm()));
m_algorithm_name = MUST(name.to_utf16_string(vm())).to_utf8_but_should_be_ported_to_utf16();
}
return m_algorithm_name;
}

View file

@ -134,7 +134,7 @@ WebIDL::ExceptionOr<NormalizedAlgorithmAndParameter> normalize_an_algorithm(JS::
}
// 4. Let algName be the value of the name attribute of initialAlg.
auto algorithm_name = TRY(initial_algorithm.to_string(vm));
auto algorithm_name = TRY(initial_algorithm.to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16();
RegisteredAlgorithm desired_type;

View file

@ -3431,7 +3431,7 @@ void Element::enqueue_a_custom_element_callback_reaction(FlyString const& callba
VERIFY(!arguments.is_empty());
auto& attribute_name_value = arguments.first();
VERIFY(attribute_name_value.is_string());
auto attribute_name = attribute_name_value.as_string().utf8_string();
auto attribute_name = attribute_name_value.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16();
// 2. If definition's observed attributes does not contain attributeName, then return.
if (!definition->observed_attributes().contains_slow(attribute_name))

View file

@ -769,7 +769,7 @@ JS::ThrowCompletionOr<void> EventTarget::process_event_handler_for_event(FlyStri
// 2. If event's returnValue attribute's value is the empty string, then set event's returnValue attribute's value to return value.
auto& before_unload_event = static_cast<HTML::BeforeUnloadEvent&>(event);
if (before_unload_event.return_value().is_empty())
before_unload_event.set_return_value(TRY(return_value.to_string(vm())));
before_unload_event.set_return_value(TRY(return_value.to_utf16_string(vm())).to_utf8_but_should_be_ported_to_utf16());
}
}

View file

@ -56,7 +56,7 @@ WebIDL::ExceptionOr<GC::Ref<Origin>> Origin::from(JS::VM& vm, JS::Value value)
}
// 2. If value is a string:
else if (value.is_string()) {
auto string = value.as_string().utf8_string();
auto string = value.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16();
// 1. Let parsedURL be the result of basic URL parsing value.
auto parsed_url = URL::Parser::basic_parse(string);

View file

@ -96,15 +96,14 @@ WebIDL::ExceptionOr<void> TextEncoderStream::encode_and_enqueue_chunk(JS::Value
auto& vm = this->vm();
// 1. Let input be the result of converting chunk to a DOMString.
auto input = TRY(chunk.to_string(vm));
auto input = TRY(chunk.to_utf16_string(vm));
// 2. Convert input to an I/O queue of code units.
// Spec Note: DOMString, as well as an I/O queue of code units rather than scalar values, are used here so that a
// surrogate pair that is split between chunks can be reassembled into the appropriate scalar value.
// The behavior is otherwise identical to USVString. In particular, lone surrogates will be replaced
// with U+FFFD.
auto code_points = input.code_points();
auto it = code_points.begin();
size_t code_unit_index = 0;
// 3. Let output be the I/O queue of bytes « end-of-queue ».
ByteBuffer output;
@ -113,7 +112,7 @@ WebIDL::ExceptionOr<void> TextEncoderStream::encode_and_enqueue_chunk(JS::Value
while (true) {
// 2. If item is end-of-queue, then:
// NOTE: This is done out-of-order so that we're not dereferencing a code point iterator that points to the end.
if (it.done()) {
if (code_unit_index >= input.length_in_code_units()) {
// 1. Convert output into a byte sequence.
// Note: No-op.
@ -132,10 +131,10 @@ WebIDL::ExceptionOr<void> TextEncoderStream::encode_and_enqueue_chunk(JS::Value
}
// 1. Let item be the result of reading from input.
auto item = *it;
auto item = input.code_unit_at(code_unit_index);
// 3. Let result be the result of executing the convert code unit to scalar value algorithm with encoder, item and input.
auto result = convert_code_unit_to_scalar_value(item, it);
auto result = convert_code_unit_to_scalar_value(item, code_unit_index);
// 4. If result is not continue, then process an item with result, encoders encoder, input, output, and "fatal".
if (result.has_value()) {
@ -168,10 +167,10 @@ WebIDL::ExceptionOr<void> TextEncoderStream::encode_and_flush()
}
// https://encoding.spec.whatwg.org/#convert-code-unit-to-scalar-value
Optional<u32> TextEncoderStream::convert_code_unit_to_scalar_value(u32 item, Utf8CodePointIterator& code_point_iterator)
Optional<u32> TextEncoderStream::convert_code_unit_to_scalar_value(u32 item, size_t& code_unit_index)
{
ArmedScopeGuard move_to_next_code_point_guard = [&] {
++code_point_iterator;
++code_unit_index;
};
// 1. If encoders leading surrogate is non-null, then:

View file

@ -32,7 +32,7 @@ private:
WebIDL::ExceptionOr<void> encode_and_enqueue_chunk(JS::Value);
WebIDL::ExceptionOr<void> encode_and_flush();
Optional<u32> convert_code_unit_to_scalar_value(u32 item, Utf8CodePointIterator& code_point_iterator);
Optional<u32> convert_code_unit_to_scalar_value(u32 item, size_t& code_unit_index);
// https://encoding.spec.whatwg.org/#textencoderstream-pending-high-surrogate
Optional<u32> m_leading_surrogate;

View file

@ -114,7 +114,7 @@ static JS::ThrowCompletionOr<Vector<String>> convert_value_to_sequence_of_string
// 2. Let x be ? ToString(V).
// 3. Return the IDL DOMString value that represents the same sequence of code units as the one the ECMAScript String value x represents.
auto string_value = TRY(next_item.to_string(vm));
auto string_value = TRY(next_item.to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16();
sequence_of_strings.append(move(string_value));

View file

@ -130,7 +130,7 @@ WebIDL::ExceptionOr<void> DOMStringMap::set_value_of_new_named_property(String c
{
// NOTE: Since PlatformObject does not know the type of value, we must convert it ourselves.
// The type of `value` is `DOMString`.
auto value = TRY(unconverted_value.to_string(vm()));
auto value = TRY(unconverted_value.to_utf16_string(vm())).to_utf8_but_should_be_ported_to_utf16();
StringBuilder builder;

View file

@ -1400,7 +1400,7 @@ void HTMLInputElement::create_range_input_shadow_tree()
auto key_value = MUST(vm.argument(0).get(vm, "key"_utf16_fly_string));
if (!key_value.is_string())
return JS::js_undefined();
auto key = key_value.as_string().utf8_string();
auto key = key_value.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16();
if (key == "ArrowLeft" || key == "ArrowDown")
MUST(step_down());

View file

@ -2625,7 +2625,7 @@ GC::Ptr<DOM::Document> Navigable::evaluate_javascript_url(URL::URL const& url, U
// 9. If evaluationStatus is a normal completion, and evaluationStatus.[[Value]] is a String, then set result to evaluationStatus.[[Value]].
if (evaluation_status.type() == JS::Completion::Type::Normal && evaluation_status.value().is_string()) {
result = evaluation_status.value().as_string().utf8_string();
result = evaluation_status.value().as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16();
} else {
// 10. Otherwise, return null.
return nullptr;

View file

@ -164,7 +164,7 @@ WebIDL::ExceptionOr<ModuleSpecifierMap> sort_and_normalise_module_specifier_map(
}
// 4. Let addressURL be the result of resolving a URL-like module specifier given value and baseURL.
auto address_url = resolve_url_like_module_specifier(value.as_string().utf8_string(), base_url);
auto address_url = resolve_url_like_module_specifier(value.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16(), base_url);
// 5. If addressURL is null, then:
if (!address_url.has_value()) {
@ -282,7 +282,7 @@ WebIDL::ExceptionOr<ModuleIntegrityMap> normalize_module_integrity_map(JS::Realm
}
// 4. Set normalized[resolvedURL] to value.
normalized.set(resolved_url.release_value(), value.as_string().utf8_string());
normalized.set(resolved_url.release_value(), value.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16());
}
// 3. Return normalized.

View file

@ -294,7 +294,7 @@ WebIDL::ExceptionOr<void> Storage::set_value_of_named_property(String const& key
{
// NOTE: Since PlatformObject does not know the type of value, we must convert it ourselves.
// The type of `value` is `DOMString`.
auto value = TRY(unconverted_value.to_string(vm()));
auto value = TRY(unconverted_value.to_utf16_string(vm())).to_utf8_but_should_be_ported_to_utf16();
return set_item(key, value);
}

View file

@ -302,7 +302,7 @@ public:
serialized.encode(MUST(value.as_bigint().big_integer().to_base(10)));
} else if (value.is_string()) {
serialized.encode(ValueTag::StringPrimitive);
serialized.encode(value.as_string().utf8_string());
serialized.encode(value.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16());
} else {
return_primitive_type = false;
}
@ -338,7 +338,7 @@ public:
// 10. Otherwise, if value has a [[StringData]] internal slot, then set serialized to { [[Type]]: "String", [[StringData]]: value.[[StringData]] }.
else if (auto const* string_object = as_if<JS::StringObject>(*object)) {
serialized.encode(ValueTag::StringObject);
serialized.encode(string_object->primitive_string().utf8_string());
serialized.encode(string_object->primitive_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16());
}
// 11. Otherwise, if value has a [[DateValue]] internal slot, then set serialized to { [[Type]]: "Date", [[DateValue]]: value.[[DateValue]] }.
@ -397,7 +397,7 @@ public:
// 2. If name is not one of "Error", "EvalError", "RangeError", "ReferenceError", "SyntaxError", "TypeError", or "URIError", then set name to "Error".
auto type = ErrorType::Error;
if (name.is_string())
type = error_name_to_type(name.as_string().utf8_string());
type = error_name_to_type(name.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16());
// 3. Let valueMessageDesc be ? value.[[GetOwnProperty]]("message").
auto value_message_descriptor = TRY(object->internal_get_own_property(m_vm.names.message));

View file

@ -293,7 +293,7 @@ WebIDL::ExceptionOr<GC::Ref<Key>> convert_a_value_to_a_key(JS::Realm& realm, JS:
if (input.is_string()) {
// 1. Return a new key with type string and value input.
return Key::create_string(realm, input.as_string().utf8_string());
return Key::create_string(realm, input.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16());
}
// - If input is a buffer source type
@ -2496,14 +2496,14 @@ WebIDL::ExceptionOr<GC::Ref<IDBRequest>> create_a_request_to_retrieve_multiple_i
count = TRY(TRY(query_or_options.get(vm, "count"_utf16)).to_u32(vm));
// 3. Set direction to query_or_options["direction"].
auto direction_value = TRY(TRY(query_or_options.get(vm, "direction"_utf16)).to_string(vm));
if (direction_value == "next")
auto direction_value = TRY(TRY(query_or_options.get(vm, "direction"_utf16)).to_utf16_string(vm));
if (direction_value == "next"sv)
direction = Bindings::IDBCursorDirection::Next;
else if (direction_value == "nextunique")
else if (direction_value == "nextunique"sv)
direction = Bindings::IDBCursorDirection::Nextunique;
else if (direction_value == "prev")
else if (direction_value == "prev"sv)
direction = Bindings::IDBCursorDirection::Prev;
else if (direction_value == "prevunique")
else if (direction_value == "prevunique"sv)
direction = Bindings::IDBCursorDirection::Prevunique;
}

View file

@ -50,7 +50,7 @@ WebIDL::ExceptionOr<String> serialize_javascript_value_to_json_string(JS::VM& vm
VERIFY(result.is_string());
// 4. Return result.
return result.as_string().utf8_string();
return result.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16();
}
// https://infra.spec.whatwg.org/#serialize-a-javascript-value-to-json-bytes
@ -154,7 +154,7 @@ String serialize_an_infra_value_to_a_json_string(JS::Realm& realm, JSONTopLevel
// whitespace inserted.
auto result = MUST(JS::call(vm, *realm.intrinsics().json_stringify_function(), JS::js_undefined(), js_value));
VERIFY(result.is_string());
return result.as_string().utf8_string();
return result.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16();
}
// https://infra.spec.whatwg.org/#serialize-a-javascript-value-to-json-bytes

View file

@ -77,7 +77,7 @@ ErrorOr<GC::Ref<HTML::WindowProxy>, WebDriver::Error> deserialize_web_frame(JS::
return WebDriver::Error::from_code(WebDriver::ErrorCode::InvalidArgument, "Object is not a web frame"sv);
// 2. Let reference be the result of getting the web frame identifier property from object.
auto reference = property.value().as_string().utf8_string();
auto reference = property.value().as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16();
// 3. Let browsing context be the browsing context whose window handle is reference, or null if no such browsing
// context exists.
@ -112,7 +112,7 @@ ErrorOr<GC::Ref<HTML::WindowProxy>, WebDriver::Error> deserialize_web_window(JS:
return WebDriver::Error::from_code(WebDriver::ErrorCode::InvalidArgument, "Object is not a web window"sv);
// 2. Let reference be the result of getting the web window identifier property from object.
auto reference = property.value().as_string().utf8_string();
auto reference = property.value().as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16();
// 3. Let browsing context be the browsing context whose window handle is reference, or null if no such browsing
// context exists.

View file

@ -194,7 +194,7 @@ ErrorOr<GC::Ref<Web::DOM::Element>, WebDriver::Error> deserialize_web_element(We
return WebDriver::Error::from_code(WebDriver::ErrorCode::InvalidArgument, "Object is not a web element"sv);
// 2. Let reference be the result of getting the web element identifier property from object.
auto reference = property.value().as_string().utf8_string();
auto reference = property.value().as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16();
// 3. Let element be the result of trying to get a known element with session and reference.
auto element = TRY(get_known_element(browsing_context, reference));
@ -490,7 +490,7 @@ ErrorOr<GC::Ref<Web::DOM::ShadowRoot>, WebDriver::Error> deserialize_shadow_root
return WebDriver::Error::from_code(WebDriver::ErrorCode::InvalidArgument, "Object is not a Shadow Root"sv);
// 2. Let reference be the result of getting the shadow root identifier property from object.
auto reference = property.value().as_string().utf8_string();
auto reference = property.value().as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16();
// 3. Let shadow be the result of trying to get a known shadow root with session and reference.
auto shadow = TRY(get_known_shadow_root(browsing_context, reference));

View file

@ -227,7 +227,7 @@ static Response internal_json_clone(HTML::BrowsingContext const& browsing_contex
if (value.is_number())
return JsonValue { value.as_double() };
if (value.is_string())
return JsonValue { value.as_string().utf8_string() };
return JsonValue { value.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16() };
// AD-HOC: BigInt and Symbol not mentioned anywhere in the WebDriver spec, as it references ES5.
// It assumes that all primitives are handled above, and the value is an object for the remaining steps.
@ -301,7 +301,7 @@ static Response internal_json_clone(HTML::BrowsingContext const& browsing_contex
if (!to_json_result.is_string())
return WebDriver::Error::from_code(ErrorCode::JavascriptError, "toJSON did not return a String"sv);
return JsonValue { to_json_result.as_string().utf8_string() };
return JsonValue { to_json_result.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16() };
}
// -> Otherwise

View file

@ -99,7 +99,7 @@ JS::ThrowCompletionOr<WebGLContextAttributes> convert_value_to_context_attribute
Bindings::WebGLPowerPreference power_preference_value { Bindings::WebGLPowerPreference::Default };
if (!power_preference.is_undefined()) {
auto power_preference_string = TRY(power_preference.to_string(vm));
auto power_preference_string = TRY(power_preference.to_utf16_string(vm));
if (power_preference_string == "high-performance"sv)
power_preference_value = Bindings::WebGLPowerPreference::HighPerformance;

View file

@ -234,22 +234,23 @@ JS::Completion call_user_object_operation(CallbackType& callback, Utf16FlyString
JS::ThrowCompletionOr<String> to_byte_string(JS::VM& vm, JS::Value value)
{
// 1. Let x be ? ToString(V).
auto x = TRY(value.to_string(vm));
auto x = TRY(value.to_utf16_string(vm));
// 2. If the value of any element of x is greater than 255, then throw a TypeError.
for (auto [i, character] : enumerate(x.code_points())) {
for (size_t i = 0; i < x.length_in_code_units(); ++i) {
auto character = x.code_unit_at(i);
if (character > 0xFF)
return vm.throw_completion<JS::TypeError>(MUST(String::formatted("Invalid byte 0x{:X} at index {}, must be an integer no less than 0 and no greater than 0xFF", character, x.code_points().byte_offset_of(i))));
return vm.throw_completion<JS::TypeError>(MUST(String::formatted("Invalid byte 0x{:X} at index {}, must be an integer no less than 0 and no greater than 0xFF", character, i)));
}
// 3. Return an IDL ByteString value whose length is the length of x, and where the value of each element is the value of the corresponding element of x.
// FIXME: This should return a ByteString.
return x;
return x.to_utf8_but_should_be_ported_to_utf16();
}
JS::ThrowCompletionOr<String> to_string(JS::VM& vm, JS::Value value)
{
return value.to_string(vm);
return TRY(value.to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16();
}
JS::ThrowCompletionOr<Utf16String> to_utf16_string(JS::VM& vm, JS::Value value)

View file

@ -149,8 +149,8 @@ JS_DEFINE_NATIVE_FUNCTION(TestRunnerGlobalObject::fuzzilli)
if (!vm.argument_count())
return JS::js_undefined();
auto operation = TRY(vm.argument(0).to_string(vm));
if (operation == "FUZZILLI_CRASH") {
auto operation = TRY(vm.argument(0).to_utf16_string(vm));
if (operation == "FUZZILLI_CRASH"sv) {
auto type = TRY(vm.argument(1).to_i32(vm));
switch (type) {
case 0:
@ -160,14 +160,14 @@ JS_DEFINE_NATIVE_FUNCTION(TestRunnerGlobalObject::fuzzilli)
VERIFY_NOT_REACHED();
break;
}
} else if (operation == "FUZZILLI_PRINT") {
} else if (operation == "FUZZILLI_PRINT"sv) {
static FILE* fzliout = fdopen(REPRL_DWFD, "w");
if (!fzliout) {
dbgln("Fuzzer output not available");
fzliout = stdout;
}
auto string = TRY(vm.argument(1).to_string(vm));
auto string = TRY(vm.argument(1).to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16();
outln(fzliout, "{}", string);
fflush(fzliout);
}

View file

@ -214,7 +214,7 @@ def write_enumeration_conversion(out: TextIO, enumeration: Enumeration, includes
JS::ThrowCompletionOr<{enumeration.name}> {converter_function_name(enumeration)}(JS::VM& vm, JS::Value value)
{{
// 1. Let S be the result of calling ? ToString(V).
auto value_as_string = TRY(value.to_string(vm));
auto value_as_string = TRY(value.to_utf16_string(vm));
// 2. If S is not one of Es enumeration values, then throw a TypeError.
// 3. Return the enumeration value of type E that is equal to S.
@ -1198,7 +1198,7 @@ def union_to_idl_value(
includes.add("LibJS/Runtime/ValueInlines.h")
enumeration_conversion = f"""
if ({value_name}.is_string()) {{
auto enumeration_string = TRY({value_name}.to_string(vm));
auto enumeration_string = TRY({value_name}.to_utf16_string(vm));
"""
for enumeration_type in types.enumeration_types:
enumeration = context.enumeration(enumeration_type)

View file

@ -69,7 +69,7 @@ JS_DEFINE_NATIVE_FUNCTION(ConsoleGlobalEnvironmentExtensions::$_function)
auto* console_global_object = TRY(get_console(vm));
auto& window = *console_global_object->m_window_object;
auto selector = TRY(vm.argument(0).to_string(vm));
auto selector = TRY(vm.argument(0).to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16();
if (vm.argument_count() > 1) {
auto node = vm.argument(1).as_if<Web::DOM::ParentNode>();
@ -91,7 +91,7 @@ JS_DEFINE_NATIVE_FUNCTION(ConsoleGlobalEnvironmentExtensions::$$_function)
auto* console_global_object = TRY(get_console(vm));
auto& window = *console_global_object->m_window_object;
auto selector = TRY(vm.argument(0).to_string(vm));
auto selector = TRY(vm.argument(0).to_utf16_string(vm)).to_utf8_but_should_be_ported_to_utf16();
Web::DOM::ParentNode* element = &window.associated_document();

View file

@ -58,7 +58,7 @@ static JsonValue serialize_js_value(JS::Realm& realm, JS::Value value)
return value.as_bool();
if (value.is_string())
return value.as_string().utf8_string();
return value.as_string().utf16_string_view().to_utf8_but_should_be_ported_to_utf16();
if (value.is_number()) {
if (value.is_nan())

Some files were not shown because too many files have changed in this diff Show more