LibJS: Store primitive strings as UTF-16

Keep primitive string storage in Utf16String and remove the UTF-8
storage path from PrimitiveString. ASCII strings still use compact
Utf16String ASCII storage, while UTF-16 becomes the only owned
representation.
This commit is contained in:
Andreas Kling 2026-06-21 19:02:35 +02:00 committed by Andreas Kling
parent f890f289a7
commit 13969b6bd4
49 changed files with 136 additions and 317 deletions

View file

@ -411,7 +411,7 @@ static void dump_metadata(StringBuilder& output, Executable const& executable)
else if (value.is_bigint())
output.appendff("BigInt({})", MUST(value.as_bigint().to_string()));
else if (value.is_string())
output.appendff("String(\"{}\")", value.as_string().utf8_string_view());
output.appendff("String(\"{}\")", value.as_string().utf16_string_view());
else if (value.is_undefined())
output.append("Undefined"sv);
else if (value.is_null())

View file

@ -646,7 +646,8 @@ 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).
TRY(vm.host_ensure_can_compile_strings(eval_realm, {}, code_string->utf8_string_view(), code_string->utf8_string_view(), direct == EvalMode::Direct ? CompilationType::DirectEval : CompilationType::IndirectEval, {}, x));
auto code_string_utf8 = code_string->utf8_string();
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.
bool in_function = false;
@ -1978,7 +1979,7 @@ 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_view()).value());
return static_cast<RoundingMode>(allowed_strings.first_index_of(string_value.as_string().utf8_string()).value());
}
// 14.5.2.4 GetRoundingIncrementOption ( options ), https://tc39.es/proposal-temporal/#sec-temporal-getroundingincrementoption

View file

@ -97,7 +97,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_view());
time_value = parse_date_string(vm, primitive.as_string().utf8_string());
}
// iii. Else,
else {

View file

@ -1212,7 +1212,7 @@ JS_DEFINE_NATIVE_FUNCTION(DatePrototype::symbol_to_primitive)
auto hint_value = vm.argument(0);
if (!hint_value.is_string())
return vm.throw_completion<TypeError>(ErrorType::InvalidHint, hint_value);
auto hint = hint_value.as_string().utf8_string_view();
auto hint = hint_value.as_string().utf16_string_view();
Value::PreferredType try_first;
if (hint == "string"sv || hint == "default"sv)
try_first = Value::PreferredType::String;

View file

@ -591,7 +591,7 @@ JS_DEFINE_NATIVE_FUNCTION(GlobalObject::unescape)
auto string = TRY(vm.argument(0).to_utf16_string(vm));
// 2. Let length be the length of string.
ssize_t length = string.length_in_code_units();
size_t length = string.length_in_code_units();
// 3. Let R be the empty String.
Utf16StringBuilder unescaped(length);
@ -607,7 +607,7 @@ JS_DEFINE_NATIVE_FUNCTION(GlobalObject::unescape)
// i. Let hexEscape be the empty String.
// ii. Let skip be 0.
// iii. If k ≤ length - 6 and the code unit at index k + 1 within string is the code unit 0x0075 (LATIN SMALL LETTER U), then
if (k <= length - 6
if (k + 5 < length
&& string.code_unit_at(k + 1) == 'u'
&& is_ascii_hex_digit(string.code_unit_at(k + 2))
&& is_ascii_hex_digit(string.code_unit_at(k + 3))
@ -620,7 +620,7 @@ JS_DEFINE_NATIVE_FUNCTION(GlobalObject::unescape)
k += 5;
}
// iv. Else if k ≤ length - 3, then
else if (k <= length - 3
else if (k + 2 < length
&& is_ascii_hex_digit(string.code_unit_at(k + 1))
&& is_ascii_hex_digit(string.code_unit_at(k + 2))) {
// 1. Set hexEscape to the substring of string from k + 1 to k + 3.

View file

@ -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_view() == "lookup"sv) {
if (matcher.is_string() && matcher.as_string().utf8_string() == "lookup"sv) {
// a. Let r be LookupMatchingLocaleByPrefix(availableLocales, requestedLocales).
matcher_result = lookup_matching_locale_by_prefix(requested_locales);
}
@ -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_view() == "lookup"sv) {
if (matcher.as_string().utf8_string() == "lookup"sv) {
// i. Let match be LookupMatchingLocaleByPrefix(availableLocales, « locale »).
match = lookup_matching_locale_by_prefix({ { locale } });
}

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_view());
collator->set_usage(usage.as_string().utf8_string());
// 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_view());
sensitivity = Unicode::sensitivity_from_string(sensitivity_value.as_string().utf8_string());
// 21. Let defaultIgnorePunctuation be resolvedLocaleData.[[ignorePunctuation]].
// NOTE: We do not acquire resolvedLocaleData.[[ignorePunctuation]] here. Instead, we let LibUnicode fill in the

View file

@ -275,7 +275,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_view());
option = Unicode::calendar_pattern_style_from_string(value.as_string().utf8_string());
// e. If value is not undefined, then
// i. Set hasExplicitFormatComponents to true.
@ -294,14 +294,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_view());
date_time_format->set_date_style(date_style.as_string().utf8_string());
// 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_view());
date_time_format->set_time_style(time_style.as_string().utf8_string());
// 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_view());
display_names->set_style(style.as_string().utf8_string());
// 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_view());
display_names->set_type(type.as_string().utf8_string());
// 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_view());
display_names->set_fallback(fallback.as_string().utf8_string());
// 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_view());
display_names->set_language_display(language_display.as_string().utf8_string());
// b. Set typeFields to typeFields.[[<languageDisplay>]].
// c. Assert: typeFields is a Record (see 12.2.3).

View file

@ -77,8 +77,8 @@ JS_DEFINE_NATIVE_FUNCTION(DisplayNamesPrototype::of)
code = PrimitiveString::create(vm, TRY(code.to_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_view()));
auto code_string = code.as_string().utf8_string_view();
code = TRY(canonical_code_for_display_names(vm, display_names->type(), code.as_string().utf8_string()));
auto code_string = code.as_string().utf8_string();
// 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_view());
style = DurationFormat::value_style_from_string(style_value.as_string().utf8_string());
}
// 4. If style is "numeric" and IsFractionalSecondUnitName(unit) is true, then

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_view());
duration_format->set_style(style.as_string().utf8_string());
// 14. Let prevStyle be the empty String.
Optional<DurationFormat::ValueStyle> previous_style;

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_view());
list_format->set_type(type.as_string().utf8_string());
// 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_view());
list_format->set_style(style.as_string().utf8_string());
// 11. Let resolvedLocaleData be r.[[LocaleData]].
// 12. Let dataLocaleTypes be resolvedLocaleData.[[<type>]].

View file

@ -37,7 +37,7 @@ 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_view()))
if (validator && !validator(option.as_string().utf8_string()))
return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, option, property);
return option.as_string().utf8_string();

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_view());
number_format->set_notation(notation.as_string().utf8_string());
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_view());
number_format->set_compact_display(compact_display.as_string().utf8_string());
// 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_view());
number_format->set_sign_display(sign_display.as_string().utf8_string());
// 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_view();
auto rounding_priority = rounding_priority_option.as_string().utf8_string();
// 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_view());
intl_object.set_rounding_mode(rounding_mode.as_string().utf8_string());
// 16. Set intlObj.[[TrailingZeroDisplay]] to trailingZeroDisplay.
intl_object.set_trailing_zero_display(trailing_zero_display.as_string().utf8_string_view());
intl_object.set_trailing_zero_display(trailing_zero_display.as_string().utf8_string());
// 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_view());
intl_object.set_style(style.as_string().utf8_string());
// 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_view())) {
else if (!is_well_formed_currency_code(currency.as_string().utf8_string())) {
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_view())) {
else if (!is_well_formed_unit_identifier(unit.as_string().utf8_string())) {
return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, unit, "unit"sv);
}
@ -426,10 +426,10 @@ ThrowCompletionOr<void> set_number_format_unit_options(VM& vm, NumberFormat& int
intl_object.set_currency(MUST(currency.as_string().utf8_string().to_uppercase()));
// c. Set intlObj.[[CurrencyDisplay]] to currencyDisplay.
intl_object.set_currency_display(currency_display.as_string().utf8_string_view());
intl_object.set_currency_display(currency_display.as_string().utf8_string());
// d. Set intlObj.[[CurrencySign]] to currencySign.
intl_object.set_currency_sign(currency_sign.as_string().utf8_string_view());
intl_object.set_currency_sign(currency_sign.as_string().utf8_string());
}
// 13. If style is "unit", then
@ -438,7 +438,7 @@ ThrowCompletionOr<void> set_number_format_unit_options(VM& vm, NumberFormat& int
intl_object.set_unit(unit.as_string().utf8_string());
// b. Set intlObj.[[UnitDisplay]] to unitDisplay.
intl_object.set_unit_display(unit_display.as_string().utf8_string_view());
intl_object.set_unit_display(unit_display.as_string().utf8_string());
}
// 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_view());
plural_rules->set_type(type.as_string().utf8_string());
// 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_view());
plural_rules->set_notation(notation.as_string().utf8_string());
// 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_view());
plural_rules->set_compact_display(compact_display.as_string().utf8_string());
}
// 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_view());
relative_time_format->set_style(style.as_string().utf8_string());
// 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_view());
relative_time_format->set_numeric(numeric.as_string().utf8_string());
// 14. Let nfOptions be OrdinaryObjectCreate(null).
// 15. Perform ! CreateDataPropertyOrThrow(nfOptions, "numberingSystem", relativeTimeFormat.[[NumberingSystem]]).

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_view());
segmenter->set_segmenter_granularity(granularity.as_string().utf8_string());
auto locale_segmenter = Unicode::Segmenter::create(segmenter->locale(), segmenter->segmenter_granularity());
segmenter->set_segmenter(move(locale_segmenter));

View file

@ -488,7 +488,7 @@ static ThrowCompletionOr<ZipMode> get_zip_mode(VM& vm, Object const& options)
// 5. If mode is not one of "shortest", "longest", or "strict", throw a TypeError exception.
if (mode.is_string()) {
auto mode_string = mode.as_string().utf8_string_view();
auto mode_string = mode.as_string().utf16_string_view();
if (mode_string == "shortest"sv)
return ZipMode::Shortest;

View file

@ -187,12 +187,15 @@ JS_DEFINE_NATIVE_FUNCTION(ObjectPrototype::to_string)
// Optimization: Instead of creating another PrimitiveString from builtin_tag, we separate tag and to_string_tag and add an additional branch to step 16.
StringView tag;
String custom_tag;
// 16. If Type(tag) is not String, set tag to builtinTag.
if (!to_string_tag.is_string())
tag = builtin_tag;
else
tag = to_string_tag.as_string().utf8_string_view();
else {
custom_tag = to_string_tag.as_string().utf8_string();
tag = custom_tag;
}
// 17. Return the string-concatenation of "[object ", tag, and "]".

View file

@ -9,11 +9,9 @@
#include <AK/CharacterTypes.h>
#include <AK/FlyString.h>
#include <AK/StringBuilder.h>
#include <AK/UnicodeUtils.h>
#include <AK/Utf16FlyString.h>
#include <AK/Utf16StringBuilder.h>
#include <AK/Utf16View.h>
#include <AK/Utf8View.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/ExternalMemory.h>
#include <LibJS/Runtime/GlobalObject.h>
@ -37,32 +35,12 @@ Optional<StringView> PrimitiveString::short_flat_string_storage_view() const
if (m_deferred_kind != DeferredKind::None)
return {};
if (m_utf8_string.has_value() && m_utf8_string->is_short_string())
return m_utf8_string->bytes_as_string_view();
if (m_utf16_string.has_value() && m_utf16_string->has_short_ascii_storage())
return m_utf16_string->ascii_view();
return {};
}
static bool utf8_views_form_surrogate_pair_across_boundary(StringView lhs, StringView rhs)
{
if (lhs.length() < 3 || rhs.length() < 3)
return false;
// Surrogates encoded as UTF-8 are 3 bytes.
if ((static_cast<u8>(lhs[lhs.length() - 3]) & 0xf0) != 0xe0)
return false;
if ((static_cast<u8>(rhs[0]) & 0xf0) != 0xe0)
return false;
auto high_surrogate = *Utf8View(lhs.substring_view(lhs.length() - 3)).begin();
auto low_surrogate = *Utf8View(rhs).begin();
return AK::UnicodeUtils::is_utf16_high_surrogate(high_surrogate)
&& AK::UnicodeUtils::is_utf16_low_surrogate(low_surrogate);
}
GC::Ptr<PrimitiveString> PrimitiveString::try_create_short_flat_concatenated_string(VM& vm, PrimitiveString const& lhs, PrimitiveString const& rhs)
{
auto lhs_view = lhs.short_flat_string_storage_view();
@ -77,9 +55,6 @@ GC::Ptr<PrimitiveString> PrimitiveString::try_create_short_flat_concatenated_str
if (byte_count > String::MAX_SHORT_STRING_BYTE_COUNT)
return nullptr;
if (utf8_views_form_surrogate_pair_across_boundary(*lhs_view, *rhs_view))
return nullptr;
AK::Array<u8, String::MAX_SHORT_STRING_BYTE_COUNT> buffer;
lhs_view->bytes().copy_to({ buffer.data(), lhs_view->length() });
rhs_view->bytes().copy_to({ buffer.data() + lhs_view->length(), rhs_view->length() });
@ -125,29 +100,7 @@ GC::Ref<PrimitiveString> PrimitiveString::create(VM& vm, Utf16FlyString const& s
GC::Ref<PrimitiveString> PrimitiveString::create(VM& vm, String const& string)
{
if (string.is_empty())
return vm.empty_string();
auto const length_in_code_units = string.length_in_code_units();
if (length_in_code_units == 1) {
auto bytes = string.bytes();
if (auto ch = bytes[0]; is_ascii(ch))
return vm.single_ascii_character_string(ch);
}
if (string.length_in_code_units() > MAX_LENGTH_FOR_STRING_CACHE) {
return vm.heap().allocate<PrimitiveString>(string);
}
auto& string_cache = vm.string_cache();
if (auto it = string_cache.find(string); it != string_cache.end())
return *it->value;
auto new_string = vm.heap().allocate<PrimitiveString>(string);
new_string->m_utf8_string_is_in_cache = true;
string_cache.set(move(string), new_string);
return *new_string;
return create(vm, Utf16String::from_utf8(string.bytes_as_string_view()));
}
GC::Ref<PrimitiveString> PrimitiveString::create(VM& vm, StringView string)
@ -224,18 +177,11 @@ PrimitiveString::PrimitiveString(Utf16String string)
{
}
PrimitiveString::PrimitiveString(String string)
: m_utf8_string(move(string))
{
}
PrimitiveString::~PrimitiveString() = default;
size_t PrimitiveString::external_memory_size() const
{
size_t size = 0;
if (m_utf8_string.has_value())
size += string_external_memory_size(*m_utf8_string);
if (m_utf16_string.has_value())
size = saturating_add_external_memory_size(size, utf16_string_external_memory_size(*m_utf16_string));
return size;
@ -249,11 +195,6 @@ void PrimitiveString::finalize()
if (string.length_in_code_units() <= MAX_LENGTH_FOR_STRING_CACHE)
vm().utf16_string_cache().remove(string);
}
if (m_utf8_string_is_in_cache) {
auto const& string = *m_utf8_string;
if (string.length_in_code_units() <= MAX_LENGTH_FOR_STRING_CACHE)
vm().string_cache().remove(string);
}
}
bool PrimitiveString::is_empty() const
@ -267,44 +208,19 @@ bool PrimitiveString::is_empty() const
if (has_utf16_string())
return m_utf16_string->is_empty();
if (has_utf8_string())
return m_utf8_string->is_empty();
VERIFY_NOT_REACHED();
}
String PrimitiveString::utf8_string() const
{
resolve_if_needed(EncodingPreference::UTF8);
if (!has_utf8_string()) {
VERIFY(has_utf16_string());
m_utf8_string = m_utf16_string->to_utf8();
}
return *m_utf8_string;
}
StringView PrimitiveString::utf8_string_view() const
{
if (!has_utf8_string()) {
if (has_utf16_string() && m_utf16_string->has_ascii_storage())
return m_utf16_string->ascii_view();
(void)utf8_string();
}
return m_utf8_string->bytes_as_string_view();
return utf16_string_view().to_utf8_but_should_be_ported_to_utf16();
}
Utf16String PrimitiveString::utf16_string() const
{
resolve_if_needed(EncodingPreference::UTF16);
if (!has_utf16_string()) {
VERIFY(has_utf8_string());
m_utf16_string = Utf16String::from_utf8(*m_utf8_string);
}
resolve_if_needed();
VERIFY(has_utf16_string());
return *m_utf16_string;
}
@ -333,8 +249,6 @@ bool PrimitiveString::operator==(PrimitiveString const& other) const
return true;
if (length_in_utf16_code_units() != other.length_in_utf16_code_units())
return false;
if (m_utf8_string.has_value() && other.m_utf8_string.has_value())
return m_utf8_string->bytes_as_string_view() == other.m_utf8_string->bytes_as_string_view();
if (m_utf16_string.has_value() && other.m_utf16_string.has_value())
return *m_utf16_string == *other.m_utf16_string;
return utf16_string_view() == other.utf16_string_view();
@ -362,29 +276,28 @@ ThrowCompletionOr<Optional<Value>> PrimitiveString::get(VM& vm, PropertyKey cons
return create(vm, *this, index.as_index(), 1);
}
void PrimitiveString::resolve_if_needed(EncodingPreference preference) const
void PrimitiveString::resolve_if_needed() const
{
switch (m_deferred_kind) {
case DeferredKind::None:
return;
case DeferredKind::Rope:
static_cast<RopeString const&>(*this).resolve(preference);
static_cast<RopeString const&>(*this).resolve();
return;
case DeferredKind::Substring:
static_cast<Substring const&>(*this).resolve(preference);
static_cast<Substring const&>(*this).resolve();
return;
}
VERIFY_NOT_REACHED();
}
void RopeString::resolve(EncodingPreference preference) const
void RopeString::resolve() const
{
// This vector will hold all the pieces of the rope that need to be assembled
// into the resolved string.
Vector<PrimitiveString const*, 2> pieces;
size_t approximate_length = 0;
size_t length_in_utf16_code_units = 0;
// NOTE: We traverse the rope tree without using recursion, since we'd run out of
@ -401,91 +314,16 @@ void RopeString::resolve(EncodingPreference preference) const
continue;
}
if (current->has_utf8_string())
approximate_length += current->utf8_string_view().length();
if (preference == EncodingPreference::UTF16)
length_in_utf16_code_units += current->length_in_utf16_code_units();
length_in_utf16_code_units += current->length_in_utf16_code_units();
pieces.append(current);
}
if (preference == EncodingPreference::UTF16) {
// The caller wants a UTF-16 string, so we can simply concatenate all the pieces
// into a UTF-16 code unit buffer and create a Utf16String from it.
Utf16StringBuilder builder(length_in_utf16_code_units);
for (auto const* current : pieces)
builder.append(current->utf16_string_view());
m_utf16_string = builder.to_string();
m_deferred_kind = DeferredKind::None;
m_lhs = nullptr;
m_rhs = nullptr;
return;
}
// Now that we have all the pieces, we can concatenate them using a StringBuilder.
StringBuilder builder(approximate_length);
// We keep track of the previous piece in order to handle surrogate pairs spread across two pieces.
PrimitiveString const* previous = nullptr;
Utf16StringBuilder builder(length_in_utf16_code_units);
for (auto const* current : pieces) {
if (!previous) {
// This is the very first piece, just append it and continue.
builder.append(current->utf8_string());
previous = current;
continue;
}
// Get the UTF-8 representations for both strings.
auto current_string_as_utf8 = current->utf8_string_view();
auto previous_string_as_utf8 = previous->utf8_string_view();
// NOTE: Now we need to look at the end of the previous string and the start
// of the current string, to see if they should be combined into a surrogate.
// Surrogates encoded as UTF-8 are 3 bytes.
if ((previous_string_as_utf8.length() < 3) || (current_string_as_utf8.length() < 3)) {
builder.append(current_string_as_utf8);
previous = current;
continue;
}
// Might the previous string end with a UTF-8 encoded surrogate?
if ((static_cast<u8>(previous_string_as_utf8[previous_string_as_utf8.length() - 3]) & 0xf0) != 0xe0) {
// If not, just append the current string and continue.
builder.append(current_string_as_utf8);
previous = current;
continue;
}
// Might the current string begin with a UTF-8 encoded surrogate?
if ((static_cast<u8>(current_string_as_utf8[0]) & 0xf0) != 0xe0) {
// If not, just append the current string and continue.
builder.append(current_string_as_utf8);
previous = current;
continue;
}
auto high_surrogate = *Utf8View(previous_string_as_utf8.substring_view(previous_string_as_utf8.length() - 3)).begin();
auto low_surrogate = *Utf8View(current_string_as_utf8).begin();
if (!AK::UnicodeUtils::is_utf16_high_surrogate(high_surrogate) || !AK::UnicodeUtils::is_utf16_low_surrogate(low_surrogate)) {
builder.append(current_string_as_utf8);
previous = current;
continue;
}
// Remove 3 bytes from the builder and replace them with the UTF-8 encoded code point.
builder.trim(3);
builder.append_code_point(AK::UnicodeUtils::decode_utf16_surrogate_pair(high_surrogate, low_surrogate));
// Append the remaining part of the current string.
builder.append(current_string_as_utf8.substring_view(3));
previous = current;
builder.append(current->utf16_string_view());
}
// NOTE: We've already produced valid UTF-8 above, so there's no need for additional validation.
m_utf8_string = builder.to_string_without_validation();
m_utf16_string = builder.to_string();
m_deferred_kind = DeferredKind::None;
m_lhs = nullptr;
m_rhs = nullptr;
@ -507,17 +345,11 @@ void RopeString::visit_edges(Cell::Visitor& visitor)
visitor.visit(m_rhs);
}
void Substring::resolve(EncodingPreference preference) const
void Substring::resolve() const
{
auto source_view = m_source_string->utf16_string_view().substring_view(m_code_unit_offset, m_code_unit_length);
if (preference == EncodingPreference::UTF16) {
m_utf16_string = Utf16String::from_utf16(source_view);
} else {
auto substring = Utf16String::from_utf16(source_view);
m_utf8_string = substring.to_utf8();
}
m_utf16_string = Utf16String::from_utf16(source_view);
m_deferred_kind = DeferredKind::None;
m_source_string = nullptr;
}

View file

@ -48,8 +48,6 @@ public:
bool is_empty() const;
[[nodiscard]] String utf8_string() const;
[[nodiscard]] StringView utf8_string_view() const;
bool has_utf8_string() const { return m_utf8_string.has_value(); }
[[nodiscard]] Utf16String utf16_string() const;
[[nodiscard]] Utf16View utf16_string_view() const;
@ -75,17 +73,10 @@ protected:
mutable DeferredKind m_deferred_kind { DeferredKind::None };
mutable Optional<String> m_utf8_string;
mutable Optional<Utf16String> m_utf16_string;
bool m_utf8_string_is_in_cache { false };
bool m_utf16_string_is_in_cache { false };
enum class EncodingPreference {
UTF8,
UTF16,
};
private:
friend class RopeString;
friend class Substring;
@ -94,9 +85,8 @@ private:
virtual size_t external_memory_size() const override;
explicit PrimitiveString(Utf16String);
explicit PrimitiveString(String);
void resolve_if_needed(EncodingPreference) const;
void resolve_if_needed() const;
Optional<StringView> short_flat_string_storage_view() const;
static GC::Ptr<PrimitiveString> try_create_short_flat_concatenated_string(VM&, PrimitiveString const& lhs, PrimitiveString const& rhs);
};
@ -115,7 +105,7 @@ private:
virtual void visit_edges(Visitor&) override;
void resolve(EncodingPreference) const;
void resolve() const;
mutable GC::Ptr<PrimitiveString> m_lhs;
mutable GC::Ptr<PrimitiveString> m_rhs;
@ -135,7 +125,7 @@ private:
virtual void visit_edges(Visitor&) override;
void resolve(EncodingPreference) const;
void resolve() const;
mutable GC::Ptr<PrimitiveString> m_source_string;
size_t m_code_unit_offset { 0 };

View file

@ -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_view();
auto string_view = string_value.as_string().utf8_string();
// 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_view();
auto string_view = string_value.as_string().utf8_string();
// 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_view();
auto string_view = string_value.as_string().utf8_string();
// 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_view();
auto string_view = string_value.as_string().utf8_string();
// 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_view();
auto string_view = string_value.as_string().utf8_string();
// 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_view();
auto string_view = string_value.as_string().utf8_string();
// 2. If stringValue is "next", return NEXT.
if (string_view == "next"sv)
@ -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_view();
auto value_string = value.as_string().utf8_string();
// 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_view(), { { Production::TemporalZonedDateTimeString, Production::TemporalDateTimeString } }));
auto result = TRY(parse_iso_date_time(vm, value.as_string().utf8_string(), { { Production::TemporalZonedDateTimeString, Production::TemporalDateTimeString } }));
// c. Let offsetString be result.[[TimeZone]].[[OffsetString]].
offset_string = move(result.time_zone.offset_string);
@ -1620,7 +1620,7 @@ 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_view()));
TRY(parse_date_time_utc_offset(vm, offset.as_string().utf8_string()));
// 4. Return offset.
return offset.as_string().utf8_string();

View file

@ -318,7 +318,7 @@ 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_view());
return parse_month_code(vm, month_code.as_string().utf8_string());
}
// 12.2.1 ParseMonthCode ( argument ), https://tc39.es/proposal-temporal/#sec-temporal-parsemonthcode

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_view()));
return TRY(parse_temporal_duration_string(vm, item.as_string().utf8_string()));
}
// 3. Let result be a new Partial Duration Record with each field set to 0.

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_view(), { { Production::TemporalInstantString } }));
auto parsed = TRY(parse_iso_date_time(vm, item.as_string().utf8_string(), { { 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_view(), { { Production::TemporalDateTimeString } }));
auto result = TRY(parse_iso_date_time(vm, item.as_string().utf8_string(), { { 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_view()));
auto calendar = TRY(canonicalize_calendar(vm, calendar_value.as_string().utf8_string()));
// 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_view(), { { Production::TemporalDateTimeString } }));
auto result = TRY(parse_iso_date_time(vm, item.as_string().utf8_string(), { { 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_view()));
auto calendar = TRY(canonicalize_calendar(vm, calendar_value.as_string().utf8_string()));
// 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_view(), { { Production::TemporalMonthDayString } }));
auto parse_result = TRY(parse_iso_date_time(vm, item.as_string().utf8_string(), { { 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_view()));
auto calendar = TRY(canonicalize_calendar(vm, calendar_value.as_string().utf8_string()));
// 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_view(), { { Production::TemporalTimeString } }));
auto parse_result = TRY(parse_iso_date_time(vm, item.as_string().utf8_string(), { { 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_view(), { { Production::TemporalYearMonthString } }));
auto parse_result = TRY(parse_iso_date_time(vm, item.as_string().utf8_string(), { { 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_view()));
auto calendar = TRY(canonicalize_calendar(vm, calendar_value.as_string().utf8_string()));
// 8. Let ref be ? ToIntegerWithTruncation(referenceISODay).
auto reference = TRY(to_integer_with_truncation(vm, reference_iso_day, ErrorType::TemporalInvalidPlainYearMonth));

View file

@ -216,7 +216,7 @@ 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_view());
return to_temporal_time_zone_identifier(vm, temporal_time_zone_like.as_string().utf8_string());
}
// 11.1.8 ToTemporalTimeZoneIdentifier ( temporalTimeZoneLike ), https://tc39.es/proposal-temporal/#sec-temporal-totemporaltimezoneidentifier

View file

@ -213,7 +213,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_view(), { { Production::TemporalZonedDateTimeString } }));
auto result = TRY(parse_iso_date_time(vm, item.as_string().utf8_string(), { { Production::TemporalZonedDateTimeString } }));
// c. Let annotation be result.[[TimeZone]].[[TimeZoneAnnotation]].
auto annotation = move(result.time_zone.time_zone_annotation);

View file

@ -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_view()));
auto calendar = TRY(canonicalize_calendar(vm, calendar_value.as_string().utf8_string()));
// 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

@ -54,9 +54,9 @@ static ThrowCompletionOr<Alphabet> parse_alphabet(VM& vm, Object& options)
// If alphabet is neither "base64" nor "base64url", throw a TypeError exception.
if (alphabet.is_string()) {
if (alphabet.as_string().utf8_string_view() == "base64"sv)
if (alphabet.as_string().utf16_string_view() == "base64"sv)
return Alphabet::Base64;
if (alphabet.as_string().utf8_string_view() == "base64url"sv)
if (alphabet.as_string().utf16_string_view() == "base64url"sv)
return Alphabet::Base64URL;
}
@ -74,11 +74,11 @@ static ThrowCompletionOr<AK::LastChunkHandling> parse_last_chunk_handling(VM& vm
// If lastChunkHandling is not one of "loose", "strict", or "stop-before-partial", throw a TypeError exception.
if (last_chunk_handling.is_string()) {
if (last_chunk_handling.as_string().utf8_string_view() == "loose"sv)
if (last_chunk_handling.as_string().utf16_string_view() == "loose"sv)
return AK::LastChunkHandling::Loose;
if (last_chunk_handling.as_string().utf8_string_view() == "strict"sv)
if (last_chunk_handling.as_string().utf16_string_view() == "strict"sv)
return AK::LastChunkHandling::Strict;
if (last_chunk_handling.as_string().utf8_string_view() == "stop-before-partial"sv)
if (last_chunk_handling.as_string().utf16_string_view() == "stop-before-partial"sv)
return AK::LastChunkHandling::StopBeforePartial;
}
@ -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_view(), alphabet, last_chunk_handling);
auto result = JS::from_base64(vm, string_value.as_string().utf8_string(), 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_view());
auto result = JS::from_hex(vm, string_value.as_string().utf8_string());
// 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_view(), alphabet, last_chunk_handling, byte_length);
auto result = JS::from_base64(vm, string_value.as_string().utf8_string(), 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_view(), byte_length);
auto result = JS::from_hex(vm, string_value.as_string().utf8_string(), byte_length);
// 8. Let bytes be result.[[Bytes]].
auto bytes = move(result.bytes);

View file

@ -84,18 +84,18 @@ VM::VM(ErrorMessages error_messages)
Bytecode::StaticPropertyLookupCache::sweep_all();
});
m_empty_string = m_heap.allocate<PrimitiveString>(String {});
m_empty_string = m_heap.allocate<PrimitiveString>(Utf16String {});
cached_strings = {
.number = m_heap.allocate<PrimitiveString>("number"_string),
.undefined = m_heap.allocate<PrimitiveString>("undefined"_string),
.object = m_heap.allocate<PrimitiveString>("object"_string),
.string = m_heap.allocate<PrimitiveString>("string"_string),
.symbol = m_heap.allocate<PrimitiveString>("symbol"_string),
.boolean = m_heap.allocate<PrimitiveString>("boolean"_string),
.bigint = m_heap.allocate<PrimitiveString>("bigint"_string),
.function = m_heap.allocate<PrimitiveString>("function"_string),
.object_Object = m_heap.allocate<PrimitiveString>("[object Object]"_string),
.number = m_heap.allocate<PrimitiveString>("number"_utf16),
.undefined = m_heap.allocate<PrimitiveString>("undefined"_utf16),
.object = m_heap.allocate<PrimitiveString>("object"_utf16),
.string = m_heap.allocate<PrimitiveString>("string"_utf16),
.symbol = m_heap.allocate<PrimitiveString>("symbol"_utf16),
.boolean = m_heap.allocate<PrimitiveString>("boolean"_utf16),
.bigint = m_heap.allocate<PrimitiveString>("bigint"_utf16),
.function = m_heap.allocate<PrimitiveString>("function"_utf16),
.object_Object = m_heap.allocate<PrimitiveString>("[object Object]"_utf16),
};
for (size_t i = 0; i < single_ascii_character_strings.size(); ++i)

View file

@ -163,11 +163,6 @@ public:
JS_ENUMERATE_WELL_KNOWN_SYMBOLS
#undef __JS_ENUMERATE
HashMap<String, GC::Ptr<PrimitiveString>>& string_cache()
{
return m_string_cache;
}
HashMap<Utf16String, GC::Ptr<PrimitiveString>>& utf16_string_cache()
{
return m_utf16_string_cache;
@ -516,7 +511,6 @@ private:
static VM* s_the;
HashMap<String, GC::Ptr<PrimitiveString>> m_string_cache;
HashMap<Utf16String, GC::Ptr<PrimitiveString>> m_utf16_string_cache;
static constexpr size_t numeric_string_cache_size = 1000;

View file

@ -852,7 +852,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_view());
return string_to_number(as_string().utf8_string());
// 7. Assert: argument is an Object.
case OBJECT_TAG: {
// 8. Let primValue be ? ToPrimitive(argument, number).
@ -906,7 +906,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_view());
auto bigint = string_to_bigint(vm, primitive.as_string().utf8_string());
// 2. If n is undefined, throw a SyntaxError exception.
if (!bigint.has_value())
@ -2431,7 +2431,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_view());
auto bigint = string_to_bigint(vm, rhs.as_string().utf8_string());
// b. If n is undefined, return false.
if (!bigint.has_value())
@ -2530,7 +2530,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_view());
auto y_bigint = string_to_bigint(vm, y_primitive.as_string().utf8_string());
// ii. If ny is undefined, return undefined.
if (!y_bigint.has_value())
@ -2545,7 +2545,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_view());
auto x_bigint = string_to_bigint(vm, x_primitive.as_string().utf8_string());
// ii. If nx is undefined, return undefined.
if (!x_bigint.has_value())

View file

@ -169,7 +169,7 @@ static void bytecode_dump_append_value_string(void* ctx, uint64_t encoded)
{
auto& builder = *static_cast<JS::RustIntegration::BytecodeDumpBuilder*>(ctx);
auto value = bit_cast<Value>(encoded);
builder.output.append(value.as_string().utf8_string_view());
builder.output.append(value.as_string().utf16_string_view());
}
static void bytecode_dump_append_value_bigint(void* ctx, uint64_t encoded)

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_view();
auto string = value.as_string().utf8_string();
// 1. Let parsedURL be the result of basic URL parsing value.
auto parsed_url = URL::Parser::basic_parse(string);

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_view(), base_url);
auto address_url = resolve_url_like_module_specifier(value.as_string().utf8_string(), base_url);
// 5. If addressURL is null, then:
if (!address_url.has_value()) {

View file

@ -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_view());
type = error_name_to_type(name.as_string().utf8_string());
// 3. Let valueMessageDesc be ? value.[[GetOwnProperty]]("message").
auto value_message_descriptor = TRY(object->internal_get_own_property(m_vm.names.message));

View file

@ -175,7 +175,7 @@ void set_up_cross_realm_transform_readable(JS::Realm& realm, ReadableStream& str
auto value = MUST(data.get(vm, vm.names.value));
// 5. Assert: type is a String.
auto type_string = type.as_string().utf8_string_view();
auto type_string = type.as_string().utf16_string_view();
// 6. If type is "chunk",
if (type_string == "chunk"sv) {
@ -286,7 +286,7 @@ void set_up_cross_realm_transform_writable(JS::Realm& realm, WritableStream& str
auto value = MUST(data.get(vm, vm.names.value));
// 5. Assert: type is a String.
auto type_string = type.as_string().utf8_string_view();
auto type_string = type.as_string().utf16_string_view();
// 6. If type is "pull",
if (type_string == "pull"sv) {

View file

@ -108,14 +108,14 @@ TESTJS_GLOBAL_FUNCTION(mark_as_garbage, markAsGarbage)
return execution_context->lexical_environment != nullptr;
});
if (!outer_environment.has_value())
return vm.throw_completion<JS::ReferenceError>(JS::ErrorType::UnknownIdentifier, variable_name.utf8_string_view());
return vm.throw_completion<JS::ReferenceError>(JS::ErrorType::UnknownIdentifier, variable_name.utf16_string_view());
auto reference = TRY(vm.resolve_binding(variable_name.utf16_string(), JS::Strict::No, outer_environment.value()->lexical_environment));
auto value = TRY(reference.get_value(vm));
if (!can_be_held_weakly(value))
return vm.throw_completion<JS::TypeError>(JS::ErrorType::CannotBeHeldWeakly, ByteString::formatted("Variable with name {}", variable_name.utf8_string_view()));
return vm.throw_completion<JS::TypeError>(JS::ErrorType::CannotBeHeldWeakly, ByteString::formatted("Variable with name {}", variable_name.utf16_string_view()));
TRY(reference.put_value(vm, JS::js_undefined()));
(void)TRY(reference.delete_(vm));

View file

@ -37,14 +37,14 @@ NEVER_INLINE static void materialize_temporary_rope(VM& vm)
auto rope = PrimitiveString::create(vm,
*PrimitiveString::create(vm, "hello"_string),
*PrimitiveString::create(vm, "world"_string));
EXPECT(rope->utf8_string_view() == "helloworld"sv);
EXPECT(rope->utf16_string_view() == "helloworld"sv);
}
NEVER_INLINE static void materialize_temporary_substring(VM& vm)
{
auto source = PrimitiveString::create(vm, "foobar"_string);
auto substring = PrimitiveString::create(vm, *source, 0, 3);
EXPECT(substring->utf8_string_view() == "foo"sv);
EXPECT(substring->utf16_string_view() == "foo"sv);
}
NEVER_INLINE static void clobber_stack()
@ -65,9 +65,9 @@ TEST_CASE(primitive_string_substring_supports_nested_ranges)
auto nested_substring = PrimitiveString::create(*test_vm.vm, *substring, 1, 2);
EXPECT_EQ(substring->length_in_utf16_code_units(), 4u);
EXPECT(substring->utf8_string_view() == "bcde"sv);
EXPECT(substring->utf16_string_view() == "bcde"sv);
EXPECT_EQ(nested_substring->length_in_utf16_code_units(), 2u);
EXPECT(nested_substring->utf8_string_view() == "cd"sv);
EXPECT(nested_substring->utf16_string_view() == "cd"sv);
}
TEST_CASE(primitive_string_substring_materializes_rope_ranges)
@ -80,7 +80,7 @@ TEST_CASE(primitive_string_substring_materializes_rope_ranges)
auto substring = PrimitiveString::create(*test_vm.vm, *rope, 3, 3);
EXPECT_EQ(substring->length_in_utf16_code_units(), 3u);
EXPECT(substring->utf8_string_view() == "def"sv);
EXPECT(substring->utf16_string_view() == "def"sv);
}
TEST_CASE(primitive_string_concat_short_flat_strings_creates_flat_string)
@ -91,8 +91,8 @@ TEST_CASE(primitive_string_concat_short_flat_strings_creates_flat_string)
*PrimitiveString::create(*test_vm.vm, "foo"_string),
*PrimitiveString::create(*test_vm.vm, "bar"_string));
EXPECT(concatenated->has_utf8_string());
EXPECT(concatenated->utf8_string_view() == "foobar"sv);
EXPECT(concatenated->has_utf16_string());
EXPECT(concatenated->utf16_string_view() == "foobar"sv);
}
TEST_CASE(primitive_string_concat_longer_strings_stays_deferred)
@ -103,9 +103,8 @@ TEST_CASE(primitive_string_concat_longer_strings_stays_deferred)
*PrimitiveString::create(*test_vm.vm, "abcd"_string),
*PrimitiveString::create(*test_vm.vm, "efgh"_string));
EXPECT(!concatenated->has_utf8_string());
EXPECT(!concatenated->has_utf16_string());
EXPECT(concatenated->utf8_string_view() == "abcdefgh"sv);
EXPECT(concatenated->utf16_string_view() == "abcdefgh"sv);
}
TEST_CASE(primitive_string_substring_reuses_cached_single_ascii_strings)
@ -132,7 +131,7 @@ TEST_CASE(primitive_string_substring_handles_surrogate_boundaries)
EXPECT_EQ(leading_surrogate->utf16_string_view().code_unit_at(0), static_cast<u16>(0xd83d));
EXPECT_EQ(trailing_surrogate->length_in_utf16_code_units(), 1u);
EXPECT_EQ(trailing_surrogate->utf16_string_view().code_unit_at(0), static_cast<u16>(0xde00));
EXPECT(full_code_point->utf8_string_view() == "😀"sv);
EXPECT(full_code_point->utf16_string_view() == "😀"sv);
}
TEST_CASE(primitive_string_concat_short_strings_handles_surrogate_boundaries)
@ -143,11 +142,11 @@ TEST_CASE(primitive_string_concat_short_strings_handles_surrogate_boundaries)
auto leading_surrogate = PrimitiveString::create(*test_vm.vm, *string, 0, 1);
auto trailing_surrogate = PrimitiveString::create(*test_vm.vm, *string, 1, 1);
(void)leading_surrogate->utf8_string_view();
(void)trailing_surrogate->utf8_string_view();
(void)leading_surrogate->utf16_string_view();
(void)trailing_surrogate->utf16_string_view();
auto concatenated = PrimitiveString::create(*test_vm.vm, *leading_surrogate, *trailing_surrogate);
EXPECT(concatenated->utf8_string_view() == "😀"sv);
EXPECT(concatenated->utf16_string_view() == "😀"sv);
}
TEST_CASE(primitive_string_substring_utf16_views_stay_deferred)