Libraries: Clean up UTF-16 source text paths

Store parser errors, source range filenames, source code filenames,
module source, and Rust parser errors as UTF-16 where they flow back
into JavaScript-visible strings. Keep byte-oriented source buffers
byte-backed.

Remove temporary PrimitiveString, ByteString, and UTF-8 detours from
JSON, RegExp, module debug logging, print formatting, and tests.
This commit is contained in:
Andreas Kling 2026-06-21 19:03:19 +02:00 committed by Andreas Kling
parent b6bef6b688
commit b81269e78b
157 changed files with 1165 additions and 1041 deletions

View file

@ -44,20 +44,25 @@ static Optional<InvalidBase64> base64_error_from_result(simdutf::result result,
output.resize((result.count / 4) * 3); output.resize((result.count / 4) * 3);
auto error = [&]() { struct DecodeError {
Base64DecodeError decode_error;
Error error;
};
auto [decode_error, error] = [&]() -> DecodeError {
switch (result.error) { switch (result.error) {
case simdutf::BASE64_EXTRA_BITS: case simdutf::BASE64_EXTRA_BITS:
return Error::from_string_literal("Extra bits found at end of chunk"); return { Base64DecodeError::ExtraBits, Error::from_string_literal("Extra bits found at end of chunk") };
case simdutf::BASE64_INPUT_REMAINDER: case simdutf::BASE64_INPUT_REMAINDER:
return Error::from_string_literal("Invalid trailing data"); return { Base64DecodeError::InputRemainder, Error::from_string_literal("Invalid trailing data") };
case simdutf::INVALID_BASE64_CHARACTER: case simdutf::INVALID_BASE64_CHARACTER:
return Error::from_string_literal("Invalid base64 character"); return { Base64DecodeError::InvalidCharacter, Error::from_string_literal("Invalid base64 character") };
default: default:
return Error::from_string_literal("Invalid base64-encoded data"); return { Base64DecodeError::InvalidData, Error::from_string_literal("Invalid base64-encoded data") };
} }
}(); }();
return InvalidBase64 { .error = move(error), .valid_input_bytes = result.count }; return InvalidBase64 { .decode_error = decode_error, .error = move(error), .valid_input_bytes = result.count };
} }
static ErrorOr<size_t, InvalidBase64> decode_base64_into_impl(StringView input, ByteBuffer& output, LastChunkHandling last_chunk_handling, simdutf::base64_options options) static ErrorOr<size_t, InvalidBase64> decode_base64_into_impl(StringView input, ByteBuffer& output, LastChunkHandling last_chunk_handling, simdutf::base64_options options)

View file

@ -24,10 +24,18 @@ enum class LastChunkHandling {
StopBeforePartial, StopBeforePartial,
}; };
enum class Base64DecodeError {
ExtraBits,
InputRemainder,
InvalidCharacter,
InvalidData,
};
ErrorOr<ByteBuffer> decode_base64(StringView, LastChunkHandling = LastChunkHandling::Loose); ErrorOr<ByteBuffer> decode_base64(StringView, LastChunkHandling = LastChunkHandling::Loose);
ErrorOr<ByteBuffer> decode_base64url(StringView, LastChunkHandling = LastChunkHandling::Loose); ErrorOr<ByteBuffer> decode_base64url(StringView, LastChunkHandling = LastChunkHandling::Loose);
struct InvalidBase64 { struct InvalidBase64 {
Base64DecodeError decode_error { Base64DecodeError::InvalidData };
Error error; Error error;
size_t valid_input_bytes { 0 }; size_t valid_input_bytes { 0 };
}; };

View file

@ -144,11 +144,11 @@ ByteString LexicalPath::canonicalized_path(ByteString path)
return builder.to_byte_string(); return builder.to_byte_string();
} }
ByteString LexicalPath::absolute_path(ByteString dir_path, ByteString target) ByteString LexicalPath::absolute_path(StringView dir_path, StringView target)
{ {
if (LexicalPath(target).is_absolute()) { if (is_absolute_path(target))
return LexicalPath::canonicalized_path(target); return LexicalPath::canonicalized_path(target);
}
return LexicalPath::canonicalized_path(join(dir_path, target).string()); return LexicalPath::canonicalized_path(join(dir_path, target).string());
} }

View file

@ -48,7 +48,7 @@ public:
[[nodiscard]] LexicalPath parent() const; [[nodiscard]] LexicalPath parent() const;
[[nodiscard]] static ByteString canonicalized_path(ByteString); [[nodiscard]] static ByteString canonicalized_path(ByteString);
[[nodiscard]] static ByteString absolute_path(ByteString dir_path, ByteString target); [[nodiscard]] static ByteString absolute_path(StringView dir_path, StringView target);
[[nodiscard]] static Optional<ByteString> relative_path(StringView absolute_path, StringView absolute_prefix); [[nodiscard]] static Optional<ByteString> relative_path(StringView absolute_path, StringView absolute_prefix);
template<typename... S> template<typename... S>

View file

@ -106,7 +106,7 @@ ByteString LexicalPath::canonicalized_path(ByteString path)
return path == "" ? "." : path; return path == "" ? "." : path;
} }
ByteString LexicalPath::absolute_path(ByteString dir_path, ByteString target) ByteString LexicalPath::absolute_path(StringView dir_path, StringView target)
{ {
if (is_absolute_path(target)) if (is_absolute_path(target))
return canonicalized_path(target); return canonicalized_path(target);

View file

@ -358,12 +358,16 @@ static void dump_header(StringBuilder& output, Executable const& executable)
// Show source location if available. // Show source location if available.
if (first_source_map_entry) { if (first_source_map_entry) {
auto filename = executable.source_code->filename(); auto filename = executable.source_code->filename().utf16_view();
if (!filename.is_empty()) { if (!filename.is_empty()) {
// Show just the basename to keep output portable across machines. // Show just the basename to keep output portable across machines.
auto last_slash = filename.bytes_as_string_view().find_last('/'); Optional<size_t> last_slash;
for (size_t i = 0; i < filename.length_in_code_units(); ++i) {
if (filename.code_unit_at(i) == '/')
last_slash = i;
}
if (last_slash.has_value()) if (last_slash.has_value())
filename = MUST(filename.substring_from_byte_offset(last_slash.value() + 1)); filename = filename.substring_view(last_slash.value() + 1);
output.appendff(" {}:{}:{}", filename, first_source_map_entry->line, first_source_map_entry->column); output.appendff(" {}:{}:{}", filename, first_source_map_entry->line, first_source_map_entry->column);
} else { } else {
output.appendff(" line {}, column {}", first_source_map_entry->line, first_source_map_entry->column); output.appendff(" line {}, column {}", first_source_map_entry->line, first_source_map_entry->column);

View file

@ -367,7 +367,7 @@ ThrowCompletionOr<Value> Console::trace()
if (element.source_range.has_value()) { if (element.source_range.has_value()) {
auto const& source_range = *element.source_range; auto const& source_range = *element.source_range;
if (!source_range.filename().is_empty()) { if (!source_range.filename().is_empty()) {
frame.source_file = MUST(String::from_byte_string(source_range.filename())); frame.source_file = source_range.filename();
frame.line = source_range.start.line; frame.line = source_range.start.line;
frame.column = source_range.start.column; frame.column = source_range.start.column;
} }

View file

@ -59,7 +59,7 @@ public:
struct TraceFrame { struct TraceFrame {
Utf16String function_name; Utf16String function_name;
Optional<String> source_file; Optional<Utf16String> source_file;
Optional<size_t> line; Optional<size_t> line;
Optional<size_t> column; Optional<size_t> column;
}; };

View file

@ -7,6 +7,7 @@
#include <AK/Debug.h> #include <AK/Debug.h>
#include <AK/QuickSort.h> #include <AK/QuickSort.h>
#include <AK/String.h>
#include <AK/TypeCasts.h> #include <AK/TypeCasts.h>
#include <LibJS/CyclicModule.h> #include <LibJS/CyclicModule.h>
#include <LibJS/Runtime/ExternalMemory.h> #include <LibJS/Runtime/ExternalMemory.h>
@ -265,7 +266,7 @@ ThrowCompletionOr<u32> CyclicModule::inner_module_linking(VM& vm, GC::RootVector
// b. Return index. // b. Return index.
// Note: Step 1, 1.a and 1.b are handled in Module.cpp // Note: Step 1, 1.a and 1.b are handled in Module.cpp
dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] inner_module_linking[{}](vm, {}, {})", this, ByteString::join(',', stack), index); dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] inner_module_linking[{}](vm, {}, {})", this, MUST(String::join(',', stack)), index);
// 2. If module.[[Status]] is linking, linked, evaluating-async, or evaluated, then // 2. If module.[[Status]] is linking, linked, evaluating-async, or evaluated, then
if (m_status == ModuleStatus::Linking || m_status == ModuleStatus::Linked || m_status == ModuleStatus::EvaluatingAsync || m_status == ModuleStatus::Evaluated) { if (m_status == ModuleStatus::Linking || m_status == ModuleStatus::Linked || m_status == ModuleStatus::EvaluatingAsync || m_status == ModuleStatus::Evaluated) {
@ -470,7 +471,7 @@ ThrowCompletionOr<GC::Ref<PromiseCapability>> CyclicModule::evaluate(VM& vm)
// 16.2.1.5.2.1 InnerModuleEvaluation ( module, stack, index ), https://tc39.es/ecma262/#sec-innermoduleevaluation // 16.2.1.5.2.1 InnerModuleEvaluation ( module, stack, index ), https://tc39.es/ecma262/#sec-innermoduleevaluation
ThrowCompletionOr<u32> CyclicModule::inner_module_evaluation(VM& vm, GC::RootVector<GC::Ref<Module>>& stack, u32 index) ThrowCompletionOr<u32> CyclicModule::inner_module_evaluation(VM& vm, GC::RootVector<GC::Ref<Module>>& stack, u32 index)
{ {
dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] inner_module_evaluation[{}](vm, {}, {})", this, ByteString::join(", "sv, stack), index); dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] inner_module_evaluation[{}](vm, {}, {})", this, MUST(String::join(", "sv, stack)), index);
// Note: Step 1 is performed in Module.cpp // Note: Step 1 is performed in Module.cpp
// 2. If module.[[Status]] is evaluating-async or evaluated, then // 2. If module.[[Status]] is evaluating-async or evaluated, then

View file

@ -5,8 +5,8 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/StringBuilder.h>
#include <AK/StringView.h> #include <AK/StringView.h>
#include <AK/Utf16StringBuilder.h>
#include <AK/Vector.h> #include <AK/Vector.h>
#include <LibJS/ParserError.h> #include <LibJS/ParserError.h>
#include <LibJS/Token.h> #include <LibJS/Token.h>
@ -20,14 +20,7 @@ Utf16String ParserError::to_utf16_string() const
return Utf16String::formatted("{} (line: {}, column: {})", message, position.value().line, position.value().column); return Utf16String::formatted("{} (line: {}, column: {})", message, position.value().line, position.value().column);
} }
ByteString ParserError::to_byte_string() const Utf16String ParserError::source_location_hint(Utf16View const& source, char spacer, char indicator) const
{
if (!position.has_value())
return message.to_byte_string();
return Utf16String::formatted("{} (line: {}, column: {})", message, position.value().line, position.value().column).to_byte_string();
}
ByteString ParserError::source_location_hint(Utf16View const& source, char spacer, char indicator) const
{ {
if (!position.has_value()) if (!position.has_value())
return {}; return {};
@ -36,13 +29,13 @@ ByteString ParserError::source_location_hint(Utf16View const& source, char space
// line terminators to \n is easier than splitting using all different LT characters. // line terminators to \n is easier than splitting using all different LT characters.
auto source_string = source.replace("\r\n"sv, "\n"sv, ReplaceMode::All).replace("\r"sv, "\n"sv, ReplaceMode::All).replace(LINE_SEPARATOR, "\n"sv, ReplaceMode::All).replace(PARAGRAPH_SEPARATOR, "\n"sv, ReplaceMode::All); auto source_string = source.replace("\r\n"sv, "\n"sv, ReplaceMode::All).replace("\r"sv, "\n"sv, ReplaceMode::All).replace(LINE_SEPARATOR, "\n"sv, ReplaceMode::All).replace(PARAGRAPH_SEPARATOR, "\n"sv, ReplaceMode::All);
StringBuilder builder; Utf16StringBuilder builder;
builder.append(source_string.split_view('\n', SplitBehavior::KeepEmpty)[position.value().line - 1]); builder.append(source_string.split_view('\n', SplitBehavior::KeepEmpty)[position.value().line - 1]);
builder.append('\n'); builder.append_ascii('\n');
for (size_t i = 0; i < position.value().column - 1; ++i) for (size_t i = 0; i < position.value().column - 1; ++i)
builder.append(spacer); builder.append_ascii(spacer);
builder.append(indicator); builder.append_ascii(indicator);
return builder.to_byte_string(); return builder.to_string();
} }
} }

View file

@ -7,7 +7,6 @@
#pragma once #pragma once
#include <AK/ByteString.h>
#include <AK/Error.h> #include <AK/Error.h>
#include <AK/Optional.h> #include <AK/Optional.h>
#include <AK/Utf16String.h> #include <AK/Utf16String.h>
@ -21,8 +20,7 @@ struct JS_API ParserError {
Optional<Position> position; Optional<Position> position;
Utf16String to_utf16_string() const; Utf16String to_utf16_string() const;
ByteString to_byte_string() const; Utf16String source_location_hint(Utf16View const& source, char spacer = ' ', char indicator = '^') const;
ByteString source_location_hint(Utf16View const& source, char spacer = ' ', char indicator = '^') const;
}; };
} }

View file

@ -8,6 +8,7 @@
#include <AK/Concepts.h> #include <AK/Concepts.h>
#include <AK/Stream.h> #include <AK/Stream.h>
#include <AK/String.h>
#include <AK/StringBuilder.h> #include <AK/StringBuilder.h>
#include <AK/Utf16StringBuilder.h> #include <AK/Utf16StringBuilder.h>
#include <LibJS/Print.h> #include <LibJS/Print.h>
@ -119,7 +120,7 @@ ErrorOr<void> print_value(JS::PrintContext& print_context, JS::ThrowCompletionOr
ErrorOr<String> strip_ansi(StringView format_string) ErrorOr<String> strip_ansi(StringView format_string)
{ {
if (format_string.is_empty()) if (format_string.is_empty())
return String(); return String {};
StringBuilder builder; StringBuilder builder;
size_t i; size_t i;

View file

@ -1285,7 +1285,7 @@ CanonicalIndex canonical_numeric_index_string(PropertyKey const& property_key, C
} }
// 22.1.3.19.1 GetSubstitution ( matched, str, position, captures, namedCaptures, replacementTemplate ), https://tc39.es/ecma262/#sec-getsubstitution // 22.1.3.19.1 GetSubstitution ( matched, str, position, captures, namedCaptures, replacementTemplate ), https://tc39.es/ecma262/#sec-getsubstitution
ThrowCompletionOr<Utf16String> get_substitution(VM& vm, Utf16View const& matched, Utf16View const& str, size_t position, Span<Value> captures, Value named_captures, Value replacement_template) ThrowCompletionOr<Utf16String> get_substitution(VM& vm, Utf16View const& matched, Utf16View const& str, size_t position, Span<Value> captures, Value named_captures, Utf16View const& replacement_template)
{ {
// 1. Let stringLength be the length of str. // 1. Let stringLength be the length of str.
auto string_length = str.length_in_code_units(); auto string_length = str.length_in_code_units();
@ -1297,8 +1297,7 @@ ThrowCompletionOr<Utf16String> get_substitution(VM& vm, Utf16View const& matched
Utf16StringBuilder result; Utf16StringBuilder result;
// 4. Let templateRemainder be replacementTemplate. // 4. Let templateRemainder be replacementTemplate.
auto replace_template_string = TRY(replacement_template.to_utf16_string(vm)); auto template_remainder = replacement_template;
Utf16View template_remainder { replace_template_string };
// 5. Repeat, while templateRemainder is not the empty String, // 5. Repeat, while templateRemainder is not the empty String,
while (!template_remainder.is_empty()) { while (!template_remainder.is_empty()) {
@ -1948,18 +1947,17 @@ ThrowCompletionOr<Value> get_option(VM& vm, Object const& options, PropertyKey c
VERIFY(type == OptionType::String); VERIFY(type == OptionType::String);
// b. Set value to ? ToString(value). // b. Set value to ? ToString(value).
value = TRY(value.to_primitive_string(vm)); auto value_string = TRY(value.to_utf16_string(vm));
}
// 5. If values is not EMPTY and values does not contain value, throw a RangeError exception. // 5. If values is not EMPTY and values does not contain value, throw a RangeError exception.
if (!values.is_empty()) { if (!values.is_empty()) {
// NOTE: Every location in the spec that invokes GetOption with type=boolean also has values=undefined. auto value_string_view = value_string.utf16_view();
VERIFY(value.is_string()); auto it = find_if(values.begin(), values.end(), [&](auto allowed_value) { return value_string_view == allowed_value; });
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()) if (it == values.end())
return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, value_string, property.as_string()); return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, value_string_view, property.as_string());
}
value = PrimitiveString::create(vm, value_string);
} }
// 6. Return value. // 6. Return value.

View file

@ -88,7 +88,7 @@ enum class CanonicalIndexMode {
IgnoreNumericRoundtrip, IgnoreNumericRoundtrip,
}; };
[[nodiscard]] CanonicalIndex canonical_numeric_index_string(PropertyKey const&, CanonicalIndexMode needs_numeric); [[nodiscard]] CanonicalIndex canonical_numeric_index_string(PropertyKey const&, CanonicalIndexMode needs_numeric);
ThrowCompletionOr<Utf16String> get_substitution(VM&, Utf16View const& matched, Utf16View const& str, size_t position, Span<Value> captures, Value named_captures, Value replacement); ThrowCompletionOr<Utf16String> get_substitution(VM&, Utf16View const& matched, Utf16View const& str, size_t position, Span<Value> captures, Value named_captures, Utf16View const& replacement);
enum class CallerMode { enum class CallerMode {
Strict, Strict,

View file

@ -6,7 +6,6 @@
#include "ConsoleObjectPrototype.h" #include "ConsoleObjectPrototype.h"
#include <AK/ByteString.h>
#include <AK/Function.h> #include <AK/Function.h>
#include <LibJS/Runtime/AbstractOperations.h> #include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Accessor.h> #include <LibJS/Runtime/Accessor.h>

View file

@ -399,7 +399,7 @@ i64 clip_double_to_sane_time(double value)
// 21.4.1.20 GetNamedTimeZoneEpochNanoseconds ( timeZoneIdentifier, year, month, day, hour, minute, second, millisecond, microsecond, nanosecond ), https://tc39.es/ecma262/#sec-getnamedtimezoneepochnanoseconds // 21.4.1.20 GetNamedTimeZoneEpochNanoseconds ( timeZoneIdentifier, year, month, day, hour, minute, second, millisecond, microsecond, nanosecond ), https://tc39.es/ecma262/#sec-getnamedtimezoneepochnanoseconds
// 14.6.3 GetNamedTimeZoneEpochNanoseconds ( timeZoneIdentifier, isoDateTime ), https://tc39.es/proposal-temporal/#sec-getnamedtimezoneepochnanoseconds // 14.6.3 GetNamedTimeZoneEpochNanoseconds ( timeZoneIdentifier, isoDateTime ), https://tc39.es/proposal-temporal/#sec-getnamedtimezoneepochnanoseconds
Vector<Crypto::SignedBigInteger> get_named_time_zone_epoch_nanoseconds(StringView time_zone_identifier, Temporal::ISODateTime const& iso_date_time) Vector<Crypto::SignedBigInteger> get_named_time_zone_epoch_nanoseconds(Utf16View time_zone_identifier, Temporal::ISODateTime const& iso_date_time)
{ {
auto local_nanoseconds = get_utc_epoch_nanoseconds(iso_date_time); auto local_nanoseconds = get_utc_epoch_nanoseconds(iso_date_time);
auto local_time = UnixDateTime::from_nanoseconds_since_epoch(clip_bigint_to_sane_time(local_nanoseconds)); auto local_time = UnixDateTime::from_nanoseconds_since_epoch(clip_bigint_to_sane_time(local_nanoseconds));
@ -416,7 +416,7 @@ Vector<Crypto::SignedBigInteger> get_named_time_zone_epoch_nanoseconds(StringVie
} }
// 21.4.1.21 GetNamedTimeZoneOffsetNanoseconds ( timeZoneIdentifier, epochNanoseconds ), https://tc39.es/ecma262/#sec-getnamedtimezoneoffsetnanoseconds // 21.4.1.21 GetNamedTimeZoneOffsetNanoseconds ( timeZoneIdentifier, epochNanoseconds ), https://tc39.es/ecma262/#sec-getnamedtimezoneoffsetnanoseconds
Unicode::TimeZoneOffset get_named_time_zone_offset_nanoseconds(StringView time_zone_identifier, Crypto::SignedBigInteger const& epoch_nanoseconds) Unicode::TimeZoneOffset get_named_time_zone_offset_nanoseconds(Utf16View time_zone_identifier, Crypto::SignedBigInteger const& epoch_nanoseconds)
{ {
// Since UnixDateTime::from_seconds_since_epoch() and UnixDateTime::from_nanoseconds_since_epoch() both take an i64, converting to // Since UnixDateTime::from_seconds_since_epoch() and UnixDateTime::from_nanoseconds_since_epoch() both take an i64, converting to
// seconds first gives us a greater range. The TZDB doesn't have sub-second offsets. // seconds first gives us a greater range. The TZDB doesn't have sub-second offsets.
@ -431,7 +431,7 @@ Unicode::TimeZoneOffset get_named_time_zone_offset_nanoseconds(StringView time_z
// 21.4.1.21 GetNamedTimeZoneOffsetNanoseconds ( timeZoneIdentifier, epochNanoseconds ), https://tc39.es/ecma262/#sec-getnamedtimezoneoffsetnanoseconds // 21.4.1.21 GetNamedTimeZoneOffsetNanoseconds ( timeZoneIdentifier, epochNanoseconds ), https://tc39.es/ecma262/#sec-getnamedtimezoneoffsetnanoseconds
// OPTIMIZATION: This overload is provided to allow callers to avoid BigInt construction if they do not need infinitely precise nanosecond resolution. // OPTIMIZATION: This overload is provided to allow callers to avoid BigInt construction if they do not need infinitely precise nanosecond resolution.
Unicode::TimeZoneOffset get_named_time_zone_offset_milliseconds(StringView time_zone_identifier, double epoch_milliseconds) Unicode::TimeZoneOffset get_named_time_zone_offset_milliseconds(Utf16View time_zone_identifier, double epoch_milliseconds)
{ {
auto seconds = epoch_milliseconds / 1000.0; auto seconds = epoch_milliseconds / 1000.0;
auto time = UnixDateTime::from_seconds_since_epoch(clip_double_to_sane_time(seconds)); auto time = UnixDateTime::from_seconds_since_epoch(clip_double_to_sane_time(seconds));
@ -499,7 +499,7 @@ double local_time(double time)
// 4. Else, // 4. Else,
else { else {
// a. Let offsetNs be GetNamedTimeZoneOffsetNanoseconds(systemTimeZoneIdentifier, ((t) × 10^6)). // a. Let offsetNs be GetNamedTimeZoneOffsetNanoseconds(systemTimeZoneIdentifier, ((t) × 10^6)).
auto offset = get_named_time_zone_offset_milliseconds(system_time_zone_identifier.utf16_view().bytes(), time); auto offset = get_named_time_zone_offset_milliseconds(system_time_zone_identifier.utf16_view(), time);
offset_nanoseconds = static_cast<double>(offset.offset.to_nanoseconds()); offset_nanoseconds = static_cast<double>(offset.offset.to_nanoseconds());
} }
@ -533,7 +533,7 @@ double utc_time(double time)
auto iso_date_time = Temporal::time_value_to_iso_date_time_record(time); auto iso_date_time = Temporal::time_value_to_iso_date_time_record(time);
// b. Let possibleInstants be GetNamedTimeZoneEpochNanoseconds(systemTimeZoneIdentifier, isoDateTime). // b. Let possibleInstants be GetNamedTimeZoneEpochNanoseconds(systemTimeZoneIdentifier, isoDateTime).
auto possible_instants = get_named_time_zone_epoch_nanoseconds(system_time_zone_identifier.utf16_view().bytes(), iso_date_time); auto possible_instants = get_named_time_zone_epoch_nanoseconds(system_time_zone_identifier.utf16_view(), iso_date_time);
// c. NOTE: The following steps ensure that when t represents local time repeating multiple times at a negative // c. NOTE: The following steps ensure that when t represents local time repeating multiple times at a negative
// time zone transition (e.g. when the daylight saving time ends or the time zone offset is decreased due to // time zone transition (e.g. when the daylight saving time ends or the time zone offset is decreased due to
@ -568,7 +568,7 @@ double utc_time(double time)
} }
// f. Let offsetNs be GetNamedTimeZoneOffsetNanoseconds(systemTimeZoneIdentifier, disambiguatedInstant). // f. Let offsetNs be GetNamedTimeZoneOffsetNanoseconds(systemTimeZoneIdentifier, disambiguatedInstant).
auto offset = get_named_time_zone_offset_nanoseconds(system_time_zone_identifier.utf16_view().bytes(), disambiguated_instant); auto offset = get_named_time_zone_offset_nanoseconds(system_time_zone_identifier.utf16_view(), disambiguated_instant);
offset_nanoseconds = static_cast<double>(offset.offset.to_nanoseconds()); offset_nanoseconds = static_cast<double>(offset.offset.to_nanoseconds());
} }

View file

@ -88,9 +88,9 @@ JS_API u16 ms_from_time(double);
Crypto::SignedBigInteger get_utc_epoch_nanoseconds(Temporal::ISODateTime const&); Crypto::SignedBigInteger get_utc_epoch_nanoseconds(Temporal::ISODateTime const&);
i64 clip_bigint_to_sane_time(Crypto::SignedBigInteger const& value); i64 clip_bigint_to_sane_time(Crypto::SignedBigInteger const& value);
i64 clip_double_to_sane_time(double value); i64 clip_double_to_sane_time(double value);
Vector<Crypto::SignedBigInteger> get_named_time_zone_epoch_nanoseconds(StringView time_zone_identifier, Temporal::ISODateTime const&); Vector<Crypto::SignedBigInteger> get_named_time_zone_epoch_nanoseconds(Utf16View time_zone_identifier, Temporal::ISODateTime const&);
Unicode::TimeZoneOffset get_named_time_zone_offset_nanoseconds(StringView time_zone_identifier, Crypto::SignedBigInteger const& epoch_nanoseconds); Unicode::TimeZoneOffset get_named_time_zone_offset_nanoseconds(Utf16View time_zone_identifier, Crypto::SignedBigInteger const& epoch_nanoseconds);
Unicode::TimeZoneOffset get_named_time_zone_offset_milliseconds(StringView time_zone_identifier, double epoch_milliseconds); Unicode::TimeZoneOffset get_named_time_zone_offset_milliseconds(Utf16View time_zone_identifier, double epoch_milliseconds);
Utf16String system_time_zone_identifier(); Utf16String system_time_zone_identifier();
JS_API void clear_system_time_zone_cache(); JS_API void clear_system_time_zone_cache();
double local_time(double time); double local_time(double time);

View file

@ -1117,7 +1117,7 @@ Utf16String time_zone_string(double time)
// 2. If offsetMinutes is EMPTY, then // 2. If offsetMinutes is EMPTY, then
if (!offset_minutes.has_value()) { if (!offset_minutes.has_value()) {
// a. Let offsetNs be GetNamedTimeZoneOffsetNanoseconds(systemTimeZoneIdentifier, ((tv) × 10^6)). // a. Let offsetNs be GetNamedTimeZoneOffsetNanoseconds(systemTimeZoneIdentifier, ((tv) × 10^6)).
auto offset = get_named_time_zone_offset_milliseconds(system_time_zone_identifier.utf16_view().bytes(), time); auto offset = get_named_time_zone_offset_milliseconds(system_time_zone_identifier.utf16_view(), time);
in_dst = offset.in_dst; in_dst = offset.in_dst;
// b. Set offsetMinutes to truncate(offsetNs / (60 × 10**9)). // b. Set offsetMinutes to truncate(offsetNs / (60 × 10**9)).
@ -1133,7 +1133,9 @@ Utf16String time_zone_string(double time)
auto tz_name = Unicode::current_time_zone(); auto tz_name = Unicode::current_time_zone();
// Most implementations seem to prefer the long-form display name of the time zone. Not super important, but we may as well match that behavior. // Most implementations seem to prefer the long-form display name of the time zone. Not super important, but we may as well match that behavior.
if (auto name = Unicode::time_zone_display_name(Unicode::default_locale().bytes(), tz_name.utf16_view().bytes(), in_dst, time); name.has_value()) auto locale = MUST(Unicode::default_locale().to_byte_string());
auto time_zone_identifier = MUST(tz_name.utf16_view().to_byte_string());
if (auto name = Unicode::time_zone_display_name(locale.view(), time_zone_identifier.view(), in_dst, time); name.has_value())
tz_name = name.release_value(); tz_name = name.release_value();
// 10. Return the string-concatenation of offsetString and tzName. // 10. Return the string-concatenation of offsetString and tzName.

View file

@ -32,11 +32,6 @@ GC::Ref<Error> Error::create(Realm& realm, Utf16View message)
return error; return error;
} }
GC::Ref<Error> Error::create(Realm& realm, StringView message)
{
return create(realm, Utf16String::from_utf8(message));
}
Utf16String Error::stack_string(CompactTraceback compact) const Utf16String Error::stack_string(CompactTraceback compact) const
{ {
return ErrorData::stack_string(compact); return ErrorData::stack_string(compact);
@ -114,11 +109,6 @@ void Error::set_message(Utf16View message)
return error; \ return error; \
} \ } \
\ \
GC::Ref<ClassName> ClassName::create(Realm& realm, StringView message) \
{ \
return create(realm, Utf16String::from_utf8(message)); \
} \
\
ClassName::ClassName(Object& prototype) \ ClassName::ClassName(Object& prototype) \
: Error(prototype) \ : Error(prototype) \
{ \ { \

View file

@ -27,7 +27,6 @@ public:
static GC::Ref<Error> create(Realm&); static GC::Ref<Error> create(Realm&);
static GC::Ref<Error> create(Realm&, Utf16String message); static GC::Ref<Error> create(Realm&, Utf16String message);
static GC::Ref<Error> create(Realm&, Utf16View message); static GC::Ref<Error> create(Realm&, Utf16View message);
static GC::Ref<Error> create(Realm&, StringView message);
virtual ~Error() override = default; virtual ~Error() override = default;
@ -65,7 +64,6 @@ inline bool Object::fast_is<Error>() const { return is_error_object(); }
static GC::Ref<ClassName> create(Realm&); \ static GC::Ref<ClassName> create(Realm&); \
static GC::Ref<ClassName> create(Realm&, Utf16String message); \ static GC::Ref<ClassName> create(Realm&, Utf16String message); \
static GC::Ref<ClassName> create(Realm&, Utf16View message); \ static GC::Ref<ClassName> create(Realm&, Utf16View message); \
static GC::Ref<ClassName> create(Realm&, StringView message); \
\ \
explicit ClassName(Object& prototype); \ explicit ClassName(Object& prototype); \
virtual ~ClassName() override = default; \ virtual ~ClassName() override = default; \

View file

@ -51,7 +51,7 @@ GC::Ref<PrimitiveString> FunctionObject::make_function_name(Variant<PropertyKey,
} }
// NOTE: This is necessary as we use a different parameter name. // NOTE: This is necessary as we use a different parameter name.
else { else {
name = name_arg.get<PropertyKey>().to_string(); name = name_arg.get<PropertyKey>().to_utf16_string();
} }
// 4. If F has an [[InitialName]] internal slot, then // 4. If F has an [[InitialName]] internal slot, then

View file

@ -373,7 +373,7 @@ static ThrowCompletionOr<Utf16String> encode(VM& vm, Utf16View const& string, St
auto string_length = string.length_in_code_units(); auto string_length = string.length_in_code_units();
// 2. Let R be the empty String. // 2. Let R be the empty String.
StringBuilder encoded_builder(StringBuilder::Mode::UTF16); Utf16StringBuilder encoded_builder;
// 3. Let alwaysUnescaped be the string-concatenation of the ASCII word characters and "-.!~*'()". // 3. Let alwaysUnescaped be the string-concatenation of the ASCII word characters and "-.!~*'()".
// 4. Let unescapedSet be the string-concatenation of alwaysUnescaped and extraUnescaped. // 4. Let unescapedSet be the string-concatenation of alwaysUnescaped and extraUnescaped.
@ -396,7 +396,7 @@ static ThrowCompletionOr<Utf16String> encode(VM& vm, Utf16View const& string, St
k++; k++;
// ii. Set R to the string-concatenation of R and C. // ii. Set R to the string-concatenation of R and C.
encoded_builder.append(code_unit); encoded_builder.append_code_unit(code_unit);
} }
// d. Else, // d. Else,
else { else {
@ -419,7 +419,7 @@ static ThrowCompletionOr<Utf16String> encode(VM& vm, Utf16View const& string, St
VERIFY(nwritten > 0); VERIFY(nwritten > 0);
} }
} }
return encoded_builder.to_utf16_string(); return encoded_builder.to_string();
} }
static ThrowCompletionOr<u8> decode_percent_encoded_byte(VM& vm, Utf16View const& string, size_t percent_index) static ThrowCompletionOr<u8> decode_percent_encoded_byte(VM& vm, Utf16View const& string, size_t percent_index)
@ -543,7 +543,7 @@ JS_DEFINE_NATIVE_FUNCTION(GlobalObject::escape)
auto string = TRY(vm.argument(0).to_utf16_string(vm)); auto string = TRY(vm.argument(0).to_utf16_string(vm));
// 3. Let R be the empty String. // 3. Let R be the empty String.
StringBuilder escaped(StringBuilder::Mode::UTF16); Utf16StringBuilder escaped;
// 4. Let unescapedSet be the string-concatenation of the ASCII word characters and "@*+-./". // 4. Let unescapedSet be the string-concatenation of the ASCII word characters and "@*+-./".
auto unescaped_set = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@*_+-./"sv; auto unescaped_set = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@*_+-./"sv;
@ -559,7 +559,7 @@ JS_DEFINE_NATIVE_FUNCTION(GlobalObject::escape)
// NOTE: We know unescapedSet is ASCII-only, so ensure we have an ASCII codepoint before casting to char. // NOTE: We know unescapedSet is ASCII-only, so ensure we have an ASCII codepoint before casting to char.
if (is_ascii(code_unit) && unescaped_set.contains(static_cast<char>(code_unit))) { if (is_ascii(code_unit) && unescaped_set.contains(static_cast<char>(code_unit))) {
// i. Let S be the String value containing the single code unit char. // i. Let S be the String value containing the single code unit char.
escaped.append(static_cast<char>(code_unit)); escaped.append_ascii(static_cast<char>(code_unit));
} }
// c. Else, // c. Else,
// i. Let n be the numeric value of char. // i. Let n be the numeric value of char.
@ -581,7 +581,7 @@ JS_DEFINE_NATIVE_FUNCTION(GlobalObject::escape)
} }
// 7. Return R. // 7. Return R.
return PrimitiveString::create(vm, escaped.to_utf16_string()); return PrimitiveString::create(vm, escaped.to_string());
} }
// B.2.1.2 unescape ( string ), https://tc39.es/ecma262/#sec-unescape-string // B.2.1.2 unescape ( string ), https://tc39.es/ecma262/#sec-unescape-string

View file

@ -4,7 +4,6 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/AllOf.h>
#include <AK/CharacterTypes.h> #include <AK/CharacterTypes.h>
#include <AK/Find.h> #include <AK/Find.h>
#include <AK/NeverDestroyed.h> #include <AK/NeverDestroyed.h>
@ -110,43 +109,18 @@ static bool is_well_formed_language_tag_impl(ViewType locale)
} }
// 6.2.1 IsWellFormedLanguageTag ( locale ), https://tc39.es/ecma402/#sec-iswellformedlanguagetag // 6.2.1 IsWellFormedLanguageTag ( locale ), https://tc39.es/ecma402/#sec-iswellformedlanguagetag
bool is_well_formed_language_tag(StringView locale)
{
return is_well_formed_language_tag_impl(locale);
}
bool is_well_formed_language_tag(Utf16View locale) bool is_well_formed_language_tag(Utf16View locale)
{ {
return is_well_formed_language_tag_impl(locale); return is_well_formed_language_tag_impl(locale);
} }
// 6.2.2 CanonicalizeUnicodeLocaleId ( locale ), https://tc39.es/ecma402/#sec-canonicalizeunicodelocaleid // 6.2.2 CanonicalizeUnicodeLocaleId ( locale ), https://tc39.es/ecma402/#sec-canonicalizeunicodelocaleid
Utf16String canonicalize_unicode_locale_id(StringView locale)
{
return Unicode::canonicalize_unicode_locale_id(locale);
}
Utf16String canonicalize_unicode_locale_id(Utf16View locale) Utf16String canonicalize_unicode_locale_id(Utf16View locale)
{ {
return Unicode::canonicalize_unicode_locale_id(locale); return Unicode::canonicalize_unicode_locale_id(locale);
} }
// 6.3.1 IsWellFormedCurrencyCode ( currency ), https://tc39.es/ecma402/#sec-iswellformedcurrencycode // 6.3.1 IsWellFormedCurrencyCode ( currency ), https://tc39.es/ecma402/#sec-iswellformedcurrencycode
bool is_well_formed_currency_code(StringView currency)
{
// 1. If the length of currency is not 3, return false.
if (currency.length() != 3)
return false;
// 2. Let normalized be the ASCII-uppercase of currency.
// 3. If normalized contains any code unit outside of 0x0041 through 0x005A (corresponding to Unicode characters LATIN CAPITAL LETTER A through LATIN CAPITAL LETTER Z), return false.
if (!all_of(currency, is_ascii_alpha))
return false;
// 4. Return true.
return true;
}
bool is_well_formed_currency_code(Utf16View currency) bool is_well_formed_currency_code(Utf16View currency)
{ {
// 1. If the length of currency is not 3, return false. // 1. If the length of currency is not 3, return false.
@ -190,7 +164,7 @@ Vector<TimeZoneIdentifier> const& available_named_time_zone_identifiers()
// b. If identifier is a Link name and identifier is not "UTC", then // b. If identifier is a Link name and identifier is not "UTC", then
if (identifier.utf16_view() != "UTC"sv) { if (identifier.utf16_view() != "UTC"sv) {
if (auto resolved = Unicode::resolve_primary_time_zone(identifier.utf16_view().bytes()); resolved.has_value() && identifier != *resolved) { if (auto resolved = Unicode::resolve_primary_time_zone(identifier.utf16_view()); resolved.has_value() && identifier != *resolved) {
// i. Set primary to the Zone name that identifier resolves to, according to the rules for resolving Link // i. Set primary to the Zone name that identifier resolves to, according to the rules for resolving Link
// names in the IANA Time Zone Database. // names in the IANA Time Zone Database.
primary = resolved.release_value(); primary = resolved.release_value();
@ -443,7 +417,8 @@ Utf16String insert_unicode_extension_and_canonicalize(Unicode::LocaleID locale,
locale.extensions.append(Unicode::LocaleExtension { move(attributes), move(keywords) }); locale.extensions.append(Unicode::LocaleExtension { move(attributes), move(keywords) });
// 10. Return CanonicalizeUnicodeLocaleId(newLocale). // 10. Return CanonicalizeUnicodeLocaleId(newLocale).
return JS::Intl::canonicalize_unicode_locale_id(locale.to_utf16_string()); auto locale_string = locale.to_utf16_string();
return JS::Intl::canonicalize_unicode_locale_id(locale_string.utf16_view());
} }
template<typename T> template<typename T>

View file

@ -66,11 +66,8 @@ AK_ENUM_BITWISE_OPERATORS(SpecialBehaviors);
using StringOrBoolean = Variant<StringView, bool>; using StringOrBoolean = Variant<StringView, bool>;
bool is_well_formed_language_tag(StringView locale);
bool is_well_formed_language_tag(Utf16View locale); bool is_well_formed_language_tag(Utf16View locale);
Utf16String canonicalize_unicode_locale_id(StringView locale);
Utf16String canonicalize_unicode_locale_id(Utf16View locale); Utf16String canonicalize_unicode_locale_id(Utf16View locale);
bool is_well_formed_currency_code(StringView currency);
bool is_well_formed_currency_code(Utf16View currency); bool is_well_formed_currency_code(Utf16View currency);
Vector<TimeZoneIdentifier> const& available_named_time_zone_identifiers(); Vector<TimeZoneIdentifier> const& available_named_time_zone_identifiers();
Optional<TimeZoneIdentifier const&> get_available_named_time_zone_identifier(Utf16View time_zone_identifier); Optional<TimeZoneIdentifier const&> get_available_named_time_zone_identifier(Utf16View time_zone_identifier);

View file

@ -128,9 +128,9 @@ ThrowCompletionOr<GC::Ref<Object>> CollatorConstructor::construct(FunctionObject
// Non-standard, create an ICU collator for this Intl object. // Non-standard, create an ICU collator for this Intl object.
auto icu_collator = Unicode::Collator::create( auto icu_collator = Unicode::Collator::create(
result.icu_locale.utf16_view().bytes(), result.icu_locale.utf16_view(),
collator->usage(), collator->usage(),
collator->collation().utf16_view().bytes(), collator->collation().utf16_view(),
sensitivity, sensitivity,
collator->case_first(), collator->case_first(),
collator->numeric(), collator->numeric(),

View file

@ -62,7 +62,7 @@ ReadonlySpan<ResolutionOptionDescriptor> DateTimeFormat::resolution_option_descr
return *descriptors; return *descriptors;
} }
static Optional<Unicode::DateTimeFormat const&> get_or_create_formatter(StringView locale, StringView time_zone, OwnPtr<Unicode::DateTimeFormat>& formatter, Optional<Unicode::CalendarPattern> const& format) static Optional<Unicode::DateTimeFormat const&> get_or_create_formatter(Utf16View locale, Utf16View time_zone, OwnPtr<Unicode::DateTimeFormat>& formatter, Optional<Unicode::CalendarPattern> const& format)
{ {
if (formatter) if (formatter)
return *formatter; return *formatter;
@ -75,32 +75,32 @@ static Optional<Unicode::DateTimeFormat const&> get_or_create_formatter(StringVi
Optional<Unicode::DateTimeFormat const&> DateTimeFormat::temporal_plain_date_formatter() Optional<Unicode::DateTimeFormat const&> DateTimeFormat::temporal_plain_date_formatter()
{ {
return get_or_create_formatter(m_icu_locale.utf16_view().bytes(), "GMT+00:00"sv, m_temporal_plain_date_formatter, m_temporal_plain_date_format); return get_or_create_formatter(m_icu_locale.utf16_view(), "GMT+00:00"sv, m_temporal_plain_date_formatter, m_temporal_plain_date_format);
} }
Optional<Unicode::DateTimeFormat const&> DateTimeFormat::temporal_plain_year_month_formatter() Optional<Unicode::DateTimeFormat const&> DateTimeFormat::temporal_plain_year_month_formatter()
{ {
return get_or_create_formatter(m_icu_locale.utf16_view().bytes(), "GMT+00:00"sv, m_temporal_plain_year_month_formatter, m_temporal_plain_year_month_format); return get_or_create_formatter(m_icu_locale.utf16_view(), "GMT+00:00"sv, m_temporal_plain_year_month_formatter, m_temporal_plain_year_month_format);
} }
Optional<Unicode::DateTimeFormat const&> DateTimeFormat::temporal_plain_month_day_formatter() Optional<Unicode::DateTimeFormat const&> DateTimeFormat::temporal_plain_month_day_formatter()
{ {
return get_or_create_formatter(m_icu_locale.utf16_view().bytes(), "GMT+00:00"sv, m_temporal_plain_month_day_formatter, m_temporal_plain_month_day_format); return get_or_create_formatter(m_icu_locale.utf16_view(), "GMT+00:00"sv, m_temporal_plain_month_day_formatter, m_temporal_plain_month_day_format);
} }
Optional<Unicode::DateTimeFormat const&> DateTimeFormat::temporal_plain_time_formatter() Optional<Unicode::DateTimeFormat const&> DateTimeFormat::temporal_plain_time_formatter()
{ {
return get_or_create_formatter(m_icu_locale.utf16_view().bytes(), "GMT+00:00"sv, m_temporal_plain_time_formatter, m_temporal_plain_time_format); return get_or_create_formatter(m_icu_locale.utf16_view(), "GMT+00:00"sv, m_temporal_plain_time_formatter, m_temporal_plain_time_format);
} }
Optional<Unicode::DateTimeFormat const&> DateTimeFormat::temporal_plain_date_time_formatter() Optional<Unicode::DateTimeFormat const&> DateTimeFormat::temporal_plain_date_time_formatter()
{ {
return get_or_create_formatter(m_icu_locale.utf16_view().bytes(), "GMT+00:00"sv, m_temporal_plain_date_time_formatter, m_temporal_plain_date_time_format); return get_or_create_formatter(m_icu_locale.utf16_view(), "GMT+00:00"sv, m_temporal_plain_date_time_formatter, m_temporal_plain_date_time_format);
} }
Optional<Unicode::DateTimeFormat const&> DateTimeFormat::temporal_instant_formatter() Optional<Unicode::DateTimeFormat const&> DateTimeFormat::temporal_instant_formatter()
{ {
return get_or_create_formatter(m_icu_locale.utf16_view().bytes(), m_temporal_time_zone.utf16_view().bytes(), m_temporal_instant_formatter, m_temporal_instant_format); return get_or_create_formatter(m_icu_locale.utf16_view(), m_temporal_time_zone.utf16_view(), m_temporal_instant_formatter, m_temporal_instant_format);
} }
// 11.5.5 FormatDateTimePattern ( dateTimeFormat, patternParts, x, rangeFormatOptions ), https://tc39.es/ecma402/#sec-formatdatetimepattern // 11.5.5 FormatDateTimePattern ( dateTimeFormat, patternParts, x, rangeFormatOptions ), https://tc39.es/ecma402/#sec-formatdatetimepattern
@ -535,7 +535,7 @@ static double to_epoch_milliseconds(Crypto::SignedBigInteger const& epoch_nanose
ThrowCompletionOr<ValueFormat> handle_date_time_temporal_date(VM& vm, DateTimeFormat& date_time_format, Temporal::PlainDate const& temporal_date) ThrowCompletionOr<ValueFormat> handle_date_time_temporal_date(VM& vm, DateTimeFormat& date_time_format, Temporal::PlainDate const& temporal_date)
{ {
// 1. If temporalDate.[[Calendar]] is not either dateTimeFormat.[[Calendar]] or "iso8601", throw a RangeError exception. // 1. If temporalDate.[[Calendar]] is not either dateTimeFormat.[[Calendar]] or "iso8601", throw a RangeError exception.
auto date_time_format_calendar = date_time_format.calendar().utf16_view().bytes(); auto date_time_format_calendar = date_time_format.calendar().utf16_view();
if (!temporal_date.calendar().is_one_of(date_time_format_calendar, "iso8601"sv)) if (!temporal_date.calendar().is_one_of(date_time_format_calendar, "iso8601"sv))
return vm.throw_completion<RangeError>(ErrorType::IntlTemporalInvalidCalendar, "Temporal.PlainDate"sv, temporal_date.calendar(), date_time_format.calendar()); return vm.throw_completion<RangeError>(ErrorType::IntlTemporalInvalidCalendar, "Temporal.PlainDate"sv, temporal_date.calendar(), date_time_format.calendar());
@ -560,7 +560,7 @@ ThrowCompletionOr<ValueFormat> handle_date_time_temporal_date(VM& vm, DateTimeFo
ThrowCompletionOr<ValueFormat> handle_date_time_temporal_year_month(VM& vm, DateTimeFormat& date_time_format, Temporal::PlainYearMonth const& temporal_year_month) ThrowCompletionOr<ValueFormat> handle_date_time_temporal_year_month(VM& vm, DateTimeFormat& date_time_format, Temporal::PlainYearMonth const& temporal_year_month)
{ {
// 1. If temporalYearMonth.[[Calendar]] is not equal to dateTimeFormat.[[Calendar]], throw a RangeError exception. // 1. If temporalYearMonth.[[Calendar]] is not equal to dateTimeFormat.[[Calendar]], throw a RangeError exception.
if (temporal_year_month.calendar() != date_time_format.calendar().utf16_view().bytes()) if (temporal_year_month.calendar() != date_time_format.calendar().utf16_view())
return vm.throw_completion<RangeError>(ErrorType::IntlTemporalInvalidCalendar, "Temporal.PlainYearMonth"sv, temporal_year_month.calendar(), date_time_format.calendar()); return vm.throw_completion<RangeError>(ErrorType::IntlTemporalInvalidCalendar, "Temporal.PlainYearMonth"sv, temporal_year_month.calendar(), date_time_format.calendar());
// 2. Let isoDateTime be CombineISODateAndTimeRecord(temporalYearMonth.[[ISODate]], NoonTimeRecord()). // 2. Let isoDateTime be CombineISODateAndTimeRecord(temporalYearMonth.[[ISODate]], NoonTimeRecord()).
@ -584,7 +584,7 @@ ThrowCompletionOr<ValueFormat> handle_date_time_temporal_year_month(VM& vm, Date
ThrowCompletionOr<ValueFormat> handle_date_time_temporal_month_day(VM& vm, DateTimeFormat& date_time_format, Temporal::PlainMonthDay const& temporal_month_day) ThrowCompletionOr<ValueFormat> handle_date_time_temporal_month_day(VM& vm, DateTimeFormat& date_time_format, Temporal::PlainMonthDay const& temporal_month_day)
{ {
// 1. If temporalMonthDay.[[Calendar]] is not equal to dateTimeFormat.[[Calendar]], throw a RangeError exception. // 1. If temporalMonthDay.[[Calendar]] is not equal to dateTimeFormat.[[Calendar]], throw a RangeError exception.
if (temporal_month_day.calendar() != date_time_format.calendar().utf16_view().bytes()) if (temporal_month_day.calendar() != date_time_format.calendar().utf16_view())
return vm.throw_completion<RangeError>(ErrorType::IntlTemporalInvalidCalendar, "Temporal.PlainMonthDay"sv, temporal_month_day.calendar(), date_time_format.calendar()); return vm.throw_completion<RangeError>(ErrorType::IntlTemporalInvalidCalendar, "Temporal.PlainMonthDay"sv, temporal_month_day.calendar(), date_time_format.calendar());
// 2. Let isoDateTime be CombineISODateAndTimeRecord(temporalMonthDay.[[ISODate]], NoonTimeRecord()). // 2. Let isoDateTime be CombineISODateAndTimeRecord(temporalMonthDay.[[ISODate]], NoonTimeRecord()).
@ -631,7 +631,7 @@ ThrowCompletionOr<ValueFormat> handle_date_time_temporal_time(VM& vm, DateTimeFo
ThrowCompletionOr<ValueFormat> handle_date_time_temporal_date_time(VM& vm, DateTimeFormat& date_time_format, Temporal::PlainDateTime const& date_time) ThrowCompletionOr<ValueFormat> handle_date_time_temporal_date_time(VM& vm, DateTimeFormat& date_time_format, Temporal::PlainDateTime const& date_time)
{ {
// 1. If dateTime.[[Calendar]] is not "iso8601" and not equal to dateTimeFormat.[[Calendar]], throw a RangeError exception. // 1. If dateTime.[[Calendar]] is not "iso8601" and not equal to dateTimeFormat.[[Calendar]], throw a RangeError exception.
auto date_time_format_calendar = date_time_format.calendar().utf16_view().bytes(); auto date_time_format_calendar = date_time_format.calendar().utf16_view();
if (!date_time.calendar().is_one_of(date_time_format_calendar, "iso8601"sv)) if (!date_time.calendar().is_one_of(date_time_format_calendar, "iso8601"sv))
return vm.throw_completion<RangeError>(ErrorType::IntlTemporalInvalidCalendar, "Temporal.PlainDateTime"sv, date_time.calendar(), date_time_format.calendar()); return vm.throw_completion<RangeError>(ErrorType::IntlTemporalInvalidCalendar, "Temporal.PlainDateTime"sv, date_time.calendar(), date_time_format.calendar());

View file

@ -337,8 +337,8 @@ ThrowCompletionOr<GC::Ref<DateTimeFormat>> create_date_time_format(VM& vm, Funct
// d. Let styles be resolvedLocaleData.[[styles]].[[<resolvedCalendar>]]. // d. Let styles be resolvedLocaleData.[[styles]].[[<resolvedCalendar>]].
// e. Let bestFormat be DateTimeStyleFormat(dateStyle, timeStyle, styles). // e. Let bestFormat be DateTimeStyleFormat(dateStyle, timeStyle, styles).
formatter = Unicode::DateTimeFormat::create_for_date_and_time_style( formatter = Unicode::DateTimeFormat::create_for_date_and_time_style(
date_time_format->icu_locale().utf16_view().bytes(), date_time_format->icu_locale().utf16_view(),
icu_time_zone.utf16_view().bytes(), icu_time_zone.utf16_view(),
format_options.hour_cycle, format_options.hour_cycle,
format_options.hour12, format_options.hour12,
date_time_format->date_style(), date_time_format->date_style(),
@ -425,8 +425,8 @@ ThrowCompletionOr<GC::Ref<DateTimeFormat>> create_date_time_format(VM& vm, Funct
} }
formatter = Unicode::DateTimeFormat::create_for_pattern_options( formatter = Unicode::DateTimeFormat::create_for_pattern_options(
date_time_format->icu_locale().utf16_view().bytes(), date_time_format->icu_locale().utf16_view(),
icu_time_zone.utf16_view().bytes(), icu_time_zone.utf16_view(),
best_format); best_format);
} }

View file

@ -79,6 +79,12 @@ JS_DEFINE_NATIVE_FUNCTION(DisplayNamesPrototype::of)
// 4. Let code be ? CanonicalCodeForDisplayNames(displayNames.[[Type]], code). // 4. Let code be ? CanonicalCodeForDisplayNames(displayNames.[[Type]], code).
code = TRY(canonical_code_for_display_names(vm, display_names->type(), code.as_string().utf16_string_view())); code = TRY(canonical_code_for_display_names(vm, display_names->type(), code.as_string().utf16_string_view()));
auto code_view = code.as_string().utf16_string_view(); auto code_view = code.as_string().utf16_string_view();
VERIFY(code_view.is_ascii());
auto code_string = MUST(code_view.to_byte_string());
auto locale_view = display_names->icu_locale().utf16_view();
VERIFY(locale_view.is_ascii());
auto locale_string = MUST(locale_view.to_byte_string());
// 5. Let fields be displayNames.[[Fields]]. // 5. Let fields be displayNames.[[Fields]].
// 6. If fields has a field [[<code>]], return fields.[[<code>]]. // 6. If fields has a field [[<code>]], return fields.[[<code>]].
@ -86,22 +92,22 @@ JS_DEFINE_NATIVE_FUNCTION(DisplayNamesPrototype::of)
switch (display_names->type()) { switch (display_names->type()) {
case DisplayNames::Type::Language: case DisplayNames::Type::Language:
result = Unicode::language_display_name(display_names->icu_locale().utf16_view().bytes(), code_view, display_names->language_display()); result = Unicode::language_display_name(locale_string.view(), code_string.view(), display_names->language_display());
break; break;
case DisplayNames::Type::Region: case DisplayNames::Type::Region:
result = Unicode::region_display_name(display_names->icu_locale().utf16_view().bytes(), code_view); result = Unicode::region_display_name(locale_string.view(), code_string.view());
break; break;
case DisplayNames::Type::Script: case DisplayNames::Type::Script:
result = Unicode::script_display_name(display_names->icu_locale().utf16_view().bytes(), code_view); result = Unicode::script_display_name(locale_string.view(), code_string.view());
break; break;
case DisplayNames::Type::Currency: case DisplayNames::Type::Currency:
result = Unicode::currency_display_name(display_names->icu_locale().utf16_view().bytes(), code_view, display_names->style()); result = Unicode::currency_display_name(locale_string.view(), code_string.view(), display_names->style());
break; break;
case DisplayNames::Type::Calendar: case DisplayNames::Type::Calendar:
result = Unicode::calendar_display_name(display_names->icu_locale().utf16_view().bytes(), code_view); result = Unicode::calendar_display_name(locale_string.view(), code_string.view());
break; break;
case DisplayNames::Type::DateTimeField: case DisplayNames::Type::DateTimeField:
result = Unicode::date_time_field_display_name(display_names->icu_locale().utf16_view().bytes(), code_view, display_names->style()); result = Unicode::date_time_field_display_name(locale_string.view(), code_string.view(), display_names->style());
break; break;
default: default:
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();

View file

@ -78,7 +78,7 @@ ThrowCompletionOr<GC::Ref<Object>> ListFormatConstructor::construct(FunctionObje
// 12. Let dataLocaleTypes be resolvedLocaleData.[[<type>]]. // 12. Let dataLocaleTypes be resolvedLocaleData.[[<type>]].
// 13. Set listFormat.[[Templates]] to dataLocaleTypes.[[<style>]]. // 13. Set listFormat.[[Templates]] to dataLocaleTypes.[[<style>]].
auto formatter = Unicode::ListFormat::create( auto formatter = Unicode::ListFormat::create(
result.icu_locale.utf16_view().bytes(), result.icu_locale.utf16_view(),
list_format->type(), list_format->type(),
list_format->style()); list_format->style());
list_format->set_formatter(move(formatter)); list_format->set_formatter(move(formatter));

View file

@ -114,7 +114,7 @@ JS_DEFINE_NATIVE_FUNCTION(LocalePrototype::maximize)
// 3. Let maximal be the result of the Add Likely Subtags algorithm applied to loc.[[Locale]]. If an error is signaled, set maximal to loc.[[Locale]]. // 3. Let maximal be the result of the Add Likely Subtags algorithm applied to loc.[[Locale]]. If an error is signaled, set maximal to loc.[[Locale]].
auto maximal = locale_object->locale(); auto maximal = locale_object->locale();
if (auto maximal_locale = Unicode::add_likely_subtags(locale_object->locale().utf16_view().bytes()); maximal_locale.has_value()) if (auto maximal_locale = Unicode::add_likely_subtags(locale_object->locale().utf16_view()); maximal_locale.has_value())
maximal = maximal_locale.release_value(); maximal = maximal_locale.release_value();
// 4. Return ! Construct(%Intl.Locale%, maximal). // 4. Return ! Construct(%Intl.Locale%, maximal).
@ -132,7 +132,7 @@ JS_DEFINE_NATIVE_FUNCTION(LocalePrototype::minimize)
// 3. Let minimal be the result of the Remove Likely Subtags algorithm applied to loc.[[Locale]]. If an error is signaled, set minimal to loc.[[Locale]]. // 3. Let minimal be the result of the Remove Likely Subtags algorithm applied to loc.[[Locale]]. If an error is signaled, set minimal to loc.[[Locale]].
auto minimal = locale_object->locale(); auto minimal = locale_object->locale();
if (auto minimal_locale = Unicode::remove_likely_subtags(locale_object->locale().utf16_view().bytes()); minimal_locale.has_value()) if (auto minimal_locale = Unicode::remove_likely_subtags(locale_object->locale().utf16_view()); minimal_locale.has_value())
minimal = minimal_locale.release_value(); minimal = minimal_locale.release_value();
// 4. Return ! Construct(%Intl.Locale%, minimal). // 4. Return ! Construct(%Intl.Locale%, minimal).

View file

@ -20,13 +20,13 @@ static Utf16String ascii_uppercase_currency_code(Utf16View currency)
{ {
VERIFY(currency.length_in_code_units() == 3); VERIFY(currency.length_in_code_units() == 3);
char code[3]; char16_t code[3];
for (size_t i = 0; i < currency.length_in_code_units(); ++i) { for (size_t i = 0; i < currency.length_in_code_units(); ++i) {
VERIFY(is_ascii_alpha(currency.code_unit_at(i))); VERIFY(is_ascii_alpha(currency.code_unit_at(i)));
code[i] = static_cast<char>(to_ascii_uppercase(currency.code_unit_at(i))); code[i] = to_ascii_uppercase(currency.code_unit_at(i));
} }
return Utf16String::from_ascii_without_validation({ code, 3 }); return Utf16String::from_utf16(Utf16View { code, 3 });
} }
// 16.1 The Intl.NumberFormat Constructor, https://tc39.es/ecma402/#sec-intl-numberformat-constructor // 16.1 The Intl.NumberFormat Constructor, https://tc39.es/ecma402/#sec-intl-numberformat-constructor
@ -172,7 +172,7 @@ ThrowCompletionOr<GC::Ref<Object>> NumberFormatConstructor::construct(FunctionOb
// Non-standard, create an ICU number formatter for this Intl object. // Non-standard, create an ICU number formatter for this Intl object.
auto formatter = Unicode::NumberFormat::create( auto formatter = Unicode::NumberFormat::create(
result.icu_locale.utf16_view().bytes(), result.icu_locale.utf16_view(),
number_format->display_options(), number_format->display_options(),
number_format->rounding_options()); number_format->rounding_options());
number_format->set_formatter(move(formatter)); number_format->set_formatter(move(formatter));

View file

@ -89,7 +89,7 @@ ThrowCompletionOr<GC::Ref<Object>> PluralRulesConstructor::construct(FunctionObj
// Non-standard, create an ICU number formatter for this Intl object. // Non-standard, create an ICU number formatter for this Intl object.
auto formatter = Unicode::NumberFormat::create( auto formatter = Unicode::NumberFormat::create(
result.icu_locale.utf16_view().bytes(), result.icu_locale.utf16_view(),
plural_rules->display_options(), plural_rules->display_options(),
plural_rules->rounding_options()); plural_rules->rounding_options());

View file

@ -88,7 +88,7 @@ ThrowCompletionOr<GC::Ref<Object>> RelativeTimeFormatConstructor::construct(Func
// 16. Let relativeTimeFormat.[[NumberFormat]] be ! Construct(%Intl.NumberFormat%, « locale, nfOptions »). // 16. Let relativeTimeFormat.[[NumberFormat]] be ! Construct(%Intl.NumberFormat%, « locale, nfOptions »).
// 17. Let relativeTimeFormat.[[PluralRules]] be ! Construct(%Intl.PluralRules%, « locale »). // 17. Let relativeTimeFormat.[[PluralRules]] be ! Construct(%Intl.PluralRules%, « locale »).
auto formatter = Unicode::RelativeTimeFormat::create( auto formatter = Unicode::RelativeTimeFormat::create(
result.icu_locale.utf16_view().bytes(), result.icu_locale.utf16_view(),
relative_time_format->style()); relative_time_format->style());
relative_time_format->set_formatter(move(formatter)); relative_time_format->set_formatter(move(formatter));

View file

@ -69,7 +69,7 @@ ThrowCompletionOr<GC::Ref<Object>> SegmenterConstructor::construct(FunctionObjec
// 9. Set segmenter.[[SegmenterGranularity]] to granularity. // 9. Set segmenter.[[SegmenterGranularity]] to granularity.
segmenter->set_segmenter_granularity(granularity.as_string().utf16_string_view()); segmenter->set_segmenter_granularity(granularity.as_string().utf16_string_view());
auto locale_segmenter = Unicode::Segmenter::create(result.icu_locale.utf16_view().bytes(), segmenter->segmenter_granularity()); auto locale_segmenter = Unicode::Segmenter::create(result.icu_locale.utf16_view(), segmenter->segmenter_granularity());
segmenter->set_segmenter(move(locale_segmenter)); segmenter->set_segmenter(move(locale_segmenter));
// 10. Return segmenter. // 10. Return segmenter.

View file

@ -5,6 +5,7 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/NeverDestroyed.h>
#include <LibGC/Root.h> #include <LibGC/Root.h>
#include <LibJS/Runtime/Accessor.h> #include <LibJS/Runtime/Accessor.h>
#include <LibJS/Runtime/AggregateErrorConstructor.h> #include <LibJS/Runtime/AggregateErrorConstructor.h>
@ -140,10 +141,11 @@
".incbin " #file_path "\n" \ ".incbin " #file_path "\n" \
".global " #name "_END\n" #name "_END:\n" \ ".global " #name "_END\n" #name "_END:\n" \
".byte 0\n"); \ ".byte 0\n"); \
extern unsigned char const name[]; extern unsigned char const name[]; \
extern unsigned char const name##_END[];
#if defined(AK_COMPILER_CLANG) || (defined(AK_COMPILER_GCC) && (__GNUC__ > 14)) #if defined(AK_COMPILER_CLANG) || (defined(AK_COMPILER_GCC) && (__GNUC__ > 14))
static constexpr unsigned char ABSTRACT_OPERATIONS[] = { static constexpr char16_t ABSTRACT_OPERATIONS[] = {
# embed "JavaScriptImplementations/AbstractOperations.js" suffix(, ) # embed "JavaScriptImplementations/AbstractOperations.js" suffix(, )
0 // null terminator 0 // null terminator
}; };
@ -152,7 +154,7 @@ INCLUDE_FILE_WITH_ASSEMBLY(ABSTRACT_OPERATIONS, "LibJS/Runtime/JavaScriptImpleme
#endif #endif
#if defined(AK_COMPILER_CLANG) || (defined(AK_COMPILER_GCC) && (__GNUC__ > 14)) #if defined(AK_COMPILER_CLANG) || (defined(AK_COMPILER_GCC) && (__GNUC__ > 14))
static constexpr unsigned char ARRAY_CONSTRUCTOR[] = { static constexpr char16_t ARRAY_CONSTRUCTOR[] = {
# embed "JavaScriptImplementations/ArrayConstructor.js" suffix(, ) # embed "JavaScriptImplementations/ArrayConstructor.js" suffix(, )
0 // null terminator 0 // null terminator
}; };
@ -164,6 +166,40 @@ namespace JS {
GC_DEFINE_ALLOCATOR(Intrinsics); GC_DEFINE_ALLOCATOR(Intrinsics);
#if !defined(AK_COMPILER_CLANG) && !(defined(AK_COMPILER_GCC) && (__GNUC__ > 14))
static Utf16String utf16_source_from_ascii_bytes(ReadonlyBytes source)
{
Vector<char16_t> code_units;
code_units.ensure_capacity(source.size());
for (auto byte : source) {
VERIFY(byte <= 0x7f);
code_units.unchecked_append(byte);
}
return Utf16String::from_utf16({ code_units.data(), code_units.size() });
}
#endif
static Utf16View abstract_operations_source()
{
#if defined(AK_COMPILER_CLANG) || (defined(AK_COMPILER_GCC) && (__GNUC__ > 14))
return { ABSTRACT_OPERATIONS, AK::array_size(ABSTRACT_OPERATIONS) - 1 };
#else
static NeverDestroyed<Utf16String> source = utf16_source_from_ascii_bytes({ ABSTRACT_OPERATIONS, static_cast<size_t>(ABSTRACT_OPERATIONS_END - ABSTRACT_OPERATIONS) });
return source->utf16_view();
#endif
}
static Utf16View array_constructor_source()
{
#if defined(AK_COMPILER_CLANG) || (defined(AK_COMPILER_GCC) && (__GNUC__ > 14))
return { ARRAY_CONSTRUCTOR, AK::array_size(ARRAY_CONSTRUCTOR) - 1 };
#else
static NeverDestroyed<Utf16String> source = utf16_source_from_ascii_bytes({ ARRAY_CONSTRUCTOR, static_cast<size_t>(ARRAY_CONSTRUCTOR_END - ARRAY_CONSTRUCTOR) });
return source->utf16_view();
#endif
}
static void initialize_constructor(VM& vm, PropertyKey const& property_key, Object& constructor, Object* prototype, PropertyAttributes constructor_property_attributes = Attribute::Writable | Attribute::Configurable) static void initialize_constructor(VM& vm, PropertyKey const& property_key, Object& constructor, Object* prototype, PropertyAttributes constructor_property_attributes = Attribute::Writable | Attribute::Configurable)
{ {
constructor.define_direct_property(vm.names.name, PrimitiveString::create(vm, property_key.as_string()), Attribute::Configurable); constructor.define_direct_property(vm.names.name, PrimitiveString::create(vm, property_key.as_string()), Attribute::Configurable);
@ -203,7 +239,7 @@ GC::Ref<Intrinsics> Intrinsics::create(Realm& realm)
return *intrinsics; return *intrinsics;
} }
static Vector<GC::Root<SharedFunctionInstanceData>> parse_builtin_file(unsigned char const* script_text, VM& vm) static Vector<GC::Root<SharedFunctionInstanceData>> parse_builtin_file(Utf16View script_text, VM& vm)
{ {
auto rust_compilation = RustIntegration::compile_builtin_file(script_text, vm); auto rust_compilation = RustIntegration::compile_builtin_file(script_text, vm);
VERIFY(rust_compilation.has_value()); VERIFY(rust_compilation.has_value());
@ -549,7 +585,7 @@ GC::Ref<Intl::Collator> Intrinsics::default_collator()
GC::Ref<NativeJavaScriptBackedFunction> Intrinsics::snake_name##_abstract_operation_function() \ GC::Ref<NativeJavaScriptBackedFunction> Intrinsics::snake_name##_abstract_operation_function() \
{ \ { \
if (!m_##snake_name##_abstract_operation_function) { \ if (!m_##snake_name##_abstract_operation_function) { \
auto shared_data_list = parse_builtin_file(ABSTRACT_OPERATIONS, m_realm->vm()); \ auto shared_data_list = parse_builtin_file(abstract_operations_source(), m_realm->vm()); \
auto it = shared_data_list.find_if([](auto const& shared_data) { \ auto it = shared_data_list.find_if([](auto const& shared_data) { \
return shared_data->m_name == #functionName##sv; \ return shared_data->m_name == #functionName##sv; \
}); \ }); \
@ -565,7 +601,7 @@ JS_ENUMERATE_NATIVE_JAVASCRIPT_BACKED_ABSTRACT_OPERATIONS
GC::Ref<NativeJavaScriptBackedFunction> Intrinsics::snake_name##_array_constructor_function() \ GC::Ref<NativeJavaScriptBackedFunction> Intrinsics::snake_name##_array_constructor_function() \
{ \ { \
if (!m_##snake_name##_array_constructor_function) { \ if (!m_##snake_name##_array_constructor_function) { \
auto shared_data_list = parse_builtin_file(ARRAY_CONSTRUCTOR, m_realm->vm()); \ auto shared_data_list = parse_builtin_file(array_constructor_source(), m_realm->vm()); \
auto it = shared_data_list.find_if([](auto const& shared_data) { \ auto it = shared_data_list.find_if([](auto const& shared_data) { \
return shared_data->m_name == #functionName##sv; \ return shared_data->m_name == #functionName##sv; \
}); \ }); \

View file

@ -4,10 +4,12 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/ByteBuffer.h>
#include <AK/Function.h> #include <AK/Function.h>
#include <AK/GenericLexer.h> #include <AK/GenericLexer.h>
#include <AK/StringConversions.h> #include <AK/StringConversions.h>
#include <AK/TypeCasts.h> #include <AK/TypeCasts.h>
#include <AK/UnicodeUtils.h>
#include <AK/Utf16StringBuilder.h> #include <AK/Utf16StringBuilder.h>
#include <AK/Utf16View.h> #include <AK/Utf16View.h>
#include <LibJS/Runtime/AbstractOperations.h> #include <LibJS/Runtime/AbstractOperations.h>
@ -86,24 +88,26 @@ ThrowCompletionOr<Optional<Utf16String>> JSONObject::stringify_impl(VM& vm, Valu
} }
} }
if (space.is_object()) { Optional<Utf16String> space_string;
if (space.is_string()) {
space_string = space.as_string().utf16_string();
} else if (space.is_object()) {
auto& space_object = space.as_object(); auto& space_object = space.as_object();
if (is<NumberObject>(space_object)) if (is<NumberObject>(space_object))
space = TRY(space.to_number(vm)); space = TRY(space.to_number(vm));
else if (is<StringObject>(space_object)) else if (is<StringObject>(space_object))
space = TRY(space.to_primitive_string(vm)); space_string = TRY(space.to_utf16_string(vm));
} }
if (space.is_number()) { if (space.is_number()) {
auto space_mv = MUST(space.to_integer_or_infinity(vm)); auto space_mv = MUST(space.to_integer_or_infinity(vm));
space_mv = min(10, space_mv); space_mv = min(10, space_mv);
state.gap = space_mv < 1 ? Utf16String {} : Utf16String::repeated(' ', space_mv); state.gap = space_mv < 1 ? Utf16String {} : Utf16String::repeated(' ', space_mv);
} else if (space.is_string()) { } else if (space_string.has_value()) {
auto string = space.as_string().utf16_string(); if (space_string->length_in_code_units() <= 10)
if (string.length_in_code_units() <= 10) state.gap = move(*space_string);
state.gap = string;
else else
state.gap = Utf16String::from_utf16(string.utf16_view().substring_view(0, 10)); state.gap = Utf16String::from_utf16(space_string->substring_view(0, 10));
} else { } else {
state.gap = Utf16String {}; state.gap = Utf16String {};
} }
@ -153,14 +157,14 @@ ThrowCompletionOr<bool> JSONObject::serialize_json_property(VM& vm, StringifySta
// b. If IsCallable(toJSON) is true, then // b. If IsCallable(toJSON) is true, then
if (to_json.is_function()) { if (to_json.is_function()) {
// i. Set value to ? Call(toJSON, value, « key »). // i. Set value to ? Call(toJSON, value, « key »).
value = TRY(call(vm, to_json.as_function(), value, PrimitiveString::create(vm, key.to_string()))); value = TRY(call(vm, to_json.as_function(), value, PrimitiveString::create(vm, key.to_utf16_string())));
} }
} }
// 3. If state.[[ReplacerFunction]] is not undefined, then // 3. If state.[[ReplacerFunction]] is not undefined, then
if (state.replacer_function) { if (state.replacer_function) {
// a. Set value to ? Call(state.[[ReplacerFunction]], holder, « key, value »). // a. Set value to ? Call(state.[[ReplacerFunction]], holder, « key, value »).
value = TRY(call(vm, *state.replacer_function, holder, PrimitiveString::create(vm, key.to_string()), value)); value = TRY(call(vm, *state.replacer_function, holder, PrimitiveString::create(vm, key.to_utf16_string()), value));
} }
// 4. If Type(value) is Object, then // 4. If Type(value) is Object, then
@ -181,7 +185,7 @@ ThrowCompletionOr<bool> JSONObject::serialize_json_property(VM& vm, StringifySta
// c. Else if value has a [[StringData]] internal slot, then // c. Else if value has a [[StringData]] internal slot, then
else if (is<StringObject>(value_object)) { else if (is<StringObject>(value_object)) {
// i. Set value to ? ToString(value). // i. Set value to ? ToString(value).
value = TRY(value.to_primitive_string(vm)); value = PrimitiveString::create(vm, TRY(value.to_utf16_string(vm)));
} }
// d. Else if value has a [[BooleanData]] internal slot, then // d. Else if value has a [[BooleanData]] internal slot, then
else if (auto const* boolean = as_if<BooleanObject>(value_object)) { else if (auto const* boolean = as_if<BooleanObject>(value_object)) {
@ -297,7 +301,7 @@ ThrowCompletionOr<void> JSONObject::serialize_json_object(VM& vm, StringifyState
} }
// Write key and colon // Write key and colon
quote_json_string(builder, key.to_string()); quote_json_string(builder, key.to_utf16_string());
builder.append_ascii(':'); builder.append_ascii(':');
if (!state.gap.is_empty()) if (!state.gap.is_empty())
builder.append_ascii(' '); builder.append_ascii(' ');
@ -601,13 +605,111 @@ static ALWAYS_INLINE ThrowCompletionOr<void> ensure_simdjson_fully_parsed(VM& vm
return {}; return {};
} }
static ThrowCompletionOr<Value> parse_simdjson_value(VM&, simdjson::ondemand::value, JSONParseRecord* record = nullptr); struct JSONTextBytes {
explicit JSONTextBytes(Utf16View text)
: text(text)
{
}
Utf16View text;
Optional<ByteBuffer> utf8_storage;
Vector<size_t> byte_to_code_unit_offsets;
char const* parser_bytes_data { nullptr };
StringView bytes() const
{
if (text.has_ascii_storage())
return { text.bytes() };
return { utf8_storage->bytes() };
}
size_t byte_offset_to_code_unit_offset(size_t byte_offset) const
{
if (text.has_ascii_storage())
return byte_offset;
VERIFY(byte_offset < byte_to_code_unit_offsets.size());
return byte_to_code_unit_offsets[byte_offset];
}
};
static void append_json_surrogate_escape(StringBuilder& builder, u16 code_unit)
{
builder.append("\\u"sv);
builder.appendff("{:04X}", code_unit);
}
static ErrorOr<JSONTextBytes> json_text_bytes(Utf16View text)
{
JSONTextBytes text_bytes { text };
if (text.has_ascii_storage())
return text_bytes;
StringBuilder builder;
text_bytes.byte_to_code_unit_offsets.append(0);
for (size_t code_unit_offset = 0; code_unit_offset < text.length_in_code_units(); ++code_unit_offset) {
auto code_unit = text.code_unit_at(code_unit_offset);
auto code_point = static_cast<u32>(code_unit);
size_t code_unit_length = 1;
if (AK::UnicodeUtils::is_utf16_high_surrogate(code_unit) && code_unit_offset + 1 < text.length_in_code_units()) {
auto next_code_unit = text.code_unit_at(code_unit_offset + 1);
if (AK::UnicodeUtils::is_utf16_low_surrogate(next_code_unit)) {
code_point = AK::UnicodeUtils::decode_utf16_surrogate_pair(code_unit, next_code_unit);
code_unit_length = 2;
}
}
if (code_unit_length == 1 && (AK::UnicodeUtils::is_utf16_high_surrogate(code_unit) || AK::UnicodeUtils::is_utf16_low_surrogate(code_unit))) {
append_json_surrogate_escape(builder, code_unit);
for (int byte_index = 0; byte_index < 6; ++byte_index) {
auto is_code_point_boundary = byte_index == 5;
text_bytes.byte_to_code_unit_offsets.append(code_unit_offset + (is_code_point_boundary ? 1 : 0));
}
continue;
}
auto utf8_length = AK::UnicodeUtils::code_point_to_utf8(code_point, [&](char byte) {
builder.append(byte);
});
VERIFY(utf8_length > 0);
for (int byte_index = 0; byte_index < utf8_length; ++byte_index) {
auto is_code_point_boundary = byte_index == utf8_length - 1;
text_bytes.byte_to_code_unit_offsets.append(code_unit_offset + (is_code_point_boundary ? code_unit_length : 0));
}
code_unit_offset += code_unit_length - 1;
}
text_bytes.utf8_storage = TRY(builder.to_byte_buffer());
VERIFY(text_bytes.byte_to_code_unit_offsets.size() == text_bytes.utf8_storage->size() + 1);
return text_bytes;
}
static ThrowCompletionOr<Value> parse_simdjson_value(VM&, JSONTextBytes const&, simdjson::ondemand::value, JSONParseRecord* record = nullptr);
// The source text matched by a primitive parse node, used by JSON.parse revivers. // The source text matched by a primitive parse node, used by JSON.parse revivers.
static Utf16String json_token_source(std::string_view raw) static Utf16String json_token_source(JSONTextBytes const& json_text, std::string_view raw)
{ {
StringView source { raw.data(), raw.size() }; StringView source { raw.data(), raw.size() };
return Utf16String::from_utf8(source.trim_whitespace()); size_t start = 0;
size_t end = source.length();
while (start < end && is_ascii_space(source[start]))
++start;
while (end > start && is_ascii_space(source[end - 1]))
--end;
auto bytes = json_text.bytes();
auto* bytes_data = json_text.parser_bytes_data;
VERIFY(bytes_data);
auto* token_start = source.characters_without_null_termination() + start;
auto* token_end = source.characters_without_null_termination() + end;
VERIFY(token_start >= bytes_data);
VERIFY(token_end >= token_start);
VERIFY(static_cast<size_t>(token_end - bytes_data) <= bytes.length());
auto code_unit_start = json_text.byte_offset_to_code_unit_offset(token_start - bytes_data);
auto code_unit_end = json_text.byte_offset_to_code_unit_offset(token_end - bytes_data);
return Utf16String::from_utf16(json_text.text.substring_view(code_unit_start, code_unit_end - code_unit_start));
} }
template<typename T> template<typename T>
@ -671,7 +773,7 @@ static ThrowCompletionOr<Value> parse_simdjson_string(VM& vm, T& value)
} }
template<typename T> template<typename T>
static ThrowCompletionOr<Value> parse_simdjson_array(VM& vm, T& value, JSONParseRecord* record = nullptr) static ThrowCompletionOr<Value> parse_simdjson_array(VM& vm, JSONTextBytes const& json_text, T& value, JSONParseRecord* record = nullptr)
{ {
auto& realm = *vm.current_realm(); auto& realm = *vm.current_realm();
@ -687,7 +789,7 @@ static ThrowCompletionOr<Value> parse_simdjson_array(VM& vm, T& value, JSONParse
if (element.get(element_value)) if (element.get(element_value))
return vm.throw_completion<SyntaxError>(ErrorType::JsonMalformed); return vm.throw_completion<SyntaxError>(ErrorType::JsonMalformed);
JSONParseRecord element_record; JSONParseRecord element_record;
auto parsed = TRY(parse_simdjson_value(vm, element_value, record ? &element_record : nullptr)); auto parsed = TRY(parse_simdjson_value(vm, json_text, element_value, record ? &element_record : nullptr));
array->define_direct_property(index++, parsed, default_attributes); array->define_direct_property(index++, parsed, default_attributes);
if (record) if (record)
record->elements.append(move(element_record)); record->elements.append(move(element_record));
@ -700,7 +802,7 @@ static ThrowCompletionOr<Value> parse_simdjson_array(VM& vm, T& value, JSONParse
} }
template<typename T> template<typename T>
static ThrowCompletionOr<Value> parse_simdjson_object(VM& vm, T& value, JSONParseRecord* record = nullptr) static ThrowCompletionOr<Value> parse_simdjson_object(VM& vm, JSONTextBytes const& json_text, T& value, JSONParseRecord* record = nullptr)
{ {
auto& realm = *vm.current_realm(); auto& realm = *vm.current_realm();
@ -723,7 +825,7 @@ static ThrowCompletionOr<Value> parse_simdjson_object(VM& vm, T& value, JSONPars
return vm.throw_completion<SyntaxError>(ErrorType::JsonMalformed); return vm.throw_completion<SyntaxError>(ErrorType::JsonMalformed);
auto key = unescaped_key.release_value(); auto key = unescaped_key.release_value();
JSONParseRecord entry_record; JSONParseRecord entry_record;
auto parsed = TRY(parse_simdjson_value(vm, field_value, record ? &entry_record : nullptr)); auto parsed = TRY(parse_simdjson_value(vm, json_text, field_value, record ? &entry_record : nullptr));
object->define_direct_property(key, parsed, default_attributes); object->define_direct_property(key, parsed, default_attributes);
if (record) { if (record) {
entry_record.key = key; entry_record.key = key;
@ -741,7 +843,7 @@ static ThrowCompletionOr<Value> parse_simdjson_object(VM& vm, T& value, JSONPars
return object; return object;
} }
static ThrowCompletionOr<Value> parse_simdjson_value(VM& vm, simdjson::ondemand::value value, JSONParseRecord* record) static ThrowCompletionOr<Value> parse_simdjson_value(VM& vm, JSONTextBytes const& json_text, simdjson::ondemand::value value, JSONParseRecord* record)
{ {
simdjson::ondemand::json_type type; simdjson::ondemand::json_type type;
if (value.type().get(type)) if (value.type().get(type))
@ -752,7 +854,7 @@ static ThrowCompletionOr<Value> parse_simdjson_value(VM& vm, simdjson::ondemand:
case simdjson::ondemand::json_type::null: case simdjson::ondemand::json_type::null:
if (record) { if (record) {
record->value = js_null(); record->value = js_null();
record->source = json_token_source(value.raw_json_token()); record->source = json_token_source(json_text, value.raw_json_token());
} }
return js_null(); return js_null();
case simdjson::ondemand::json_type::boolean: { case simdjson::ondemand::json_type::boolean: {
@ -762,7 +864,7 @@ static ThrowCompletionOr<Value> parse_simdjson_value(VM& vm, simdjson::ondemand:
return vm.throw_completion<SyntaxError>(ErrorType::JsonMalformed); return vm.throw_completion<SyntaxError>(ErrorType::JsonMalformed);
if (record) { if (record) {
record->value = Value(boolean_value); record->value = Value(boolean_value);
record->source = json_token_source(token); record->source = json_token_source(json_text, token);
} }
return Value(boolean_value); return Value(boolean_value);
} }
@ -772,7 +874,7 @@ static ThrowCompletionOr<Value> parse_simdjson_value(VM& vm, simdjson::ondemand:
auto parsed = TRY(parse_simdjson_number(vm, value, raw_sv)); auto parsed = TRY(parse_simdjson_number(vm, value, raw_sv));
if (record) { if (record) {
record->value = parsed; record->value = parsed;
record->source = json_token_source(raw); record->source = json_token_source(json_text, raw);
} }
return parsed; return parsed;
} }
@ -781,14 +883,14 @@ static ThrowCompletionOr<Value> parse_simdjson_value(VM& vm, simdjson::ondemand:
auto parsed = TRY(parse_simdjson_string(vm, value)); auto parsed = TRY(parse_simdjson_string(vm, value));
if (record) { if (record) {
record->value = parsed; record->value = parsed;
record->source = json_token_source(token); record->source = json_token_source(json_text, token);
} }
return parsed; return parsed;
} }
case simdjson::ondemand::json_type::array: case simdjson::ondemand::json_type::array:
return parse_simdjson_array(vm, value, record); return parse_simdjson_array(vm, json_text, value, record);
case simdjson::ondemand::json_type::object: case simdjson::ondemand::json_type::object:
return parse_simdjson_object(vm, value, record); return parse_simdjson_object(vm, json_text, value, record);
case simdjson::ondemand::json_type::unknown: case simdjson::ondemand::json_type::unknown:
return vm.throw_completion<SyntaxError>(ErrorType::JsonMalformed); return vm.throw_completion<SyntaxError>(ErrorType::JsonMalformed);
} }
@ -796,7 +898,7 @@ static ThrowCompletionOr<Value> parse_simdjson_value(VM& vm, simdjson::ondemand:
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} }
static ThrowCompletionOr<Value> parse_simdjson_document(VM& vm, simdjson::ondemand::document& document, JSONParseRecord* record = nullptr) static ThrowCompletionOr<Value> parse_simdjson_document(VM& vm, JSONTextBytes const& json_text, simdjson::ondemand::document& document, JSONParseRecord* record = nullptr)
{ {
simdjson::ondemand::json_type type; simdjson::ondemand::json_type type;
if (document.type().get(type)) if (document.type().get(type))
@ -819,7 +921,7 @@ static ThrowCompletionOr<Value> parse_simdjson_document(VM& vm, simdjson::ondema
if (document.raw_json_token().get(null_token)) if (document.raw_json_token().get(null_token))
return vm.throw_completion<SyntaxError>(ErrorType::JsonMalformed); return vm.throw_completion<SyntaxError>(ErrorType::JsonMalformed);
record->value = js_null(); record->value = js_null();
record->source = json_token_source(null_token); record->source = json_token_source(json_text, null_token);
} }
return js_null(); return js_null();
} }
@ -831,7 +933,7 @@ static ThrowCompletionOr<Value> parse_simdjson_document(VM& vm, simdjson::ondema
return vm.throw_completion<SyntaxError>(ErrorType::JsonMalformed); return vm.throw_completion<SyntaxError>(ErrorType::JsonMalformed);
if (record) { if (record) {
record->value = Value(boolean_value); record->value = Value(boolean_value);
record->source = json_token_source(raw_token); record->source = json_token_source(json_text, raw_token);
} }
return Value(boolean_value); return Value(boolean_value);
} }
@ -841,7 +943,7 @@ static ThrowCompletionOr<Value> parse_simdjson_document(VM& vm, simdjson::ondema
auto parsed = TRY(parse_simdjson_number(vm, document, trimmed)); auto parsed = TRY(parse_simdjson_number(vm, document, trimmed));
if (record) { if (record) {
record->value = parsed; record->value = parsed;
record->source = json_token_source(raw_token); record->source = json_token_source(json_text, raw_token);
} }
return parsed; return parsed;
} }
@ -852,14 +954,14 @@ static ThrowCompletionOr<Value> parse_simdjson_document(VM& vm, simdjson::ondema
auto parsed = TRY(parse_simdjson_string(vm, document)); auto parsed = TRY(parse_simdjson_string(vm, document));
if (record) { if (record) {
record->value = parsed; record->value = parsed;
record->source = json_token_source(string_token); record->source = json_token_source(json_text, string_token);
} }
return parsed; return parsed;
} }
case simdjson::ondemand::json_type::array: case simdjson::ondemand::json_type::array:
return parse_simdjson_array(vm, document, record); return parse_simdjson_array(vm, json_text, document, record);
case simdjson::ondemand::json_type::object: case simdjson::ondemand::json_type::object:
return parse_simdjson_object(vm, document, record); return parse_simdjson_object(vm, json_text, document, record);
case simdjson::ondemand::json_type::unknown: case simdjson::ondemand::json_type::unknown:
return vm.throw_completion<SyntaxError>(ErrorType::JsonMalformed); return vm.throw_completion<SyntaxError>(ErrorType::JsonMalformed);
} }
@ -867,15 +969,6 @@ static ThrowCompletionOr<Value> parse_simdjson_document(VM& vm, simdjson::ondema
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} }
static StringView utf8_json_text_bytes(Utf16View text, Optional<String>& utf8_text)
{
if (text.has_ascii_storage())
return { text.bytes() };
utf8_text = MUST(text.to_utf8());
return utf8_text->bytes_as_string_view();
}
// 25.5.1.1 ParseJSON ( text ), https://tc39.es/ecma262/#sec-ParseJSON // 25.5.1.1 ParseJSON ( text ), https://tc39.es/ecma262/#sec-ParseJSON
ThrowCompletionOr<Value> JSONObject::parse_json(VM& vm, Utf16View text, JSONParseRecord* root_record) ThrowCompletionOr<Value> JSONObject::parse_json(VM& vm, Utf16View text, JSONParseRecord* root_record)
{ {
@ -884,11 +977,12 @@ ThrowCompletionOr<Value> JSONObject::parse_json(VM& vm, Utf16View text, JSONPars
if (text.length_in_code_units() >= 1 && text.code_unit_at(0) == 0xFEFF) if (text.length_in_code_units() >= 1 && text.code_unit_at(0) == 0xFEFF)
return vm.throw_completion<SyntaxError>(ErrorType::JsonMalformed); return vm.throw_completion<SyntaxError>(ErrorType::JsonMalformed);
Optional<String> utf8_text; auto json_text = json_text_bytes(text).release_value_but_fixme_should_propagate_errors();
auto text_bytes = utf8_json_text_bytes(text, utf8_text); auto text_bytes = json_text.bytes();
simdjson::ondemand::parser parser; simdjson::ondemand::parser parser;
simdjson::padded_string padded(text_bytes.characters_without_null_termination(), text_bytes.length()); simdjson::padded_string padded(text_bytes.characters_without_null_termination(), text_bytes.length());
json_text.parser_bytes_data = padded.data();
simdjson::ondemand::document document; simdjson::ondemand::document document;
if (parser.iterate(padded).get(document)) if (parser.iterate(padded).get(document))
@ -899,7 +993,7 @@ ThrowCompletionOr<Value> JSONObject::parse_json(VM& vm, Utf16View text, JSONPars
// 4. NOTE: The early error rules defined in 13.2.5.1 have special handling for the above invocation of ParseText. // 4. NOTE: The early error rules defined in 13.2.5.1 have special handling for the above invocation of ParseText.
// 5. Assert: script is a Parse Node. // 5. Assert: script is a Parse Node.
// 6. Let result be ! Evaluation of script. // 6. Let result be ! Evaluation of script.
auto result = TRY(parse_simdjson_document(vm, document, root_record)); auto result = TRY(parse_simdjson_document(vm, json_text, document, root_record));
// 7. NOTE: The PropertyDefinitionEvaluation semantics defined in 13.2.5.5 have special handling for the above evaluation. // 7. NOTE: The PropertyDefinitionEvaluation semantics defined in 13.2.5.5 have special handling for the above evaluation.
// 8. Assert: result is either a String, a Number, a Boolean, an Object that is defined by either an ArrayLiteral or an ObjectLiteral, or null. // 8. Assert: result is either a String, a Number, a Boolean, an Object that is defined by either an ArrayLiteral or an ObjectLiteral, or null.
@ -994,7 +1088,7 @@ ThrowCompletionOr<Value> JSONObject::internalize_json_property(VM& vm, Object* h
} }
// 6. Return ? Call(reviver, holder, « name, value, context »). // 6. Return ? Call(reviver, holder, « name, value, context »).
return TRY(call(vm, reviver, holder, PrimitiveString::create(vm, name.to_string()), value, context)); return TRY(call(vm, reviver, holder, PrimitiveString::create(vm, name.to_utf16_string()), value, context));
} }
// 1.3 JSON.rawJSON ( text ), https://tc39.es/proposal-json-parse-with-source/#sec-json.rawjson // 1.3 JSON.rawJSON ( text ), https://tc39.es/proposal-json-parse-with-source/#sec-json.rawjson
@ -1022,8 +1116,8 @@ JS_DEFINE_NATIVE_FUNCTION(JSONObject::raw_json)
// 3. Parse StringToCodePoints(jsonString) as a JSON text as specified in ECMA-404. Throw a SyntaxError exception // 3. Parse StringToCodePoints(jsonString) as a JSON text as specified in ECMA-404. Throw a SyntaxError exception
// if it is not a valid JSON text as defined in that specification, or if its outermost value is an object or // if it is not a valid JSON text as defined in that specification, or if its outermost value is an object or
// array as defined in that specification. // array as defined in that specification.
Optional<String> utf8_text; auto json_text = json_text_bytes(json_string_view).release_value_but_fixme_should_propagate_errors();
auto text_bytes = utf8_json_text_bytes(json_string_view, utf8_text); auto text_bytes = json_text.bytes();
simdjson::ondemand::parser parser; simdjson::ondemand::parser parser;
simdjson::padded_string padded(text_bytes.characters_without_null_termination(), text_bytes.length()); simdjson::padded_string padded(text_bytes.characters_without_null_termination(), text_bytes.length());

View file

@ -6,8 +6,8 @@
#pragma once #pragma once
#include <AK/Utf16StringBuilder.h>
#include <AK/Utf16String.h> #include <AK/Utf16String.h>
#include <AK/Utf16StringBuilder.h>
#include <AK/Utf16View.h> #include <AK/Utf16View.h>
#include <LibJS/Export.h> #include <LibJS/Export.h>
#include <LibJS/Runtime/Object.h> #include <LibJS/Runtime/Object.h>

View file

@ -71,7 +71,7 @@ async function fromAsync(asyncItems, mapper, thisArg) {
// c. Let k be 0. // c. Let k be 0.
// d. Repeat, // d. Repeat,
for (let k = 0; ; ++k) { for (let k = 0; ; ++k) {
// i. If k 2**53 - 1, then // i. If k >= 2**53 - 1, then
if (k >= MAX_ARRAY_LIKE_INDEX) { if (k >= MAX_ARRAY_LIKE_INDEX) {
// 1. Let error be ThrowCompletion(a newly created TypeError object). // 1. Let error be ThrowCompletion(a newly created TypeError object).
const error = NewTypeError("Maximum array size exceeded"); const error = NewTypeError("Maximum array size exceeded");
@ -80,7 +80,7 @@ async function fromAsync(asyncItems, mapper, thisArg) {
return AsyncIteratorClose(iteratorRecord, error, true); return AsyncIteratorClose(iteratorRecord, error, true);
} }
// ii. Let Pk be ! ToString(𝔽(k)). // ii. Let Pk be ! ToString(F(k)).
// iii. Let nextResult be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]]). // iii. Let nextResult be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]]).
// iv. Set nextResult to ? Await(nextResult). // iv. Set nextResult to ? Await(nextResult).
const nextResult = await Call(iteratorRecord.nextMethod, iteratorRecord.iterator); const nextResult = await Call(iteratorRecord.nextMethod, iteratorRecord.iterator);
@ -93,7 +93,7 @@ async function fromAsync(asyncItems, mapper, thisArg) {
// vii. If done is true, then // vii. If done is true, then
if (done) { if (done) {
// 1. Perform ? Set(A, "length", 𝔽(k), true). // 1. Perform ? Set(A, "length", F(k), true).
array.length = k; array.length = k;
// 2. Return A. // 2. Return A.
@ -108,7 +108,7 @@ async function fromAsync(asyncItems, mapper, thisArg) {
// ix. If mapping is true, then // ix. If mapping is true, then
if (mapping) { if (mapping) {
// 1. Let mappedValue be Completion(Call(mapper, thisArg, « nextValue, 𝔽(k) »)). // 1. Let mappedValue be Completion(Call(mapper, thisArg, nextValue, F(k))).
// 2. IfAbruptCloseAsyncIterator(mappedValue, iteratorRecord). // 2. IfAbruptCloseAsyncIterator(mappedValue, iteratorRecord).
// 3. Set mappedValue to Completion(Await(mappedValue)). // 3. Set mappedValue to Completion(Await(mappedValue)).
// 4. IfAbruptCloseAsyncIterator(mappedValue, iteratorRecord). // 4. IfAbruptCloseAsyncIterator(mappedValue, iteratorRecord).
@ -143,7 +143,7 @@ async function fromAsync(asyncItems, mapper, thisArg) {
// d. If IsConstructor(C) is true, then // d. If IsConstructor(C) is true, then
if (IsConstructor(constructor)) { if (IsConstructor(constructor)) {
// i. Let A be ? Construct(C, « 𝔽(len) »). // i. Let A be ? Construct(C, F(len)).
array = new constructor(length); array = new constructor(length);
} }
// e. Else, // e. Else,
@ -155,7 +155,7 @@ async function fromAsync(asyncItems, mapper, thisArg) {
// f. Let k be 0. // f. Let k be 0.
// g. Repeat, while k < len, // g. Repeat, while k < len,
for (let k = 0; k < length; ++k) { for (let k = 0; k < length; ++k) {
// i. Let Pk be ! ToString(𝔽(k)). // i. Let Pk be ! ToString(F(k)).
// ii. Let kValue be ? Get(arrayLike, Pk). // ii. Let kValue be ? Get(arrayLike, Pk).
// iii. Set kValue to ? Await(kValue). // iii. Set kValue to ? Await(kValue).
const kValue = await arrayLike[k]; const kValue = await arrayLike[k];
@ -164,7 +164,7 @@ async function fromAsync(asyncItems, mapper, thisArg) {
// iv. If mapping is true, then // iv. If mapping is true, then
if (mapping) { if (mapping) {
// 1. Let mappedValue be ? Call(mapper, thisArg, « kValue, 𝔽(k) »). // 1. Let mappedValue be ? Call(mapper, thisArg, kValue, F(k)).
// 2. Set mappedValue to ? Await(mappedValue). // 2. Set mappedValue to ? Await(mappedValue).
mappedValue = await Call(mapper, thisArg, kValue, k); mappedValue = await Call(mapper, thisArg, kValue, k);
} }
@ -180,7 +180,7 @@ async function fromAsync(asyncItems, mapper, thisArg) {
// vii. Set k to k + 1. // vii. Set k to k + 1.
} }
// h. Perform ? Set(A, "length", 𝔽(len), true). // h. Perform ? Set(A, "length", F(len), true).
array.length = length; array.length = length;
// i. Return A. // i. Return A.

View file

@ -75,7 +75,7 @@ ThrowCompletionOr<Optional<PropertyDescriptor>> ModuleNamespaceObject::internal_
// 2. Let exports be O.[[Exports]]. // 2. Let exports be O.[[Exports]].
// 3. If P is not an element of exports, return undefined. // 3. If P is not an element of exports, return undefined.
auto export_element = m_exports.find(property_key.to_string()); auto export_element = m_exports.find(property_key.to_utf16_string());
if (export_element.is_end()) if (export_element.is_end())
return Optional<PropertyDescriptor> {}; return Optional<PropertyDescriptor> {};
@ -133,7 +133,7 @@ ThrowCompletionOr<bool> ModuleNamespaceObject::internal_has_property(PropertyKey
// 2. Let exports be O.[[Exports]]. // 2. Let exports be O.[[Exports]].
// 3. If P is an element of exports, return true. // 3. If P is an element of exports, return true.
auto export_element = m_exports.find(property_key.to_string()); auto export_element = m_exports.find(property_key.to_utf16_string());
if (!export_element.is_end()) if (!export_element.is_end())
return true; return true;
@ -154,13 +154,13 @@ ThrowCompletionOr<Value> ModuleNamespaceObject::internal_get(PropertyKey const&
// 2. Let exports be O.[[Exports]]. // 2. Let exports be O.[[Exports]].
// 3. If P is not an element of exports, return undefined. // 3. If P is not an element of exports, return undefined.
auto export_element = m_exports.find(property_key.to_string()); auto export_element = m_exports.find(property_key.to_utf16_string());
if (export_element.is_end()) if (export_element.is_end())
return js_undefined(); return js_undefined();
// 4. Let m be O.[[Module]]. // 4. Let m be O.[[Module]].
// 5. Let binding be m.ResolveExport(P). // 5. Let binding be m.ResolveExport(P).
auto binding = m_module->resolve_export(vm, property_key.to_string()); auto binding = m_module->resolve_export(vm, property_key.to_utf16_string());
// 6. Assert: binding is a ResolvedBinding Record. // 6. Assert: binding is a ResolvedBinding Record.
VERIFY(binding.is_valid()); VERIFY(binding.is_valid());
@ -206,7 +206,7 @@ ThrowCompletionOr<bool> ModuleNamespaceObject::internal_delete(PropertyKey const
// 2. Let exports be O.[[Exports]]. // 2. Let exports be O.[[Exports]].
// 3. If P is an element of exports, return false. // 3. If P is an element of exports, return false.
auto export_element = m_exports.find(property_key.to_string()); auto export_element = m_exports.find(property_key.to_utf16_string());
if (!export_element.is_end()) if (!export_element.is_end())
return false; return false;

View file

@ -5,7 +5,6 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/ByteString.h>
#include <AK/NeverDestroyed.h> #include <AK/NeverDestroyed.h>
#include <AK/QuickSort.h> #include <AK/QuickSort.h>
#include <AK/TypeCasts.h> #include <AK/TypeCasts.h>

View file

@ -5,7 +5,6 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/ByteString.h>
#include <AK/Function.h> #include <AK/Function.h>
#include <LibJS/Runtime/AbstractOperations.h> #include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Accessor.h> #include <LibJS/Runtime/Accessor.h>

View file

@ -7,9 +7,9 @@
#pragma once #pragma once
#include <AK/ByteString.h>
#include <AK/Format.h> #include <AK/Format.h>
#include <AK/Types.h> #include <AK/Types.h>
#include <AK/Utf16String.h>
#include <AK/Vector.h> #include <AK/Vector.h>
namespace JS { namespace JS {
@ -76,14 +76,14 @@ static constexpr PropertyAttributes default_attributes = Attribute::Configurable
namespace AK { namespace AK {
template<> template<>
struct Formatter<JS::PropertyAttributes> : Formatter<StringView> { struct Formatter<JS::PropertyAttributes> : Formatter<FormatString> {
ErrorOr<void> format(FormatBuilder& builder, JS::PropertyAttributes const& property_attributes) ErrorOr<void> format(FormatBuilder& builder, JS::PropertyAttributes const& property_attributes)
{ {
Vector<ByteString> parts; Vector<Utf16String> parts;
parts.append(ByteString::formatted("[[Writable]]: {}", property_attributes.is_writable())); TRY(parts.try_append(Utf16String::formatted("[[Writable]]: {}", property_attributes.is_writable())));
parts.append(ByteString::formatted("[[Enumerable]]: {}", property_attributes.is_enumerable())); TRY(parts.try_append(Utf16String::formatted("[[Enumerable]]: {}", property_attributes.is_enumerable())));
parts.append(ByteString::formatted("[[Configurable]]: {}", property_attributes.is_configurable())); TRY(parts.try_append(Utf16String::formatted("[[Configurable]]: {}", property_attributes.is_configurable())));
return Formatter<StringView>::format(builder, ByteString::formatted("PropertyAttributes {{ {} }}", ByteString::join(", "sv, parts))); return Formatter<Utf16String> {}.format(builder, Utf16String::formatted("PropertyAttributes {{ {} }}", Utf16String::join(", "sv, parts)));
} }
}; };

View file

@ -153,7 +153,7 @@ public:
return Value { PrimitiveString::create_from_unsigned_integer(vm, as_number()) }; return Value { PrimitiveString::create_from_unsigned_integer(vm, as_number()) };
} }
Utf16String to_string() const Utf16String to_utf16_string() const
{ {
if (is_string()) if (is_string())
return as_string().to_utf16_string(); return as_string().to_utf16_string();
@ -239,7 +239,7 @@ struct Formatter<JS::PropertyKey> : Formatter<Utf16String> {
{ {
if (property_key.is_number()) if (property_key.is_number())
return builder.put_u64(property_key.as_number()); return builder.put_u64(property_key.as_number());
return Formatter<Utf16String> {}.format(builder, property_key.to_string()); return Formatter<Utf16String> {}.format(builder, property_key.to_utf16_string());
} }
}; };

View file

@ -79,7 +79,7 @@ Completion Reference::throw_reference_error(VM& vm) const
if (is_private_reference()) if (is_private_reference())
return vm.throw_completion<ReferenceError>(ErrorType::ReferenceUnresolvable); return vm.throw_completion<ReferenceError>(ErrorType::ReferenceUnresolvable);
else else
return vm.throw_completion<ReferenceError>(ErrorType::UnknownIdentifier, name().to_string()); return vm.throw_completion<ReferenceError>(ErrorType::UnknownIdentifier, name().to_utf16_string());
} }
// 6.2.4.5 GetValue ( V ), https://tc39.es/ecma262/#sec-getvalue // 6.2.4.5 GetValue ( V ), https://tc39.es/ecma262/#sec-getvalue

View file

@ -216,10 +216,10 @@ ThrowCompletionOr<void> set_legacy_regexp_static_property(VM& vm, RegExpConstruc
return vm.throw_completion<TypeError>(ErrorType::SetLegacyRegExpStaticPropertyThisValueMismatch); return vm.throw_completion<TypeError>(ErrorType::SetLegacyRegExpStaticPropertyThisValueMismatch);
// 3. Let strVal be ? ToString(val). // 3. Let strVal be ? ToString(val).
auto str_value = TRY(value.to_primitive_string(vm)); auto str_value = TRY(value.to_utf16_string(vm));
// 4. Set the value of the internal slot of C named internalSlotName to strVal. // 4. Set the value of the internal slot of C named internalSlotName to strVal.
(constructor.legacy_static_properties().*property_setter)(str_value); (constructor.legacy_static_properties().*property_setter)(PrimitiveString::create(vm, move(str_value)));
return {}; return {};
} }

View file

@ -10,6 +10,7 @@
#include <AK/EnumBits.h> #include <AK/EnumBits.h>
#include <AK/Optional.h> #include <AK/Optional.h>
#include <AK/Result.h> #include <AK/Result.h>
#include <AK/String.h>
#include <LibJS/Export.h> #include <LibJS/Export.h>
#include <LibJS/Runtime/Object.h> #include <LibJS/Runtime/Object.h>
#include <LibRegex/ECMAScriptRegex.h> #include <LibRegex/ECMAScriptRegex.h>

View file

@ -306,7 +306,7 @@ static ThrowCompletionOr<Value> regexp_builtin_exec(VM& vm, RegExpObject& regexp
// Named groups: find by linear scan (typically very few named groups). // Named groups: find by linear scan (typically very few named groups).
for (auto const& ng : named_groups) { for (auto const& ng : named_groups) {
if (ng.index == i) { if (ng.index == i) {
auto group_name = Utf16FlyString::from_utf8(ng.name); auto const& group_name = ng.name;
if (matched_group_names.contains(group_name)) { if (matched_group_names.contains(group_name)) {
// Name already matched with a non-undefined value; skip. // Name already matched with a non-undefined value; skip.
break; break;
@ -325,7 +325,7 @@ static ThrowCompletionOr<Value> regexp_builtin_exec(VM& vm, RegExpObject& regexp
groups = Object::create(realm, nullptr); groups = Object::create(realm, nullptr);
for (auto const& ng : named_groups) { for (auto const& ng : named_groups) {
auto group_name = Utf16FlyString::from_utf8(ng.name); auto const& group_name = ng.name;
auto value = original_groups.as_object().get_without_side_effects(group_name); auto value = original_groups.as_object().get_without_side_effects(group_name);
MUST(groups.as_object().create_data_property_or_throw(group_name, value)); MUST(groups.as_object().create_data_property_or_throw(group_name, value));
} }
@ -377,7 +377,7 @@ static ThrowCompletionOr<Value> regexp_builtin_exec(VM& vm, RegExpObject& regexp
if (has_groups) { if (has_groups) {
HashTable<Utf16FlyString> matched_index_group_names; HashTable<Utf16FlyString> matched_index_group_names;
for (auto const& ng : named_groups) { for (auto const& ng : named_groups) {
auto group_name = Utf16FlyString::from_utf8(ng.name); auto const& group_name = ng.name;
if (matched_index_group_names.contains(group_name)) if (matched_index_group_names.contains(group_name))
continue; continue;
unsigned int group_idx = ng.index; unsigned int group_idx = ng.index;
@ -906,10 +906,10 @@ ThrowCompletionOr<Value> RegExpPrototype::symbol_replace_impl(VM& vm, Object& re
// 5. Let functionalReplace be IsCallable(replaceValue). // 5. Let functionalReplace be IsCallable(replaceValue).
// 6. If functionalReplace is false, then // 6. If functionalReplace is false, then
Optional<Utf16String> replace_string;
if (!replace_value.is_function()) { if (!replace_value.is_function()) {
// a. Set replaceValue to ? ToString(replaceValue). // a. Set replaceValue to ? ToString(replaceValue).
auto replace_string = TRY(replace_value.to_utf16_string(vm)); 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")). // 7. Let flags be ? ToString(? Get(rx, "flags")).
@ -1053,7 +1053,8 @@ ThrowCompletionOr<Value> RegExpPrototype::symbol_replace_impl(VM& vm, Object& re
} }
// ii. Let replacement be ? GetSubstitution(matched, S, position, captures, namedCaptures, replaceValue). // ii. Let replacement be ? GetSubstitution(matched, S, position, captures, namedCaptures, replaceValue).
replacement = TRY(get_substitution(vm, matched->utf16_string_view(), string->utf16_string_view(), position, captures, named_captures, replace_value)); VERIFY(replace_string.has_value());
replacement = TRY(get_substitution(vm, matched->utf16_string_view(), string->utf16_string_view(), position, captures, named_captures, *replace_string));
} }
// m. If position ≥ nextSourcePosition, then // m. If position ≥ nextSourcePosition, then

View file

@ -79,10 +79,10 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpStringIteratorPrototype::next)
} }
// 12. Let matchStr be ? ToString(? Get(match, "0")). // 12. Let matchStr be ? ToString(? Get(match, "0")).
auto match_string = TRY(TRY(match.get(vm, 0)).to_primitive_string(vm)); auto match_string = TRY(TRY(match.get(vm, 0)).to_utf16_string(vm));
// 13. If matchStr is the empty String, then // 13. If matchStr is the empty String, then
if (match_string->is_empty()) { if (match_string.is_empty()) {
// a. Let thisIndex be (? ToLength(? Get(R, "lastIndex"))). // a. Let thisIndex be (? ToLength(? Get(R, "lastIndex"))).
auto this_index = TRY(TRY(regexp.get(vm.names.lastIndex)).to_length(vm)); auto this_index = TRY(TRY(regexp.get(vm.names.lastIndex)).to_length(vm));

View file

@ -363,7 +363,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::ends_with)
return vm.throw_completion<TypeError>(ErrorType::IsNotA, "searchString", "string, but a regular expression"); return vm.throw_completion<TypeError>(ErrorType::IsNotA, "searchString", "string, but a regular expression");
// 5. Let searchStr be ? ToString(searchString). // 5. Let searchStr be ? ToString(searchString).
auto search_string = TRY(search_string_value.to_primitive_string(vm)); auto search_string = TRY(search_string_value.to_utf16_string(vm));
// 6. Let len be the length of S. // 6. Let len be the length of S.
auto string_length = string->length_in_utf16_code_units(); auto string_length = string->length_in_utf16_code_units();
@ -378,7 +378,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::ends_with)
} }
// 9. Let searchLength be the length of searchStr. // 9. Let searchLength be the length of searchStr.
auto search_length = search_string->length_in_utf16_code_units(); auto search_length = search_string.length_in_code_units();
// 10. If searchLength = 0, return true. // 10. If searchLength = 0, return true.
if (search_length == 0) if (search_length == 0)
@ -396,7 +396,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::ends_with)
// 14. If substring is searchStr, return true. // 14. If substring is searchStr, return true.
// 15. Return false. // 15. Return false.
return Value(substring_view == search_string->utf16_string_view()); return Value(substring_view == search_string);
} }
// 22.1.3.8 String.prototype.includes ( searchString [ , position ] ), https://tc39.es/ecma262/#sec-string.prototype.includes // 22.1.3.8 String.prototype.includes ( searchString [ , position ] ), https://tc39.es/ecma262/#sec-string.prototype.includes
@ -417,7 +417,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::includes)
return vm.throw_completion<TypeError>(ErrorType::IsNotA, "searchString", "string, but a regular expression"); return vm.throw_completion<TypeError>(ErrorType::IsNotA, "searchString", "string, but a regular expression");
// 5. Let searchStr be ? ToString(searchString). // 5. Let searchStr be ? ToString(searchString).
auto search_string = TRY(search_string_value.to_primitive_string(vm)); auto search_string = TRY(search_string_value.to_utf16_string(vm));
size_t start = 0; size_t start = 0;
if (!position.is_undefined()) { if (!position.is_undefined()) {
@ -431,7 +431,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::includes)
} }
// 10. Let index be StringIndexOf(S, searchStr, start). // 10. Let index be StringIndexOf(S, searchStr, start).
auto index = string_index_of(string->utf16_string_view(), search_string->utf16_string_view(), start); auto index = string_index_of(string->utf16_string_view(), search_string, start);
// 11. If index ≠ -1, return true. // 11. If index ≠ -1, return true.
// 12. Return false. // 12. Return false.
@ -446,10 +446,10 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::index_of)
auto string = TRY(primitive_string_from(vm)); auto string = TRY(primitive_string_from(vm));
// 3. Let searchStr be ? ToString(searchString). // 3. Let searchStr be ? ToString(searchString).
auto search_string = TRY(vm.argument(0).to_primitive_string(vm)); auto search_string = TRY(vm.argument(0).to_utf16_string(vm));
auto utf16_string_view = string->utf16_string_view(); auto utf16_string_view = string->utf16_string_view();
auto utf16_search_view = search_string->utf16_string_view(); auto utf16_search_view = search_string.utf16_view();
size_t start = 0; size_t start = 0;
if (vm.argument_count() > 1) { if (vm.argument_count() > 1) {
@ -487,7 +487,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::last_index_of)
auto string = TRY(primitive_string_from(vm)); auto string = TRY(primitive_string_from(vm));
// 4. Let searchStr be ? ToString(searchString). // 4. Let searchStr be ? ToString(searchString).
auto search_string = TRY(vm.argument(0).to_primitive_string(vm)); auto search_string = TRY(vm.argument(0).to_utf16_string(vm));
// 5. Let numPos be ? ToNumber(position). // 5. Let numPos be ? ToNumber(position).
// 6. Assert: If position is undefined, then numPos is NaN. // 6. Assert: If position is undefined, then numPos is NaN.
@ -500,7 +500,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::last_index_of)
auto string_length = string->length_in_utf16_code_units(); auto string_length = string->length_in_utf16_code_units();
// 9. Let searchLen be the length of searchStr. // 9. Let searchLen be the length of searchStr.
auto search_length = search_string->length_in_utf16_code_units(); auto search_length = search_string.length_in_code_units();
// 10. If len < searchLen, return -1𝔽. // 10. If len < searchLen, return -1𝔽.
if (string_length < search_length) if (string_length < search_length)
@ -510,7 +510,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::last_index_of)
size_t start = clamp(pos, static_cast<double>(0), static_cast<double>(string_length - search_length)); size_t start = clamp(pos, static_cast<double>(0), static_cast<double>(string_length - search_length));
// 12. Let result be StringLastIndexOf(S, searchStr, start). // 12. Let result be StringLastIndexOf(S, searchStr, start).
auto result = string_last_index_of(string->utf16_string_view(), search_string->utf16_string_view(), start); auto result = string_last_index_of(string->utf16_string_view(), search_string, start);
// 13. If result is NOT-FOUND, return -1𝔽. // 13. If result is NOT-FOUND, return -1𝔽.
if (!result.has_value()) if (!result.has_value())
@ -874,10 +874,10 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::replace)
// 5. Let functionalReplace be IsCallable(replaceValue). // 5. Let functionalReplace be IsCallable(replaceValue).
// 6. If functionalReplace is false, then // 6. If functionalReplace is false, then
Optional<Utf16String> replace_string;
if (!replace_value.is_function()) { if (!replace_value.is_function()) {
// a. Set replaceValue to ? ToString(replaceValue). // a. Set replaceValue to ? ToString(replaceValue).
auto replace_string = TRY(replace_value.to_primitive_string(vm)); replace_string = TRY(replace_value.to_utf16_string(vm));
replace_value = replace_string;
} }
// 7. Let searchLength be the length of searchString. // 7. Let searchLength be the length of searchString.
@ -906,13 +906,13 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::replace)
// 13. Else, // 13. Else,
else { else {
// a. Assert: replaceValue is a String. // a. Assert: replaceValue is a String.
VERIFY(replace_value.is_string()); VERIFY(replace_string.has_value());
// b. Let captures be a new empty List. // b. Let captures be a new empty List.
Span<Value> captures; Span<Value> captures;
// c. Let replacement be ! GetSubstitution(searchString, string, position, captures, undefined, replaceValue). // c. Let replacement be ! GetSubstitution(searchString, string, position, captures, undefined, replaceValue).
replacement = TRY(get_substitution(vm, search_string->utf16_string_view(), string->utf16_string_view(), *position, captures, js_undefined(), replace_value)); replacement = TRY(get_substitution(vm, search_string->utf16_string_view(), string->utf16_string_view(), *position, captures, js_undefined(), *replace_string));
} }
// 14. Return the string-concatenation of preceding, replacement, and following. // 14. Return the string-concatenation of preceding, replacement, and following.
@ -976,10 +976,10 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::replace_all)
// 5. Let functionalReplace be IsCallable(replaceValue). // 5. Let functionalReplace be IsCallable(replaceValue).
// 6. If functionalReplace is false, then // 6. If functionalReplace is false, then
Optional<Utf16String> replace_string;
if (!replace_value.is_function()) { if (!replace_value.is_function()) {
// a. Set replaceValue to ? ToString(replaceValue). // a. Set replaceValue to ? ToString(replaceValue).
auto replace_string = TRY(replace_value.to_primitive_string(vm)); replace_string = TRY(replace_value.to_utf16_string(vm));
replace_value = replace_string;
} }
// 7. Let searchLength be the length of searchString. // 7. Let searchLength be the length of searchString.
@ -1023,9 +1023,10 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::replace_all)
// c. Else, // c. Else,
else { else {
// i. Assert: replaceValue is a String. // i. Assert: replaceValue is a String.
VERIFY(replace_string.has_value());
// ii. Let captures be a new empty List. // ii. Let captures be a new empty List.
// iii. Let replacement be ! GetSubstitution(searchString, string, p, captures, undefined, replaceValue). // iii. Let replacement be ! GetSubstitution(searchString, string, p, captures, undefined, replaceValue).
replacement = TRY(get_substitution(vm, search_string->utf16_string_view(), string->utf16_string_view(), position, {}, js_undefined(), replace_value)); replacement = TRY(get_substitution(vm, search_string->utf16_string_view(), string->utf16_string_view(), position, {}, js_undefined(), *replace_string));
} }
// d. Set result to the string-concatenation of result, preserved, and replacement. // d. Set result to the string-concatenation of result, preserved, and replacement.
@ -1172,7 +1173,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::split)
limit = TRY(limit_argument.to_u32(vm)); limit = TRY(limit_argument.to_u32(vm));
// 6. Let separatorStr be ? ToString(separator). // 6. Let separatorStr be ? ToString(separator).
auto separator = TRY(separator_argument.to_primitive_string(vm)); auto separator = TRY(separator_argument.to_utf16_string(vm));
// 7. If lim = 0, then // 7. If lim = 0, then
if (limit == 0) { if (limit == 0) {
@ -1190,7 +1191,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::split)
} }
// 9. Let separatorLength be the length of separatorStr. // 9. Let separatorLength be the length of separatorStr.
auto separator_length = separator->length_in_utf16_code_units(); auto separator_length = separator.length_in_code_units();
// 10. If separatorLength = 0, then // 10. If separatorLength = 0, then
if (separator_length == 0) { if (separator_length == 0) {
@ -1213,7 +1214,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::split)
} }
auto string_view = string->utf16_string_view(); auto string_view = string->utf16_string_view();
auto separator_view = separator->utf16_string_view(); auto separator_view = separator.utf16_view();
// 13. Let searchStart be 0. // 13. Let searchStart be 0.
size_t search_start = 0; size_t search_start = 0;
@ -1265,7 +1266,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::starts_with)
return vm.throw_completion<TypeError>(ErrorType::IsNotA, "searchString", "string, but a regular expression"); return vm.throw_completion<TypeError>(ErrorType::IsNotA, "searchString", "string, but a regular expression");
// 5. Let searchStr be ? ToString(searchString). // 5. Let searchStr be ? ToString(searchString).
auto search_string = TRY(search_string_value.to_primitive_string(vm)); auto search_string = TRY(search_string_value.to_utf16_string(vm));
// 6. Let len be the length of S. // 6. Let len be the length of S.
auto string_length = string->length_in_utf16_code_units(); auto string_length = string->length_in_utf16_code_units();
@ -1281,7 +1282,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::starts_with)
} }
// 9. Let searchLength be the length of searchStr. // 9. Let searchLength be the length of searchStr.
auto search_length = search_string->length_in_utf16_code_units(); auto search_length = search_string.length_in_code_units();
// 10. If searchLength = 0, return true. // 10. If searchLength = 0, return true.
if (search_length == 0) if (search_length == 0)
@ -1299,7 +1300,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::starts_with)
// 14. If substring is searchStr, return true. // 14. If substring is searchStr, return true.
// 15. Return false. // 15. Return false.
return Value(substring_view == search_string->utf16_string_view()); return Value(substring_view == search_string);
} }
// 22.1.3.25 String.prototype.substring ( start, end ), https://tc39.es/ecma262/#sec-string.prototype.substring // 22.1.3.25 String.prototype.substring ( start, end ), https://tc39.es/ecma262/#sec-string.prototype.substring

View file

@ -92,7 +92,7 @@ Optional<Crypto::SignedBigInteger> get_named_time_zone_next_transition(Utf16View
.include_given_time = Unicode::TimeZoneTransition::Options::IncludeGivenTime::No, .include_given_time = Unicode::TimeZoneTransition::Options::IncludeGivenTime::No,
.transition_rule = Unicode::TimeZoneTransition::Options::TransitionRule::TransitionWhereUTCOffsetChanges, .transition_rule = Unicode::TimeZoneTransition::Options::TransitionRule::TransitionWhereUTCOffsetChanges,
}; };
auto time_zone_transition = Unicode::get_time_zone_transition(time_zone.bytes(), time, options); auto time_zone_transition = Unicode::get_time_zone_transition(time_zone, time, options);
if (!time_zone_transition.has_value()) if (!time_zone_transition.has_value())
return {}; return {};
@ -122,7 +122,7 @@ Optional<Crypto::SignedBigInteger> get_named_time_zone_previous_transition(Utf16
.include_given_time = has_sub_millisecond_precision ? Unicode::TimeZoneTransition::Options::IncludeGivenTime::Yes : Unicode::TimeZoneTransition::Options::IncludeGivenTime::No, .include_given_time = has_sub_millisecond_precision ? Unicode::TimeZoneTransition::Options::IncludeGivenTime::Yes : Unicode::TimeZoneTransition::Options::IncludeGivenTime::No,
.transition_rule = Unicode::TimeZoneTransition::Options::TransitionRule::TransitionWhereUTCOffsetChanges, .transition_rule = Unicode::TimeZoneTransition::Options::TransitionRule::TransitionWhereUTCOffsetChanges,
}; };
auto time_zone_transition = Unicode::get_time_zone_transition(time_zone.bytes(), time, options); auto time_zone_transition = Unicode::get_time_zone_transition(time_zone, time, options);
if (!time_zone_transition.has_value()) if (!time_zone_transition.has_value())
return {}; return {};
@ -254,7 +254,7 @@ i64 get_offset_nanoseconds_for(Utf16View time_zone, Crypto::SignedBigInteger con
return *parse_result.offset_minutes * 60'000'000'000; return *parse_result.offset_minutes * 60'000'000'000;
// 3. Return GetNamedTimeZoneOffsetNanoseconds(parseResult.[[Name]], epochNs). // 3. Return GetNamedTimeZoneOffsetNanoseconds(parseResult.[[Name]], epochNs).
return get_named_time_zone_offset_nanoseconds(parse_result.name->utf16_view().bytes(), epoch_nanoseconds).offset.to_nanoseconds(); return get_named_time_zone_offset_nanoseconds(parse_result.name->utf16_view(), epoch_nanoseconds).offset.to_nanoseconds();
} }
// 11.1.10 GetISODateTimeFor ( timeZone, epochNs ), https://tc39.es/proposal-temporal/#sec-temporal-getisodatetimefor // 11.1.10 GetISODateTimeFor ( timeZone, epochNs ), https://tc39.es/proposal-temporal/#sec-temporal-getisodatetimefor
@ -425,7 +425,7 @@ ThrowCompletionOr<Vector<Crypto::SignedBigInteger>> get_possible_epoch_nanosecon
// 3. Else, // 3. Else,
else { else {
// a. Let possibleEpochNanoseconds be GetNamedTimeZoneEpochNanoseconds(parseResult.[[Name]], isoDateTime). // a. Let possibleEpochNanoseconds be GetNamedTimeZoneEpochNanoseconds(parseResult.[[Name]], isoDateTime).
possible_epoch_nanoseconds = get_named_time_zone_epoch_nanoseconds(parse_result.name->utf16_view().bytes(), iso_date_time); possible_epoch_nanoseconds = get_named_time_zone_epoch_nanoseconds(parse_result.name->utf16_view(), iso_date_time);
} }
// 4. For each value epochNanoseconds in possibleEpochNanoseconds, do // 4. For each value epochNanoseconds in possibleEpochNanoseconds, do

View file

@ -85,6 +85,22 @@ static ThrowCompletionOr<AK::LastChunkHandling> parse_last_chunk_handling(VM& vm
return vm.throw_completion<TypeError>(ErrorType::OptionIsNotValidValue, last_chunk_handling, "lastChunkHandling"sv); return vm.throw_completion<TypeError>(ErrorType::OptionIsNotValidValue, last_chunk_handling, "lastChunkHandling"sv);
} }
static Utf16String base64_decode_error_message(AK::Base64DecodeError error)
{
switch (error) {
case AK::Base64DecodeError::ExtraBits:
return "Extra bits found at end of chunk"_utf16;
case AK::Base64DecodeError::InputRemainder:
return "Invalid trailing data"_utf16;
case AK::Base64DecodeError::InvalidCharacter:
return "Invalid base64 character"_utf16;
case AK::Base64DecodeError::InvalidData:
return "Invalid base64-encoded data"_utf16;
}
VERIFY_NOT_REACHED();
}
// 23.3.1.1 Uint8Array.fromBase64 ( string [ , options ] ), https://tc39.es/ecma262/#sec-uint8array.frombase64 // 23.3.1.1 Uint8Array.fromBase64 ( string [ , options ] ), https://tc39.es/ecma262/#sec-uint8array.frombase64
JS_DEFINE_NATIVE_FUNCTION(Uint8ArrayConstructorHelpers::from_base64) JS_DEFINE_NATIVE_FUNCTION(Uint8ArrayConstructorHelpers::from_base64)
{ {
@ -516,7 +532,7 @@ DecodeResult from_base64(VM& vm, Utf16View string, Alphabet alphabet, AK::LastCh
: AK::decode_base64url_into(string, output, last_chunk_handling); : AK::decode_base64url_into(string, output, last_chunk_handling);
if (result.is_error()) { if (result.is_error()) {
auto error = vm.throw_completion<SyntaxError>(result.error().error.string_literal()); auto error = vm.throw_completion<SyntaxError>(base64_decode_error_message(result.error().decode_error));
return { .read = result.error().valid_input_bytes, .bytes = move(output), .error = move(error) }; return { .read = result.error().valid_input_bytes, .bytes = move(output), .error = move(error) };
} }
@ -542,7 +558,7 @@ DecodeResult from_hex(VM& vm, Utf16View string, Optional<size_t> max_length)
// 5. If length modulo 2 ≠ 0, then // 5. If length modulo 2 ≠ 0, then
if (length % 2 != 0) { if (length % 2 != 0) {
// a. Let error be a newly created SyntaxError object. // a. Let error be a newly created SyntaxError object.
auto error = vm.throw_completion<SyntaxError>("Hex string must have an even length"sv); auto error = vm.throw_completion<SyntaxError>("Hex string must have an even length"_utf16);
// b. Return the Record { [[Read]]: read, [[Bytes]]: bytes, [[Error]]: error }. // b. Return the Record { [[Read]]: read, [[Bytes]]: bytes, [[Error]]: error }.
return { .read = read, .bytes = move(bytes), .error = move(error) }; return { .read = read, .bytes = move(bytes), .error = move(error) };
@ -561,7 +577,7 @@ DecodeResult from_hex(VM& vm, Utf16View string, Optional<size_t> max_length)
// b. If hexits contains any code units which are not in "0123456789abcdefABCDEF", then // b. If hexits contains any code units which are not in "0123456789abcdefABCDEF", then
if (!byte.has_value()) { if (!byte.has_value()) {
// i. Let error be a newly created SyntaxError object. // i. Let error be a newly created SyntaxError object.
auto error = vm.throw_completion<SyntaxError>("Hex string must only contain hex characters"sv); auto error = vm.throw_completion<SyntaxError>("Hex string must only contain hex characters"_utf16);
// ii. Return the Record { [[Read]]: read, [[Bytes]]: bytes, [[Error]]: error }. // ii. Return the Record { [[Read]]: read, [[Bytes]]: bytes, [[Error]]: error }.
return { .read = read, .bytes = move(bytes), .error = move(error) }; return { .read = read, .bytes = move(bytes), .error = move(error) };

View file

@ -7,12 +7,14 @@
*/ */
#include <AK/Array.h> #include <AK/Array.h>
#include <AK/ByteBuffer.h>
#include <AK/Debug.h> #include <AK/Debug.h>
#include <AK/LexicalPath.h> #include <AK/LexicalPath.h>
#include <AK/ScopeGuard.h> #include <AK/ScopeGuard.h>
#include <AK/String.h> #include <AK/String.h>
#include <AK/StringBuilder.h> #include <AK/StringBuilder.h>
#include <AK/Time.h> #include <AK/Time.h>
#include <LibCore/ImmutableBytes.h>
#include <LibFileSystem/FileSystem.h> #include <LibFileSystem/FileSystem.h>
#include <LibGC/Heap.h> #include <LibGC/Heap.h>
#include <LibJS/Bytecode/Executable.h> #include <LibJS/Bytecode/Executable.h>
@ -33,8 +35,10 @@
#include <LibJS/Runtime/Symbol.h> #include <LibJS/Runtime/Symbol.h>
#include <LibJS/Runtime/Temporal/Instant.h> #include <LibJS/Runtime/Temporal/Instant.h>
#include <LibJS/Runtime/VM.h> #include <LibJS/Runtime/VM.h>
#include <LibJS/SourceCode.h>
#include <LibJS/SourceTextModule.h> #include <LibJS/SourceTextModule.h>
#include <LibJS/SyntheticModule.h> #include <LibJS/SyntheticModule.h>
#include <LibTextCodec/Decoder.h>
namespace JS { namespace JS {
@ -627,6 +631,17 @@ VM::StoredModule* VM::get_stored_module(ImportedModuleReferrer const&, ByteStrin
static ByteString resolve_module_filename(StringView filename, Utf16View const& module_type); static ByteString resolve_module_filename(StringView filename, Utf16View const& module_type);
static StringView utf8_path_view(Utf16View path, Optional<ByteBuffer>& utf8_storage)
{
if (path.has_ascii_storage())
return { path.bytes() };
StringBuilder builder;
builder.append(path);
utf8_storage = MUST(builder.to_byte_buffer());
return { utf8_storage->bytes() };
}
ThrowCompletionOr<void> VM::link_and_eval_module(SourceTextModule& module) ThrowCompletionOr<void> VM::link_and_eval_module(SourceTextModule& module)
{ {
return link_and_eval_module(static_cast<CyclicModule&>(module)); return link_and_eval_module(static_cast<CyclicModule&>(module));
@ -759,7 +774,9 @@ void VM::load_imported_module(ImportedModuleReferrer referrer, ModuleRequest con
}); });
LexicalPath base_path { base_filename }; LexicalPath base_path { base_filename };
auto filename = LexicalPath::absolute_path(base_path.dirname(), MUST(module_request.module_specifier.view().to_byte_string())); Optional<ByteBuffer> module_specifier_utf8_storage;
auto module_specifier_path = utf8_path_view(module_request.module_specifier.view(), module_specifier_utf8_storage);
auto filename = LexicalPath::absolute_path(base_path.dirname(), module_specifier_path);
dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] base path: '{}'", base_path); dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] base path: '{}'", base_path);
dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] initial filename: '{}'", filename); dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] initial filename: '{}'", filename);
@ -769,18 +786,17 @@ void VM::load_imported_module(ImportedModuleReferrer referrer, ModuleRequest con
dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] resolved filename: '{}'", filename); dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] resolved filename: '{}'", filename);
#if JS_MODULE_DEBUG #if JS_MODULE_DEBUG
ByteString referencing_module_string = referrer.visit( referrer.visit(
[&](GC::Ref<Script> const& script) { [&](GC::Ref<Script> const& script) {
return ByteString::formatted("Script @ {}", script.ptr()); dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] load_imported_module(Script @ {}, {})", script.ptr(), filename);
}, },
[&](GC::Ref<CyclicModule> const& module) { [&](GC::Ref<CyclicModule> const& module) {
return ByteString::formatted("Module @ {}", module.ptr()); dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] load_imported_module(Module @ {}, {})", module.ptr(), filename);
}, },
[&](GC::Ref<Realm> const& realm) { [&](GC::Ref<Realm> const& realm) {
return ByteString::formatted("Realm @ {}", realm.ptr()); dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] load_imported_module(Realm @ {}, {})", realm.ptr(), filename);
}); });
dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] load_imported_module({}, {})", referencing_module_string, filename);
dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] resolved {} + {} -> {}", base_path, module_request.module_specifier, filename); dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] resolved {} + {} -> {}", base_path, module_request.module_specifier, filename);
#endif #endif
@ -811,20 +827,25 @@ void VM::load_imported_module(ImportedModuleReferrer referrer, ModuleRequest con
return; return;
} }
StringView const content_view { file_content_or_error.value().bytes() }; auto source_bytes = Core::ImmutableBytes::adopt(file_content_or_error.release_value());
auto decoder = TextCodec::decoder_for("UTF-8"sv);
VERIFY(decoder.has_value());
auto source_length = TextCodec::convert_input_to_utf16_length_using_given_decoder_unless_there_is_a_byte_order_mark(*decoder, StringView { source_bytes.bytes() }).release_value_but_fixme_should_propagate_errors();
auto display_filename = Utf16String::from_utf8(filename);
auto source_code = SourceCode::create(move(display_filename), source_length, "UTF-8"sv, move(source_bytes));
auto module = [&, content = file_content_or_error.release_value()]() -> ThrowCompletionOr<GC::Ref<Module>> { auto module = [&, source_code = move(source_code)]() mutable -> ThrowCompletionOr<GC::Ref<Module>> {
// If moduleRequest.[[Attributes]] has an entry entry such that entry.[[Key]] is "type" and entry.[[Value]] is "json", // If moduleRequest.[[Attributes]] has an entry entry such that entry.[[Key]] is "type" and entry.[[Value]] is "json",
// when the host environment performs FinishLoadingImportedModule(referrer, moduleRequest, payload, result), result // when the host environment performs FinishLoadingImportedModule(referrer, moduleRequest, payload, result), result
// must either be the Completion Record returned by an invocation of ParseJSONModule or a throw completion. // must either be the Completion Record returned by an invocation of ParseJSONModule or a throw completion.
if (module_type == "json"sv) { if (module_type == "json"sv) {
dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] reading and parsing JSON module {}", filename); dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] reading and parsing JSON module {}", filename);
return TRY(parse_json_module(*current_realm(), content_view, filename)); return TRY(parse_json_module(*current_realm(), source_code->code_view(), filename));
} }
dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] reading and parsing as SourceTextModule module {}", filename); dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] reading and parsing as SourceTextModule module {}", filename);
// Note: We treat all files as module, so if a script does not have exports it just runs it. // Note: We treat all files as module, so if a script does not have exports it just runs it.
auto module_or_errors = SourceTextModule::parse(content_view, *current_realm(), filename); auto module_or_errors = SourceTextModule::parse(move(source_code), *current_realm(), filename);
if (module_or_errors.is_error()) { if (module_or_errors.is_error()) {
VERIFY(module_or_errors.error().size() > 0); VERIFY(module_or_errors.error().size() > 0);

View file

@ -10,7 +10,6 @@
#include <AK/Assertions.h> #include <AK/Assertions.h>
#include <AK/BitCast.h> #include <AK/BitCast.h>
#include <AK/ByteString.h>
#include <AK/Format.h> #include <AK/Format.h>
#include <AK/Forward.h> #include <AK/Forward.h>
#include <AK/Function.h> #include <AK/Function.h>

View file

@ -1591,7 +1591,7 @@ pub enum ExpressionKind {
StringLiteral(Box<Utf16String>), StringLiteral(Box<Utf16String>),
BooleanLiteral(bool), BooleanLiteral(bool),
NullLiteral, NullLiteral,
BigIntLiteral(Box<String>), BigIntLiteral(Box<Utf16String>),
RegExpLiteral(Box<RegExpLiteralData>), RegExpLiteral(Box<RegExpLiteralData>),
// Identifiers // Identifiers

View file

@ -13,13 +13,6 @@ use crate::ast::*;
use std::cell::RefCell; use std::cell::RefCell;
use std::fmt::Write; use std::fmt::Write;
unsafe extern "C" {
// FIXME: This FFI workaround exists only to match C++ float-to-string
// formatting in the AST dump. Once the C++ pipeline is removed,
// this can be deleted and we can use our own formatting.
fn rust_format_double(value: f64, buffer: *mut u8, buffer_len: usize) -> usize;
}
/// Defines a function that maps enum variants to static string slices. /// Defines a function that maps enum variants to static string slices.
macro_rules! op_to_string { macro_rules! op_to_string {
($name:ident, $enum_type:ty, { $($variant:ident => $str:literal),+ $(,)? }) => { ($name:ident, $enum_type:ty, { $($variant:ident => $str:literal),+ $(,)? }) => {
@ -224,30 +217,9 @@ fn utf16_to_string(s: &[u16]) -> String {
.collect() .collect()
} }
/// Format f64 matching the C++ AK::Formatter<double> output exactly. /// Format f64 matching the C++ Number::toString output exactly.
///
/// FIXME: This calls into C++ via FFI to guarantee identical output.
/// Once the C++ pipeline is removed, this can be replaced with
/// a native implementation.
fn format_f64(value: f64) -> String { fn format_f64(value: f64) -> String {
// C++ AST dump formats JS::Value which uses to_utf16_string_without_side_effects(), utf16_to_string(&crate::bytecode::ffi::js_number_to_utf16(value))
// producing "Infinity"/"-Infinity"/"NaN". The rust_format_double FFI uses
// AK's double formatter which produces "inf"/"-inf"/"nan" instead.
if value.is_nan() {
return "NaN".to_string();
}
if value.is_infinite() {
return if value > 0.0 {
"Infinity".to_string()
} else {
"-Infinity".to_string()
};
}
let mut buffer = [0u8; 128];
let length = unsafe { rust_format_double(value, buffer.as_mut_ptr(), buffer.len()) };
std::str::from_utf8(&buffer[..length])
.expect("C++ produced invalid UTF-8")
.to_string()
} }
op_to_string!(binary_op_to_string, BinaryOp, { op_to_string!(binary_op_to_string, BinaryOp, {
@ -681,11 +653,12 @@ fn dump_expression(expression: &Expression, state: &DumpState) {
} }
ExpressionKind::BigIntLiteral(value) => { ExpressionKind::BigIntLiteral(value) => {
let value = utf16_to_string(&value.0);
dump_node!( dump_node!(
state, state,
"BigIntLiteral", "BigIntLiteral",
&expression.range, &expression.range,
color_number_str(state, value) color_number_str(state, &value)
); );
} }

View file

@ -238,9 +238,8 @@ fn generate_expression_inner(
ExpressionKind::StringLiteral(value) => Some(generator.add_constant_string((**value).clone())), ExpressionKind::StringLiteral(value) => Some(generator.add_constant_string((**value).clone())),
ExpressionKind::BigIntLiteral(value) => { ExpressionKind::BigIntLiteral(value) => {
// The AST stores the raw value including the 'n' suffix; strip it for codegen. let digits = bigint_literal_digits(value);
let digits = value.strip_suffix('n').unwrap_or(value.as_str()); Some(generator.add_constant_bigint(ascii_code_units_to_string(digits)?))
Some(generator.add_constant_bigint(digits.to_string()))
} }
ExpressionKind::RegExpLiteral(data) => { ExpressionKind::RegExpLiteral(data) => {
@ -6354,8 +6353,8 @@ fn generate_class_expression(
ExpressionKind::PrivateIdentifier(p) => p.name.clone(), ExpressionKind::PrivateIdentifier(p) => p.name.clone(),
ExpressionKind::NumericLiteral(n) => super::ffi::js_number_to_utf16(*n), ExpressionKind::NumericLiteral(n) => super::ffi::js_number_to_utf16(*n),
ExpressionKind::BigIntLiteral(s) => { ExpressionKind::BigIntLiteral(s) => {
let digits = s.strip_suffix('n').unwrap_or(s.as_str()); let digits = bigint_literal_digits(s);
Utf16String(digits.encode_utf16().collect()) Utf16String(digits.to_vec())
} }
_ => Utf16String::new(), _ => Utf16String::new(),
}; };
@ -9396,68 +9395,137 @@ impl NonDecimalRadix {
} }
} }
fn strip_non_decimal_prefix(text: &str) -> Option<(NonDecimalRadix, &str)> { fn is_utf16_whitespace(code_unit: u16) -> bool {
// Detect an ASCII non-decimal prefix without slicing at a UTF-8-invalid boundary. char::from_u32(code_unit.into()).is_some_and(char::is_whitespace)
// Empty suffixes ("0x", "0o", "0b") remain invalid and are rejected here.
// Callers validate suffix digits before delegating conversion to numeric parsers.
if let Some(rest) = text.strip_prefix("0b").or_else(|| text.strip_prefix("0B")) {
return (!rest.is_empty()).then_some((NonDecimalRadix::Binary, rest));
}
if let Some(rest) = text.strip_prefix("0o").or_else(|| text.strip_prefix("0O")) {
return (!rest.is_empty()).then_some((NonDecimalRadix::Octal, rest));
}
if let Some(rest) = text.strip_prefix("0x").or_else(|| text.strip_prefix("0X")) {
return (!rest.is_empty()).then_some((NonDecimalRadix::Hexadecimal, rest));
}
None
} }
fn is_valid_non_decimal_digits(text: &str, radix: NonDecimalRadix) -> bool { fn trim_utf16_whitespace(text: &[u16]) -> &[u16] {
let mut start = 0;
while start < text.len() && is_utf16_whitespace(text[start]) {
start += 1;
}
let mut end = text.len();
while end > start && is_utf16_whitespace(text[end - 1]) {
end -= 1;
}
&text[start..end]
}
fn strip_non_decimal_prefix_utf16(text: &[u16]) -> Option<(NonDecimalRadix, &[u16])> {
if text.len() < 3 || text[0] != b'0'.into() {
return None;
}
let radix = match text[1] {
code_unit if code_unit == b'b'.into() || code_unit == b'B'.into() => NonDecimalRadix::Binary,
code_unit if code_unit == b'o'.into() || code_unit == b'O'.into() => NonDecimalRadix::Octal,
code_unit if code_unit == b'x'.into() || code_unit == b'X'.into() => NonDecimalRadix::Hexadecimal,
_ => return None,
};
Some((radix, &text[2..]))
}
fn is_valid_non_decimal_digits_utf16(text: &[u16], radix: NonDecimalRadix) -> bool {
// Keep this JS-specific precheck even though parse_bytes() also validates: // Keep this JS-specific precheck even though parse_bytes() also validates:
// num-bigint accepts forms that are invalid in JS StringToNumber/StringToBigInt // num-bigint accepts forms that are invalid in JS StringToNumber/StringToBigInt
// non-decimal parsing (for example a leading '+' and '_' separators). // non-decimal parsing (for example a leading '+' and '_' separators).
// strip_non_decimal_prefix() guarantees that this suffix is non-empty. // strip_non_decimal_prefix_utf16() guarantees that this suffix is non-empty.
debug_assert!(!text.is_empty()); debug_assert!(!text.is_empty());
match radix { match radix {
NonDecimalRadix::Binary => text.bytes().all(|b| matches!(b, b'0' | b'1')), NonDecimalRadix::Binary => text
NonDecimalRadix::Octal => text.bytes().all(|b| matches!(b, b'0'..=b'7')), .iter()
NonDecimalRadix::Hexadecimal => text.bytes().all(|b| b.is_ascii_hexdigit()), .all(|&code_unit| code_unit == b'0'.into() || code_unit == b'1'.into()),
NonDecimalRadix::Octal => text
.iter()
.all(|&code_unit| (b'0'.into()..=b'7'.into()).contains(&code_unit)),
NonDecimalRadix::Hexadecimal => text.iter().all(|&code_unit| {
(b'0'.into()..=b'9'.into()).contains(&code_unit)
|| (b'a'.into()..=b'f'.into()).contains(&code_unit)
|| (b'A'.into()..=b'F'.into()).contains(&code_unit)
}),
} }
} }
fn ascii_code_units_to_bytes(text: &[u16]) -> Option<Vec<u8>> {
text.iter()
.map(|&code_unit| {
u8::try_from(code_unit)
.ok()
.and_then(|byte| byte.is_ascii().then_some(byte))
})
.collect()
}
fn ascii_code_units_to_string(text: &[u16]) -> Option<String> {
text.iter()
.map(|&code_unit| {
u8::try_from(code_unit)
.ok()
.and_then(|byte| byte.is_ascii().then_some(char::from(byte)))
})
.collect()
}
fn ascii_code_units_eq(text: &[u16], ascii: &[u8]) -> bool {
text.len() == ascii.len()
&& text
.iter()
.zip(ascii)
.all(|(&code_unit, &byte)| code_unit == byte.into())
}
fn is_ascii_decimal_number_code_unit(code_unit: u16) -> bool {
(b'0'.into()..=b'9'.into()).contains(&code_unit)
|| code_unit == b'.'.into()
|| code_unit == b'e'.into()
|| code_unit == b'E'.into()
|| code_unit == b'+'.into()
|| code_unit == b'-'.into()
}
fn bigint_literal_digits(literal: &Utf16String) -> &[u16] {
literal.0.strip_suffix(&[b'n'.into()]).unwrap_or(literal.0.as_slice())
}
/// Implements StringToBigInt per https://tc39.es/ecma262/#sec-stringtobigint. /// Implements StringToBigInt per https://tc39.es/ecma262/#sec-stringtobigint.
/// Trims whitespace, handles optional sign (decimal only), and handles /// Trims whitespace, handles optional sign (decimal only), and handles
/// 0b/0o/0x prefixes. Returns None if the string is not a valid /// 0b/0o/0x prefixes. Returns None if the string is not a valid
/// StringIntegerLiteral. /// StringIntegerLiteral.
fn string_to_bigint(s: &Utf16String) -> Option<BigInt> { fn string_to_bigint(s: &Utf16String) -> Option<BigInt> {
use num_bigint::BigInt; use num_bigint::BigInt;
let s_utf8: String = char::decode_utf16(s.0.iter().copied()) let s_trimmed = trim_utf16_whitespace(&s.0);
.map(|r| r.unwrap_or('\u{FFFD}'))
.collect();
let s_trimmed = s_utf8.trim();
if s_trimmed.is_empty() { if s_trimmed.is_empty() {
return Some(BigInt::from(0)); return Some(BigInt::from(0));
} }
// Check for non-decimal prefixes (no sign allowed). // Check for non-decimal prefixes (no sign allowed).
if let Some((radix, rest)) = strip_non_decimal_prefix(s_trimmed) { if let Some((radix, rest)) = strip_non_decimal_prefix_utf16(s_trimmed) {
if !is_valid_non_decimal_digits(rest, radix) { if !is_valid_non_decimal_digits_utf16(rest, radix) {
return None; return None;
} }
// Convert validated suffix digits using the parser. // Convert validated suffix digits using the parser.
return BigInt::parse_bytes(rest.as_bytes(), radix.as_u32()); let bytes = ascii_code_units_to_bytes(rest)?;
return BigInt::parse_bytes(&bytes, radix.as_u32());
} }
// Decimal with optional sign. Only allow digits (no dots, no exponents). // Decimal with optional sign. Only allow digits (no dots, no exponents).
let (is_negative, digits) = if let Some(rest) = s_trimmed.strip_prefix('-') { let (is_negative, digits) = if let Some(rest) = s_trimmed.strip_prefix(&[b'-'.into()]) {
(true, rest) (true, rest)
} else if let Some(rest) = s_trimmed.strip_prefix('+') { } else if let Some(rest) = s_trimmed.strip_prefix(&[b'+'.into()]) {
(false, rest) (false, rest)
} else { } else {
(false, s_trimmed) (false, s_trimmed)
}; };
if digits.is_empty() || !digits.bytes().all(|b| b.is_ascii_digit()) { if digits.is_empty()
|| !digits
.iter()
.all(|code_unit| (b'0'.into()..=b'9'.into()).contains(code_unit))
{
return None; return None;
} }
let bi = BigInt::parse_bytes(digits.as_bytes(), 10)?; let bytes = ascii_code_units_to_bytes(digits)?;
let bi = BigInt::parse_bytes(&bytes, 10)?;
Some(if is_negative { -bi } else { bi }) Some(if is_negative { -bi } else { bi })
} }
@ -9557,9 +9625,12 @@ fn js_exponentiate(base: f64, exponent: f64) -> f64 {
/// Parse a non-decimal integer string via BigInt to f64, matching /// Parse a non-decimal integer string via BigInt to f64, matching
/// UnsignedBigInteger::from_base() + to_double(). This avoids u64 overflow /// UnsignedBigInteger::from_base() + to_double(). This avoids u64 overflow
/// for large literals like 0x10000000000000000. /// for large literals like 0x10000000000000000.
fn bigint_string_to_f64(s: &str, radix: u32) -> f64 { fn bigint_digits_to_f64(digits: &[u16], radix: u32) -> f64 {
use num_bigint::BigUint; use num_bigint::BigUint;
match BigUint::parse_bytes(s.as_bytes(), radix) { let Some(bytes) = ascii_code_units_to_bytes(digits) else {
return f64::NAN;
};
match BigUint::parse_bytes(&bytes, radix) {
Some(n) => { Some(n) => {
use num_traits::ToPrimitive; use num_traits::ToPrimitive;
n.to_f64().unwrap_or(f64::INFINITY) n.to_f64().unwrap_or(f64::INFINITY)
@ -9570,33 +9641,29 @@ fn bigint_string_to_f64(s: &str, radix: u32) -> f64 {
// 7.1.4.1.1 StringToNumber ( str ), https://tc39.es/ecma262/#sec-stringtonumber // 7.1.4.1.1 StringToNumber ( str ), https://tc39.es/ecma262/#sec-stringtonumber
fn string_to_number(s: &Utf16String) -> f64 { fn string_to_number(s: &Utf16String) -> f64 {
let text: String = char::decode_utf16(s.0.iter().copied()) let trimmed = trim_utf16_whitespace(&s.0);
.map(|r| r.unwrap_or('\u{FFFD}'))
.collect();
let trimmed = text.trim();
if trimmed.is_empty() { if trimmed.is_empty() {
return 0.0; return 0.0;
} }
if trimmed == "Infinity" || trimmed == "+Infinity" { if ascii_code_units_eq(trimmed, b"Infinity") || ascii_code_units_eq(trimmed, b"+Infinity") {
return f64::INFINITY; return f64::INFINITY;
} }
if trimmed == "-Infinity" { if ascii_code_units_eq(trimmed, b"-Infinity") {
return f64::NEG_INFINITY; return f64::NEG_INFINITY;
} }
if let Some((radix, rest)) = strip_non_decimal_prefix(trimmed) { if let Some((radix, rest)) = strip_non_decimal_prefix_utf16(trimmed) {
if !is_valid_non_decimal_digits(rest, radix) { if !is_valid_non_decimal_digits_utf16(rest, radix) {
return f64::NAN; return f64::NAN;
} }
// Convert validated suffix digits using the parser. return bigint_digits_to_f64(rest, radix.as_u32());
return bigint_string_to_f64(rest, radix.as_u32());
} }
if !trimmed if !trimmed.iter().copied().all(is_ascii_decimal_number_code_unit) {
.bytes()
.all(|b| b.is_ascii_digit() || b == b'.' || b == b'e' || b == b'E' || b == b'+' || b == b'-')
{
return f64::NAN; return f64::NAN;
} }
trimmed.parse::<f64>().unwrap_or(f64::NAN) let Some(text) = ascii_code_units_to_string(trimmed) else {
return f64::NAN;
};
text.parse::<f64>().unwrap_or(f64::NAN)
} }
/// Convert a constant value to a JS number for constant folding purposes. /// Convert a constant value to a JS number for constant folding purposes.
@ -9982,44 +10049,9 @@ fn member_to_string_approximation(expression: &Expression, arena: &crate::ast::A
result.0.extend_from_slice(utf16!("'")); result.0.extend_from_slice(utf16!("'"));
result result
} }
ExpressionKind::NumericLiteral(n) => { ExpressionKind::NumericLiteral(n) => super::ffi::js_number_to_utf16(*n),
let s = format_double_for_display(*n);
s.encode_utf16().collect()
}
ExpressionKind::This => Utf16String(utf16!("this").to_vec()), ExpressionKind::This => Utf16String(utf16!("this").to_vec()),
ExpressionKind::PrivateIdentifier(ident) => ident.name.clone(), ExpressionKind::PrivateIdentifier(ident) => ident.name.clone(),
_ => Utf16String(utf16!("<object>").to_vec()), _ => Utf16String(utf16!("<object>").to_vec()),
} }
} }
/// Format a double matching AK's `Utf16String::formatted("{}", double)`.
/// Uses ECMA-262 rules: scientific notation when the decimal exponent n
/// satisfies n < -5 or n > 21, otherwise regular decimal notation.
fn format_double_for_display(n: f64) -> String {
if n.is_nan() {
return "NaN".to_string();
}
if n.is_infinite() {
return if n > 0.0 { "Infinity" } else { "-Infinity" }.to_string();
}
if n == 0.0 {
return "0".to_string();
}
// Get the scientific notation representation to extract the exponent.
let e_str = format!("{n:e}");
if let Some(e_pos) = e_str.find('e') {
let exp_str = &e_str[e_pos + 1..];
let displayed_exponent = exp_str.parse::<i32>().unwrap_or(0);
// AK uses: n < -5 || n > 21 where n = displayed_exponent + 1.
// Equivalently: displayed_exponent < -6 || displayed_exponent > 20.
if !(-6..=20).contains(&displayed_exponent) {
let mantissa_part = &e_str[..e_pos];
if displayed_exponent < 0 {
return format!("{mantissa_part}e{displayed_exponent}");
} else {
return format!("{mantissa_part}e+{displayed_exponent}");
}
}
}
format!("{n}")
}

View file

@ -370,10 +370,11 @@ unsafe extern "C" {
pattern_len: usize, pattern_len: usize,
flags_data: *const u16, flags_data: *const u16,
flags_len: usize, flags_len: usize,
error_out: *mut *const std::os::raw::c_char, error_out: *mut *const u16,
error_len_out: *mut usize,
) -> *mut c_void; ) -> *mut c_void;
pub fn rust_free_error_string(str: *const std::os::raw::c_char); pub fn rust_free_error_string(str: *const u16);
pub fn rust_number_to_utf16(value: f64, buffer: *mut u16, buffer_len: usize) -> usize; pub fn rust_number_to_utf16(value: f64, buffer: *mut u16, buffer_len: usize) -> usize;
@ -950,20 +951,22 @@ pub fn js_number_to_utf16(value: f64) -> Utf16String {
/// ///
/// On success, returns an opaque handle to the compiled regex (a C++ /// On success, returns an opaque handle to the compiled regex (a C++
/// RustCompiledRegex*). On failure, returns the error message. /// RustCompiledRegex*). On failure, returns the error message.
pub fn compile_regex(pattern: &[u16], flags: &[u16]) -> Result<*mut c_void, String> { pub fn compile_regex(pattern: &[u16], flags: &[u16]) -> Result<*mut c_void, Utf16String> {
unsafe { unsafe {
let mut error: *const std::os::raw::c_char = std::ptr::null(); let mut error: *const u16 = std::ptr::null();
let mut error_len = 0usize;
let handle = rust_compile_regex( let handle = rust_compile_regex(
pattern.as_ptr(), pattern.as_ptr(),
pattern.len(), pattern.len(),
flags.as_ptr(), flags.as_ptr(),
flags.len(), flags.len(),
&raw mut error, &raw mut error,
&raw mut error_len,
); );
if error.is_null() { if error.is_null() {
Ok(handle) Ok(handle)
} else { } else {
let msg = std::ffi::CStr::from_ptr(error).to_string_lossy().into_owned(); let msg = Utf16String(std::slice::from_raw_parts(error, error_len).to_vec());
rust_free_error_string(error); rust_free_error_string(error);
Err(msg) Err(msg)
} }

View file

@ -3108,7 +3108,7 @@ fn validate_constant_value(decoder: &mut Decoder<'_>) -> Option<()> {
} }
tag if tag == ConstantTag::BigInt as u8 => { tag if tag == ConstantTag::BigInt as u8 => {
let length: usize = u32::decode(decoder)?.try_into().ok()?; let length: usize = u32::decode(decoder)?.try_into().ok()?;
std::str::from_utf8(decoder.bytes(length)?).ok()?; decoder.bytes(length)?.is_ascii().then_some(())?;
} }
tag if tag == ConstantTag::WellKnownSymbol as u8 => match u8::decode(decoder)? { tag if tag == ConstantTag::WellKnownSymbol as u8 => match u8::decode(decoder)? {
0 | 1 => {} 0 | 1 => {}
@ -3167,7 +3167,9 @@ impl Decode for ConstantValue {
tag if tag == ConstantTag::Empty as u8 => Some(Self::Empty), tag if tag == ConstantTag::Empty as u8 => Some(Self::Empty),
tag if tag == ConstantTag::String as u8 => Some(Self::String(ast::Utf16String::decode(decoder)?)), tag if tag == ConstantTag::String as u8 => Some(Self::String(ast::Utf16String::decode(decoder)?)),
tag if tag == ConstantTag::BigInt as u8 => { tag if tag == ConstantTag::BigInt as u8 => {
Some(Self::BigInt(String::from_utf8(ByteVector::decode(decoder)?).ok()?)) let bytes = ByteVector::decode(decoder)?;
bytes.is_ascii().then_some(())?;
Some(Self::BigInt(bytes.into_iter().map(char::from).collect()))
} }
tag if tag == ConstantTag::WellKnownSymbol as u8 => match u8::decode(decoder)? { tag if tag == ConstantTag::WellKnownSymbol as u8 => match u8::decode(decoder)? {
0 => Some(Self::WellKnownSymbol(WellKnownSymbolKind::SymbolIterator)), 0 => Some(Self::WellKnownSymbol(WellKnownSymbolKind::SymbolIterator)),

View file

@ -252,9 +252,28 @@ unsafe fn source_from_raw<'a>(source: *const u16, len: usize) -> Option<&'a [u16
/// Callback type for reporting parse errors to C++. /// Callback type for reporting parse errors to C++.
pub type ParseErrorCallback = Option< pub type ParseErrorCallback = Option<
unsafe extern "C" fn(ctx: *mut c_void, message: *const u8, message_len: usize, line: u32, column: u32) -> (), unsafe extern "C" fn(ctx: *mut c_void, message: *const u16, message_len: usize, line: u32, column: u32) -> (),
>; >;
unsafe fn report_parse_error(
callback: unsafe extern "C" fn(
ctx: *mut c_void,
message: *const u16,
message_len: usize,
line: u32,
column: u32,
) -> (),
context: *mut c_void,
message: &str,
line: u32,
column: u32,
) {
let message_utf16: Vec<u16> = message.encode_utf16().collect();
unsafe {
callback(context, message_utf16.as_ptr(), message_utf16.len(), line, column);
}
}
/// Check for errors, optionally reporting them via a C++ callback. /// Check for errors, optionally reporting them via a C++ callback.
fn check_errors_with_callback( fn check_errors_with_callback(
parser: &mut Parser, parser: &mut Parser,
@ -264,9 +283,8 @@ fn check_errors_with_callback(
if parser.has_errors() { if parser.has_errors() {
if let Some(cb) = error_callback { if let Some(cb) = error_callback {
for err in parser.errors() { for err in parser.errors() {
let msg = &err.message;
unsafe { unsafe {
cb(error_context, msg.as_ptr(), msg.len(), err.line, err.column); report_parse_error(cb, error_context, &err.message, err.line, err.column);
} }
} }
} }
@ -275,9 +293,8 @@ fn check_errors_with_callback(
if parser.scope_collector.has_errors() { if parser.scope_collector.has_errors() {
if let Some(cb) = error_callback { if let Some(cb) = error_callback {
for err in parser.scope_collector.drain_errors() { for err in parser.scope_collector.drain_errors() {
let msg = &err.message;
unsafe { unsafe {
cb(error_context, msg.as_ptr(), msg.len(), err.line, err.column); report_parse_error(cb, error_context, &err.message, err.line, err.column);
} }
} }
} }
@ -680,8 +697,13 @@ pub unsafe extern "C" fn rust_parsed_program_take_errors(
unsafe { unsafe {
let parsed = &mut *parsed; let parsed = &mut *parsed;
for err in parsed.errors.drain(..) { for err in parsed.errors.drain(..) {
let msg = err.message.as_bytes(); report_parse_error(
error_callback.unwrap()(error_context, msg.as_ptr(), msg.len(), err.line, err.column); error_callback.unwrap(),
error_context,
&err.message,
err.line,
err.column,
);
} }
} }
} }
@ -1552,13 +1574,7 @@ pub unsafe extern "C" fn rust_compile_dynamic_function(
.message .message
.unwrap_or_else(|| format!("Unexpected token {}", token.token_type.name())); .unwrap_or_else(|| format!("Unexpected token {}", token.token_type.name()));
if let Some(cb) = error_callback { if let Some(cb) = error_callback {
cb( report_parse_error(cb, error_context, &msg, token.line_number, token.line_column);
error_context,
msg.as_ptr(),
msg.len(),
token.line_number,
token.line_column,
);
} }
return std::ptr::null_mut(); return std::ptr::null_mut();
} }
@ -1642,8 +1658,7 @@ pub unsafe extern "C" fn rust_compile_dynamic_function(
if parser.scope_collector.has_errors() { if parser.scope_collector.has_errors() {
if let Some(cb) = error_callback { if let Some(cb) = error_callback {
for err in parser.scope_collector.drain_errors() { for err in parser.scope_collector.drain_errors() {
let msg = &err.message; report_parse_error(cb, error_context, &err.message, err.line, err.column);
cb(error_context, msg.as_ptr(), msg.len(), err.line, err.column);
} }
} }
return std::ptr::null_mut(); return std::ptr::null_mut();
@ -1678,8 +1693,7 @@ pub unsafe extern "C" fn rust_compile_dynamic_function(
let Some(function_id) = function_id else { let Some(function_id) = function_id else {
if let Some(cb) = error_callback { if let Some(cb) = error_callback {
let msg = "Failed to parse dynamic function"; report_parse_error(cb, error_context, "Failed to parse dynamic function", 0, 0);
cb(error_context, msg.as_ptr(), msg.len(), 0, 0);
} }
return std::ptr::null_mut(); return std::ptr::null_mut();
}; };

View file

@ -379,15 +379,9 @@ impl Parser<'_> {
let token = self.consume(); let token = self.consume();
let value = self.token_value(&token); let value = self.token_value(&token);
// Store the raw value including the 'n' suffix, matching C++. // Store the raw value including the 'n' suffix, matching C++.
let value_utf8: String = value let value = Utf16String(value.to_vec());
.iter()
.map(|&c| {
assert!(c < 128, "BigIntLiteral should only contain ASCII characters");
c as u8 as char
})
.collect();
( (
self.expression(start, ExpressionKind::BigIntLiteral(Box::new(value_utf8))), self.expression(start, ExpressionKind::BigIntLiteral(Box::new(value))),
true, true,
) )
} }
@ -654,7 +648,7 @@ impl Parser<'_> {
let compiled_regex = match crate::bytecode::ffi::compile_regex(&pattern, &flags) { let compiled_regex = match crate::bytecode::ffi::compile_regex(&pattern, &flags) {
Ok(handle) => Arc::new(CompiledRegex::new(handle)), Ok(handle) => Arc::new(CompiledRegex::new(handle)),
Err(msg) => { Err(msg) => {
self.syntax_error_at_position(&msg, start); self.syntax_error_at_position(&String::from_utf16_lossy(&msg), start);
Arc::new(CompiledRegex::new(std::ptr::null_mut())) Arc::new(CompiledRegex::new(std::ptr::null_mut()))
} }
}; };
@ -1804,14 +1798,8 @@ impl Parser<'_> {
let token = self.consume(); let token = self.consume();
let value = self.token_value(&token); let value = self.token_value(&token);
// Store the raw value including the 'n' suffix, matching C++. // Store the raw value including the 'n' suffix, matching C++.
let value_utf8: String = value let value = Utf16String(value.to_vec());
.iter() let expression = self.expression(start, ExpressionKind::BigIntLiteral(Box::new(value)));
.map(|&c| {
assert!(c < 128, "BigIntLiteral should only contain ASCII characters");
c as u8 as char
})
.collect();
let expression = self.expression(start, ExpressionKind::BigIntLiteral(Box::new(value_utf8)));
PropertyKey { PropertyKey {
expression, expression,
name: None, name: None,

View file

@ -85,21 +85,21 @@ struct BytecodeDumpBuilder {
// --- Error collection callbacks --- // --- Error collection callbacks ---
// Collects parse errors as a Vector<ParserError> (for Script/Module compilation). // Collects parse errors as a Vector<ParserError> (for Script/Module compilation).
static void collect_parse_errors(void* ctx, uint8_t const* message, size_t message_len, uint32_t line, uint32_t column) static void collect_parse_errors(void* ctx, uint16_t const* message, size_t message_len, uint32_t line, uint32_t column)
{ {
auto& errors = *static_cast<Vector<ParserError>*>(ctx); auto& errors = *static_cast<Vector<ParserError>*>(ctx);
errors.append({ errors.append({
Utf16String::from_utf8({ message, message_len }), Utf16String::from_utf16(utf16_view_from_bytes(message, message_len)),
Position { line, column }, Position { line, column },
}); });
} }
// Collects a single parse error as a formatted Utf16String (for eval/dynamic function compilation). // Collects a single parse error as a formatted Utf16String (for eval/dynamic function compilation).
static void collect_single_parse_error(void* ctx, uint8_t const* message, size_t message_len, uint32_t line, uint32_t column) static void collect_single_parse_error(void* ctx, uint16_t const* message, size_t message_len, uint32_t line, uint32_t column)
{ {
auto& error_message = *static_cast<Utf16String*>(ctx); auto& error_message = *static_cast<Utf16String*>(ctx);
if (error_message.is_empty()) if (error_message.is_empty())
error_message = Utf16String::formatted("{} (line: {}, column: {})", Utf16String::from_utf8({ message, message_len }), line, column); error_message = Utf16String::formatted("{} (line: {}, column: {})", utf16_view_from_bytes(message, message_len), line, column);
} }
// --- Script GDI builder and callbacks --- // --- Script GDI builder and callbacks ---
@ -704,10 +704,10 @@ Optional<Result<ScriptResult, Vector<ParserError>>> materialize_bytecode_cache_s
return builder.result; return builder.result;
} }
Optional<Result<ScriptResult, Vector<ParserError>>> compile_script(Utf16View source_text, Realm& realm, StringView filename, size_t line_number_offset) Optional<Result<ScriptResult, Vector<ParserError>>> compile_script(Utf16View source_text, Realm& realm, Utf16View display_filename, size_t line_number_offset)
{ {
auto source_code = SourceCode::create( auto source_code = SourceCode::create(
String::from_utf8(filename).release_value_but_fixme_should_propagate_errors(), Utf16String::from_utf16(display_filename),
Utf16String::from_utf16(source_text)); Utf16String::from_utf16(source_text));
auto const* source_ptr = source_code->utf16_data(); auto const* source_ptr = source_code->utf16_data();
@ -989,10 +989,17 @@ ModuleBytecodeCacheInstallResult install_generated_bytecode_cache_module(Decoded
return result.release_value(); return result.release_value();
} }
Optional<Result<ModuleResult, Vector<ParserError>>> compile_module(StringView source_text, Realm& realm, StringView filename) Optional<Result<ModuleResult, Vector<ParserError>>> compile_module(Utf16View source_text, Realm& realm, Utf16View display_filename)
{ {
auto source_code = SourceCode::create(String::from_utf8(filename).release_value_but_fixme_should_propagate_errors(), Utf16String::from_utf8(source_text)); auto source_code = SourceCode::create(
Utf16String::from_utf16(display_filename),
Utf16String::from_utf16(source_text));
return compile_module(move(source_code), realm);
}
Optional<Result<ModuleResult, Vector<ParserError>>> compile_module(NonnullRefPtr<SourceCode const> source_code, Realm& realm)
{
auto const* source_ptr = source_code->utf16_data(); auto const* source_ptr = source_code->utf16_data();
auto length = source_code->length_in_code_units(); auto length = source_code->length_in_code_units();
auto* parsed = rust_parse_program(source_ptr, length, static_cast<u8>(ProgramType::Module), 0, g_dump_ast, g_dump_ast_use_color); auto* parsed = rust_parse_program(source_ptr, length, static_cast<u8>(ProgramType::Module), 0, g_dump_ast, g_dump_ast_use_color);
@ -1046,10 +1053,9 @@ Optional<Result<GC::Ref<SharedFunctionInstanceData>, Utf16String>> compile_dynam
} }
Optional<Vector<GC::Root<SharedFunctionInstanceData>>> compile_builtin_file( Optional<Vector<GC::Root<SharedFunctionInstanceData>>> compile_builtin_file(
unsigned char const* script_text, VM& vm) Utf16View script_text, VM& vm)
{ {
auto script_text_as_utf16 = Utf16String::from_utf8_without_validation({ script_text, strlen(reinterpret_cast<char const*>(script_text)) }); auto code = SourceCode::create("BuiltinFile"_utf16, Utf16String::from_utf16(script_text));
auto code = SourceCode::create("BuiltinFile"_string, move(script_text_as_utf16));
auto const& code_view = code->code_view(); auto const& code_view = code->code_view();
auto length = code_view.length_in_code_units(); auto length = code_view.length_in_code_units();
@ -1824,12 +1830,22 @@ extern "C" void module_sfd_set_name(
extern "C" void* rust_compile_regex( extern "C" void* rust_compile_regex(
uint16_t const* pattern_data, size_t pattern_len, uint16_t const* pattern_data, size_t pattern_len,
uint16_t const* flags_data, size_t flags_len, uint16_t const* flags_data, size_t flags_len,
char const** error_out) uint16_t const** error_out, size_t* error_len_out)
{ {
*error_out = nullptr; *error_out = nullptr;
*error_len_out = 0;
auto pattern = JS::RustIntegration::utf16_view_from_bytes(pattern_data, pattern_len); auto pattern = JS::RustIntegration::utf16_view_from_bytes(pattern_data, pattern_len);
auto flags_view = JS::RustIntegration::utf16_view_from_bytes(flags_data, flags_len); auto flags_view = JS::RustIntegration::utf16_view_from_bytes(flags_data, flags_len);
auto set_error = [&](Utf16String message) {
auto view = message.utf16_view();
auto* buffer = static_cast<uint16_t*>(kmalloc(view.length_in_code_units() * sizeof(uint16_t)));
for (size_t i = 0; i < view.length_in_code_units(); ++i)
buffer[i] = view.code_unit_at(i);
*error_out = buffer;
*error_len_out = view.length_in_code_units();
};
// Extract unicode/unicode_sets from flags for parse_regex_pattern. // Extract unicode/unicode_sets from flags for parse_regex_pattern.
bool is_unicode = false; bool is_unicode = false;
bool is_unicode_sets = false; bool is_unicode_sets = false;
@ -1843,11 +1859,7 @@ extern "C" void* rust_compile_regex(
auto parsed_pattern = JS::parse_regex_pattern(pattern, is_unicode, is_unicode_sets); auto parsed_pattern = JS::parse_regex_pattern(pattern, is_unicode, is_unicode_sets);
if (parsed_pattern.is_error()) { if (parsed_pattern.is_error()) {
auto msg = Utf16String::formatted("RegExp compile error: {}", parsed_pattern.release_error().error).to_byte_string(); set_error(Utf16String::formatted("RegExp compile error: {}", parsed_pattern.release_error().error));
auto* buf = static_cast<char*>(kmalloc(msg.length() + 1));
memcpy(buf, msg.bytes().data(), msg.length());
buf[msg.length()] = '\0';
*error_out = buf;
return nullptr; return nullptr;
} }
auto pattern_str = parsed_pattern.release_value(); auto pattern_str = parsed_pattern.release_value();
@ -1888,11 +1900,8 @@ extern "C" void* rust_compile_regex(
auto compiled = regex::ECMAScriptRegex::compile(pattern_str.utf16_view(), compile_flags); auto compiled = regex::ECMAScriptRegex::compile(pattern_str.utf16_view(), compile_flags);
if (compiled.is_error()) { if (compiled.is_error()) {
auto msg = MUST(String::formatted("RegExp compile error: {}", compiled.release_error())); auto error = compiled.release_error();
auto* buf = static_cast<char*>(kmalloc(msg.byte_count() + 1)); set_error(Utf16String::formatted("RegExp compile error: {}", Utf16String::from_utf8(error)));
memcpy(buf, msg.bytes().data(), msg.byte_count());
buf[msg.byte_count()] = '\0';
*error_out = buf;
return nullptr; return nullptr;
} }
@ -1904,9 +1913,9 @@ extern "C" void rust_free_compiled_regex(void* ptr)
delete static_cast<RustCompiledRegex*>(ptr); delete static_cast<RustCompiledRegex*>(ptr);
} }
extern "C" void rust_free_error_string(char const* str) extern "C" void rust_free_error_string(uint16_t const* str)
{ {
kfree(const_cast<char*>(str)); kfree(const_cast<uint16_t*>(str));
} }
extern "C" size_t rust_number_to_utf16(double value, uint16_t* buffer, size_t buffer_len) extern "C" size_t rust_number_to_utf16(double value, uint16_t* buffer, size_t buffer_len)
@ -1919,17 +1928,4 @@ extern "C" size_t rust_number_to_utf16(double value, uint16_t* buffer, size_t bu
return len; return len;
} }
// FIXME: This FFI workaround exists only to match C++ float-to-string
// formatting in the Rust AST dump. Once the C++ pipeline is
// removed, this can be deleted and the Rust side can use its own
// formatting without needing to match C++.
extern "C" size_t rust_format_double(double value, uint8_t* buffer, size_t buffer_len)
{
auto str = MUST(String::formatted("{}", value));
auto bytes = str.bytes();
auto len = min(bytes.size(), buffer_len);
memcpy(buffer, bytes.data(), len);
return len;
}
} }

View file

@ -158,7 +158,7 @@ Optional<Result<ScriptResult, Vector<ParserError>>> compile_parsed_script(FFI::P
Optional<Result<ScriptResult, Vector<ParserError>>> materialize_compiled_script(FFI::CompiledProgram* compiled, NonnullRefPtr<SourceCode const> source_code, Realm& realm); Optional<Result<ScriptResult, Vector<ParserError>>> materialize_compiled_script(FFI::CompiledProgram* compiled, NonnullRefPtr<SourceCode const> source_code, Realm& realm);
// Compile a script. Returns nullopt if Rust is not available. // Compile a script. Returns nullopt if Rust is not available.
Optional<Result<ScriptResult, Vector<ParserError>>> compile_script(Utf16View source_text, Realm& realm, StringView filename, size_t line_number_offset); Optional<Result<ScriptResult, Vector<ParserError>>> compile_script(Utf16View source_text, Realm& realm, Utf16View display_filename, size_t line_number_offset);
// Compile eval code. Returns nullopt if Rust is not available. // Compile eval code. Returns nullopt if Rust is not available.
// On success, the executable's name is set to "eval". // On success, the executable's name is set to "eval".
@ -177,7 +177,8 @@ Optional<Result<ModuleResult, Vector<ParserError>>> compile_parsed_module(FFI::P
Optional<Result<ModuleResult, Vector<ParserError>>> materialize_compiled_module(FFI::CompiledProgram* compiled, NonnullRefPtr<SourceCode const> source_code, Realm& realm); Optional<Result<ModuleResult, Vector<ParserError>>> materialize_compiled_module(FFI::CompiledProgram* compiled, NonnullRefPtr<SourceCode const> source_code, Realm& realm);
// Compile a module. Returns nullopt if Rust is not available. // Compile a module. Returns nullopt if Rust is not available.
Optional<Result<ModuleResult, Vector<ParserError>>> compile_module(StringView source_text, Realm& realm, StringView filename); Optional<Result<ModuleResult, Vector<ParserError>>> compile_module(Utf16View source_text, Realm& realm, Utf16View display_filename);
Optional<Result<ModuleResult, Vector<ParserError>>> compile_module(NonnullRefPtr<SourceCode const>, Realm& realm);
// Compile a dynamic function (new Function()). // Compile a dynamic function (new Function()).
// On success, returns a SharedFunctionInstanceData with source_text set. // On success, returns a SharedFunctionInstanceData with source_text set.
@ -187,7 +188,7 @@ JS_API Optional<Result<GC::Ref<SharedFunctionInstanceData>, Utf16String>> compil
// Compile a builtin JS file. Returns nullopt if Rust is not available. // Compile a builtin JS file. Returns nullopt if Rust is not available.
Optional<Vector<GC::Root<SharedFunctionInstanceData>>> compile_builtin_file( Optional<Vector<GC::Root<SharedFunctionInstanceData>>> compile_builtin_file(
unsigned char const* script_text, VM& vm); Utf16View script_text, VM& vm);
// Compile a function body for lazy compilation. // Compile a function body for lazy compilation.
// Returns nullptr if Rust is not available or the SFD doesn't use Rust compilation. // Returns nullptr if Rust is not available or the SFD doesn't use Rust compilation.

View file

@ -22,9 +22,13 @@ bool g_dump_ast_use_color = false;
GC_DEFINE_ALLOCATOR(Script); GC_DEFINE_ALLOCATOR(Script);
// 16.1.5 ParseScript ( sourceText, realm, hostDefined ), https://tc39.es/ecma262/#sec-parse-script // 16.1.5 ParseScript ( sourceText, realm, hostDefined ), https://tc39.es/ecma262/#sec-parse-script
Result<GC::Ref<Script>, Vector<ParserError>> Script::parse(Utf16View source_text, Realm& realm, StringView filename, HostDefined* host_defined, size_t line_number_offset) Result<GC::Ref<Script>, Vector<ParserError>> Script::parse(Utf16View source_text, Realm& realm, StringView filename, Utf16View display_filename, HostDefined* host_defined, size_t line_number_offset)
{ {
auto rust_compilation = RustIntegration::compile_script(source_text, realm, filename, line_number_offset); auto fallback_display_filename = display_filename.is_empty() ? Utf16String::from_utf8(filename) : Utf16String {};
if (display_filename.is_empty())
display_filename = fallback_display_filename.utf16_view();
auto rust_compilation = RustIntegration::compile_script(source_text, realm, display_filename, line_number_offset);
if (!rust_compilation.has_value()) if (!rust_compilation.has_value())
return Vector<ParserError> {}; return Vector<ParserError> {};
if (rust_compilation->is_error()) if (rust_compilation->is_error())
@ -32,9 +36,8 @@ Result<GC::Ref<Script>, Vector<ParserError>> Script::parse(Utf16View source_text
return realm.heap().allocate<Script>(realm, filename, move(rust_compilation->value()), ExecutableBacking::source(), host_defined); return realm.heap().allocate<Script>(realm, filename, move(rust_compilation->value()), ExecutableBacking::source(), host_defined);
} }
Result<GC::Ref<Script>, Vector<ParserError>> Script::create_from_parsed(FFI::ParsedProgram* parsed, NonnullRefPtr<SourceCode const> source_code, Realm& realm, HostDefined* host_defined) Result<GC::Ref<Script>, Vector<ParserError>> Script::create_from_parsed(FFI::ParsedProgram* parsed, NonnullRefPtr<SourceCode const> source_code, Realm& realm, StringView filename, HostDefined* host_defined)
{ {
auto filename = source_code->filename();
auto rust_compilation = RustIntegration::compile_parsed_script(parsed, move(source_code), realm); auto rust_compilation = RustIntegration::compile_parsed_script(parsed, move(source_code), realm);
if (!rust_compilation.has_value()) if (!rust_compilation.has_value())
return Vector<ParserError> {}; return Vector<ParserError> {};
@ -43,9 +46,8 @@ Result<GC::Ref<Script>, Vector<ParserError>> Script::create_from_parsed(FFI::Par
return realm.heap().allocate<Script>(realm, filename, move(rust_compilation->value()), ExecutableBacking::source(), host_defined); return realm.heap().allocate<Script>(realm, filename, move(rust_compilation->value()), ExecutableBacking::source(), host_defined);
} }
Result<GC::Ref<Script>, Vector<ParserError>> Script::create_from_compiled(FFI::CompiledProgram* compiled, NonnullRefPtr<SourceCode const> source_code, Realm& realm, HostDefined* host_defined) Result<GC::Ref<Script>, Vector<ParserError>> Script::create_from_compiled(FFI::CompiledProgram* compiled, NonnullRefPtr<SourceCode const> source_code, Realm& realm, StringView filename, HostDefined* host_defined)
{ {
auto filename = source_code->filename();
auto rust_compilation = RustIntegration::materialize_compiled_script(compiled, move(source_code), realm); auto rust_compilation = RustIntegration::materialize_compiled_script(compiled, move(source_code), realm);
if (!rust_compilation.has_value()) if (!rust_compilation.has_value())
return Vector<ParserError> {}; return Vector<ParserError> {};
@ -54,9 +56,8 @@ Result<GC::Ref<Script>, Vector<ParserError>> Script::create_from_compiled(FFI::C
return realm.heap().allocate<Script>(realm, filename, move(rust_compilation->value()), ExecutableBacking::heap_bytecode(), host_defined); return realm.heap().allocate<Script>(realm, filename, move(rust_compilation->value()), ExecutableBacking::heap_bytecode(), host_defined);
} }
Result<GC::Ref<Script>, Vector<ParserError>> Script::create_from_bytecode_cache(NonnullRefPtr<RustIntegration::DecodedBytecodeCache> bytecode_cache, NonnullRefPtr<SourceCode const> source_code, Realm& realm, HostDefined* host_defined) Result<GC::Ref<Script>, Vector<ParserError>> Script::create_from_bytecode_cache(NonnullRefPtr<RustIntegration::DecodedBytecodeCache> bytecode_cache, NonnullRefPtr<SourceCode const> source_code, Realm& realm, StringView filename, HostDefined* host_defined)
{ {
auto filename = source_code->filename();
auto rust_compilation = RustIntegration::materialize_bytecode_cache_script(bytecode_cache, move(source_code), realm); auto rust_compilation = RustIntegration::materialize_bytecode_cache_script(bytecode_cache, move(source_code), realm);
if (!rust_compilation.has_value()) if (!rust_compilation.has_value())
return Vector<ParserError> {}; return Vector<ParserError> {};

View file

@ -58,10 +58,10 @@ public:
}; };
virtual ~Script() override; virtual ~Script() override;
static Result<GC::Ref<Script>, Vector<ParserError>> parse(Utf16View source_text, Realm&, StringView filename = {}, HostDefined* = nullptr, size_t line_number_offset = 1); static Result<GC::Ref<Script>, Vector<ParserError>> parse(Utf16View source_text, Realm&, StringView filename = {}, Utf16View display_filename = {}, HostDefined* = nullptr, size_t line_number_offset = 1);
static Result<GC::Ref<Script>, Vector<ParserError>> create_from_parsed(FFI::ParsedProgram* parsed, NonnullRefPtr<SourceCode const> source_code, Realm&, HostDefined* = nullptr); static Result<GC::Ref<Script>, Vector<ParserError>> create_from_parsed(FFI::ParsedProgram* parsed, NonnullRefPtr<SourceCode const> source_code, Realm&, StringView filename, HostDefined* = nullptr);
static Result<GC::Ref<Script>, Vector<ParserError>> create_from_compiled(FFI::CompiledProgram* compiled, NonnullRefPtr<SourceCode const> source_code, Realm&, HostDefined* = nullptr); static Result<GC::Ref<Script>, Vector<ParserError>> create_from_compiled(FFI::CompiledProgram* compiled, NonnullRefPtr<SourceCode const> source_code, Realm&, StringView filename, HostDefined* = nullptr);
static Result<GC::Ref<Script>, Vector<ParserError>> create_from_bytecode_cache(NonnullRefPtr<RustIntegration::DecodedBytecodeCache>, NonnullRefPtr<SourceCode const> source_code, Realm&, HostDefined* = nullptr); static Result<GC::Ref<Script>, Vector<ParserError>> create_from_bytecode_cache(NonnullRefPtr<RustIntegration::DecodedBytecodeCache>, NonnullRefPtr<SourceCode const> source_code, Realm&, StringView filename, HostDefined* = nullptr);
Realm& realm() { return *m_realm; } Realm& realm() { return *m_realm; }
Vector<LoadedModuleRequest>& loaded_modules() { return m_loaded_modules; } Vector<LoadedModuleRequest>& loaded_modules() { return m_loaded_modules; }

View file

@ -39,17 +39,17 @@ static bool ascii_source_bytes_decode_to_same_code_units(StringView standardized
return bytes_are_identity_mapped && byte_offset == bytes.size(); return bytes_are_identity_mapped && byte_offset == bytes.size();
} }
NonnullRefPtr<SourceCode const> SourceCode::create(String filename, Utf16String code) NonnullRefPtr<SourceCode const> SourceCode::create(Utf16String filename, Utf16String code)
{ {
return adopt_ref(*new SourceCode(move(filename), move(code))); return adopt_ref(*new SourceCode(move(filename), move(code)));
} }
NonnullRefPtr<SourceCode const> SourceCode::create(String filename, size_t length_in_code_units, String source_encoding, Core::ImmutableBytes source_bytes) NonnullRefPtr<SourceCode const> SourceCode::create(Utf16String filename, size_t length_in_code_units, ByteString source_encoding, Core::ImmutableBytes source_bytes)
{ {
return adopt_ref(*new SourceCode(move(filename), length_in_code_units, move(source_encoding), move(source_bytes))); return adopt_ref(*new SourceCode(move(filename), length_in_code_units, move(source_encoding), move(source_bytes)));
} }
SourceCode::SourceCode(String filename, Utf16String code) SourceCode::SourceCode(Utf16String filename, Utf16String code)
: m_filename(move(filename)) : m_filename(move(filename))
, m_code(move(code)) , m_code(move(code))
, m_code_view(m_code->utf16_view()) , m_code_view(m_code->utf16_view())
@ -57,7 +57,7 @@ SourceCode::SourceCode(String filename, Utf16String code)
{ {
} }
SourceCode::SourceCode(String filename, size_t length_in_code_units, String source_encoding, Core::ImmutableBytes source_bytes) SourceCode::SourceCode(Utf16String filename, size_t length_in_code_units, ByteString source_encoding, Core::ImmutableBytes source_bytes)
: m_filename(move(filename)) : m_filename(move(filename))
, m_source_encoding(move(source_encoding)) , m_source_encoding(move(source_encoding))
, m_source_bytes(move(source_bytes)) , m_source_bytes(move(source_bytes))
@ -118,12 +118,9 @@ Utf16String SourceCode::source_text_from_offsets(size_t start_offset, size_t len
auto bytes = m_source_bytes.bytes(); auto bytes = m_source_bytes.bytes();
VERIFY(m_length_in_code_units == bytes.size()); VERIFY(m_length_in_code_units == bytes.size());
auto source_text_bytes = bytes.slice(start_offset, length); auto source_text_bytes = bytes.slice(start_offset, length);
if (all_of(source_text_bytes, AK::is_ascii)) VERIFY(all_of(source_text_bytes, AK::is_ascii));
return Utf16String::from_ascii_without_validation(source_text_bytes); return Utf16String::from_ascii_without_validation(source_text_bytes);
return Utf16String::from_utf8(StringView { source_text_bytes });
} }
if (auto source_text = source_text_from_utf8_source_bytes(start_offset, length); source_text.has_value())
return source_text.release_value();
return decode_source_range(start_offset, length); return decode_source_range(start_offset, length);
} }
@ -134,7 +131,7 @@ Utf16String SourceCode::source_text_from_offsets(size_t start_offset, size_t len
bool SourceCode::source_bytes_can_be_sliced_by_code_unit_offsets() const bool SourceCode::source_bytes_can_be_sliced_by_code_unit_offsets() const
{ {
if (!m_source_bytes_can_be_sliced_by_code_unit_offsets.has_value()) { if (!m_source_bytes_can_be_sliced_by_code_unit_offsets.has_value()) {
auto standardized_encoding = TextCodec::get_standardized_encoding(m_source_encoding); auto standardized_encoding = TextCodec::get_standardized_encoding(m_source_encoding.view());
if (!standardized_encoding.has_value()) { if (!standardized_encoding.has_value()) {
m_source_bytes_can_be_sliced_by_code_unit_offsets = false; m_source_bytes_can_be_sliced_by_code_unit_offsets = false;
return *m_source_bytes_can_be_sliced_by_code_unit_offsets; return *m_source_bytes_can_be_sliced_by_code_unit_offsets;
@ -163,114 +160,6 @@ bool SourceCode::source_bytes_can_be_sliced_by_code_unit_offsets() const
return *m_source_bytes_can_be_sliced_by_code_unit_offsets; return *m_source_bytes_can_be_sliced_by_code_unit_offsets;
} }
Optional<Utf16String> SourceCode::source_text_from_utf8_source_bytes(size_t start_offset, size_t length) const
{
auto start_byte_offset = byte_offset_for_utf8_code_unit_offset(start_offset);
if (!start_byte_offset.has_value())
return {};
auto end_byte_offset = byte_offset_for_utf8_code_unit_offset(start_offset + length);
if (!end_byte_offset.has_value())
return {};
VERIFY(*start_byte_offset <= *end_byte_offset);
auto source_text_bytes = m_source_bytes.bytes().slice(*start_byte_offset, *end_byte_offset - *start_byte_offset);
if (all_of(source_text_bytes, AK::is_ascii))
return Utf16String::from_ascii_without_validation(source_text_bytes);
return Utf16String::from_utf8(StringView { source_text_bytes });
}
bool SourceCode::ensure_utf8_source_byte_spans() const
{
if (m_tried_to_build_utf8_source_byte_spans)
return m_can_use_utf8_source_byte_spans;
m_tried_to_build_utf8_source_byte_spans = true;
auto standardized_encoding = TextCodec::get_standardized_encoding(m_source_encoding);
if (!standardized_encoding.has_value() || !standardized_encoding->equals_ignoring_ascii_case("UTF-8"sv))
return false;
auto bytes = m_source_bytes.bytes();
StringView input { bytes };
if (bytes.size() >= 3 && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF) {
input = input.substring_view(3);
m_utf8_source_byte_span_initial_byte_offset = 3;
} else if (bytes.size() >= 2 && ((bytes[0] == 0xFE && bytes[1] == 0xFF) || (bytes[0] == 0xFF && bytes[1] == 0xFE))) {
return false;
}
auto utf8_view = Utf8View { input };
if (!utf8_view.validate(AllowLonelySurrogates::No))
return false;
size_t code_unit_offset = 0;
for (auto it = utf8_view.begin(); it != utf8_view.end(); ++it) {
auto code_point = *it;
size_t code_unit_length = code_point <= 0xffff ? 1 : 2;
auto byte_length = it.underlying_code_point_length_in_bytes();
if (byte_length != code_unit_length) {
m_utf8_source_byte_spans.append({
.code_unit_offset = code_unit_offset,
.code_unit_length = code_unit_length,
.byte_offset = m_utf8_source_byte_span_initial_byte_offset + utf8_view.byte_offset_of(it),
.byte_length = byte_length,
});
}
code_unit_offset += code_unit_length;
}
if (code_unit_offset != m_length_in_code_units) {
m_utf8_source_byte_spans.clear();
return false;
}
m_can_use_utf8_source_byte_spans = true;
return true;
}
Optional<size_t> SourceCode::byte_offset_for_utf8_code_unit_offset(size_t code_unit_offset) const
{
if (code_unit_offset > m_length_in_code_units)
return {};
if (!ensure_utf8_source_byte_spans())
return {};
size_t low = 0;
size_t high = m_utf8_source_byte_spans.size();
while (low < high) {
auto middle = low + (high - low) / 2;
auto const& span = m_utf8_source_byte_spans[middle];
auto span_end = span.code_unit_offset + span.code_unit_length;
if (span_end <= code_unit_offset)
low = middle + 1;
else
high = middle;
}
if (low < m_utf8_source_byte_spans.size()) {
auto const& span = m_utf8_source_byte_spans[low];
if (code_unit_offset >= span.code_unit_offset && code_unit_offset < span.code_unit_offset + span.code_unit_length) {
if (code_unit_offset == span.code_unit_offset)
return span.byte_offset;
return {};
}
}
size_t byte_delta = m_utf8_source_byte_span_initial_byte_offset;
if (low > 0) {
auto const& previous_span = m_utf8_source_byte_spans[low - 1];
auto previous_span_end_byte_offset = previous_span.byte_offset + previous_span.byte_length;
auto previous_span_end_code_unit_offset = previous_span.code_unit_offset + previous_span.code_unit_length;
VERIFY(previous_span_end_byte_offset >= previous_span_end_code_unit_offset);
byte_delta = previous_span_end_byte_offset - previous_span_end_code_unit_offset;
}
return code_unit_offset + byte_delta;
}
Utf16String SourceCode::decode_source_range(size_t start_offset, size_t length) const Utf16String SourceCode::decode_source_range(size_t start_offset, size_t length) const
{ {
if (length == 0) if (length == 0)
@ -279,7 +168,7 @@ Utf16String SourceCode::decode_source_range(size_t start_offset, size_t length)
VERIFY(start_offset <= NumericLimits<size_t>::max() - length); VERIFY(start_offset <= NumericLimits<size_t>::max() - length);
auto end_offset = start_offset + length; auto end_offset = start_offset + length;
StringView input { m_source_bytes.bytes() }; StringView input { m_source_bytes.bytes() };
auto decoder = TextCodec::decoder_for(m_source_encoding); auto decoder = TextCodec::decoder_for(m_source_encoding.view());
VERIFY(decoder.has_value()); VERIFY(decoder.has_value());
TextCodec::Decoder* actual_decoder = &decoder.value(); TextCodec::Decoder* actual_decoder = &decoder.value();

View file

@ -6,8 +6,8 @@
#pragma once #pragma once
#include <AK/ByteString.h>
#include <AK/Optional.h> #include <AK/Optional.h>
#include <AK/String.h>
#include <AK/Utf16String.h> #include <AK/Utf16String.h>
#include <AK/Vector.h> #include <AK/Vector.h>
#include <LibCore/ImmutableBytes.h> #include <LibCore/ImmutableBytes.h>
@ -19,10 +19,10 @@ namespace JS {
class JS_API SourceCode : public RefCounted<SourceCode> { class JS_API SourceCode : public RefCounted<SourceCode> {
public: public:
static NonnullRefPtr<SourceCode const> create(String filename, Utf16String code); static NonnullRefPtr<SourceCode const> create(Utf16String filename, Utf16String code);
static NonnullRefPtr<SourceCode const> create(String filename, size_t length_in_code_units, String source_encoding, Core::ImmutableBytes source_bytes); static NonnullRefPtr<SourceCode const> create(Utf16String filename, size_t length_in_code_units, ByteString source_encoding, Core::ImmutableBytes source_bytes);
String const& filename() const { return m_filename; } Utf16String const& filename() const { return m_filename; }
Utf16String const& code() const; Utf16String const& code() const;
Utf16View const& code_view() const; Utf16View const& code_view() const;
size_t length_in_code_units() const { return m_length_in_code_units; } size_t length_in_code_units() const { return m_length_in_code_units; }
@ -33,25 +33,15 @@ public:
SourceRange range_from_offsets(u32 start_offset, u32 end_offset) const; SourceRange range_from_offsets(u32 start_offset, u32 end_offset) const;
private: private:
SourceCode(String filename, Utf16String code); SourceCode(Utf16String filename, Utf16String code);
SourceCode(String filename, size_t length_in_code_units, String source_encoding, Core::ImmutableBytes source_bytes); SourceCode(Utf16String filename, size_t length_in_code_units, ByteString source_encoding, Core::ImmutableBytes source_bytes);
void ensure_code() const; void ensure_code() const;
Utf16String decode_source_range(size_t start_offset, size_t length) const; Utf16String decode_source_range(size_t start_offset, size_t length) const;
bool source_bytes_can_be_sliced_by_code_unit_offsets() const; bool source_bytes_can_be_sliced_by_code_unit_offsets() const;
Optional<Utf16String> source_text_from_utf8_source_bytes(size_t start_offset, size_t length) const;
bool ensure_utf8_source_byte_spans() const;
Optional<size_t> byte_offset_for_utf8_code_unit_offset(size_t code_unit_offset) const;
struct Utf8SourceByteSpan { Utf16String m_filename;
size_t code_unit_offset { 0 };
size_t code_unit_length { 0 };
size_t byte_offset { 0 };
size_t byte_length { 0 };
};
String m_filename;
Optional<Utf16String> mutable m_code; Optional<Utf16String> mutable m_code;
String m_source_encoding; ByteString m_source_encoding;
Core::ImmutableBytes mutable m_source_bytes; Core::ImmutableBytes mutable m_source_bytes;
Utf16View mutable m_code_view; Utf16View mutable m_code_view;
size_t m_length_in_code_units { 0 }; size_t m_length_in_code_units { 0 };
@ -70,10 +60,6 @@ private:
// utf16_data() for use by the Rust compilation pipeline. // utf16_data() for use by the Rust compilation pipeline.
Vector<u16> mutable m_utf16_data_cache; Vector<u16> mutable m_utf16_data_cache;
Optional<bool> mutable m_source_bytes_can_be_sliced_by_code_unit_offsets; Optional<bool> mutable m_source_bytes_can_be_sliced_by_code_unit_offsets;
Vector<Utf8SourceByteSpan> mutable m_utf8_source_byte_spans;
size_t mutable m_utf8_source_byte_span_initial_byte_offset { 0 };
bool mutable m_tried_to_build_utf8_source_byte_spans { false };
bool mutable m_can_use_utf8_source_byte_spans { false };
}; };
} }

View file

@ -9,7 +9,6 @@
#include <AK/NonnullRefPtr.h> #include <AK/NonnullRefPtr.h>
#include <AK/RefPtr.h> #include <AK/RefPtr.h>
#include <AK/StringView.h>
#include <AK/Types.h> #include <AK/Types.h>
#include <LibJS/Export.h> #include <LibJS/Export.h>
#include <LibJS/Position.h> #include <LibJS/Position.h>
@ -21,7 +20,7 @@ struct JS_API SourceRange {
NonnullRefPtr<SourceCode const> code; NonnullRefPtr<SourceCode const> code;
Position start; Position start;
ByteString filename() const { return code->filename().to_byte_string(); } Utf16String const& filename() const { return code->filename(); }
}; };
} }

View file

@ -116,9 +116,8 @@ size_t SourceTextModule::external_memory_size() const
return size; return size;
} }
Result<GC::Ref<SourceTextModule>, Vector<ParserError>> SourceTextModule::parse_from_pre_parsed(FFI::ParsedProgram* parsed, NonnullRefPtr<SourceCode const> source_code, Realm& realm, Script::HostDefined* host_defined) Result<GC::Ref<SourceTextModule>, Vector<ParserError>> SourceTextModule::parse_from_pre_parsed(FFI::ParsedProgram* parsed, NonnullRefPtr<SourceCode const> source_code, Realm& realm, StringView filename, Script::HostDefined* host_defined)
{ {
auto filename = source_code->filename();
auto rust_result = RustIntegration::compile_parsed_module(parsed, move(source_code), realm); auto rust_result = RustIntegration::compile_parsed_module(parsed, move(source_code), realm);
// Always from the Rust pipeline, so the Optional must have a value. // Always from the Rust pipeline, so the Optional must have a value.
VERIFY(rust_result.has_value()); VERIFY(rust_result.has_value());
@ -140,9 +139,8 @@ Result<GC::Ref<SourceTextModule>, Vector<ParserError>> SourceTextModule::parse_f
module_result.executable.ptr(), module_result.tla_shared_data.ptr(), ExecutableBacking::source()); module_result.executable.ptr(), module_result.tla_shared_data.ptr(), ExecutableBacking::source());
} }
Result<GC::Ref<SourceTextModule>, Vector<ParserError>> SourceTextModule::parse_from_pre_compiled(FFI::CompiledProgram* compiled, NonnullRefPtr<SourceCode const> source_code, Realm& realm, Script::HostDefined* host_defined) Result<GC::Ref<SourceTextModule>, Vector<ParserError>> SourceTextModule::parse_from_pre_compiled(FFI::CompiledProgram* compiled, NonnullRefPtr<SourceCode const> source_code, Realm& realm, StringView filename, Script::HostDefined* host_defined)
{ {
auto filename = source_code->filename();
auto rust_result = RustIntegration::materialize_compiled_module(compiled, move(source_code), realm); auto rust_result = RustIntegration::materialize_compiled_module(compiled, move(source_code), realm);
// Always from the Rust pipeline, so the Optional must have a value. // Always from the Rust pipeline, so the Optional must have a value.
VERIFY(rust_result.has_value()); VERIFY(rust_result.has_value());
@ -164,9 +162,8 @@ Result<GC::Ref<SourceTextModule>, Vector<ParserError>> SourceTextModule::parse_f
module_result.executable.ptr(), module_result.tla_shared_data.ptr(), ExecutableBacking::heap_bytecode()); module_result.executable.ptr(), module_result.tla_shared_data.ptr(), ExecutableBacking::heap_bytecode());
} }
Result<GC::Ref<SourceTextModule>, Vector<ParserError>> SourceTextModule::parse_from_bytecode_cache(NonnullRefPtr<RustIntegration::DecodedBytecodeCache> bytecode_cache, NonnullRefPtr<SourceCode const> source_code, Realm& realm, Script::HostDefined* host_defined) Result<GC::Ref<SourceTextModule>, Vector<ParserError>> SourceTextModule::parse_from_bytecode_cache(NonnullRefPtr<RustIntegration::DecodedBytecodeCache> bytecode_cache, NonnullRefPtr<SourceCode const> source_code, Realm& realm, StringView filename, Script::HostDefined* host_defined)
{ {
auto filename = source_code->filename();
auto rust_result = RustIntegration::materialize_bytecode_cache_module(bytecode_cache, move(source_code), realm); auto rust_result = RustIntegration::materialize_bytecode_cache_module(bytecode_cache, move(source_code), realm);
// Always from the Rust pipeline, so the Optional must have a value. // Always from the Rust pipeline, so the Optional must have a value.
VERIFY(rust_result.has_value()); VERIFY(rust_result.has_value());
@ -263,10 +260,8 @@ void SourceTextModule::complete_bytecode_cache_install(GC::Ptr<Bytecode::Executa
verify_executable_backing_invariants(); verify_executable_backing_invariants();
} }
// 16.2.1.7.1 ParseModule ( sourceText, realm, hostDefined ), https://tc39.es/ecma262/#sec-parsemodule Result<GC::Ref<SourceTextModule>, Vector<ParserError>> SourceTextModule::from_rust_result(Optional<Result<RustIntegration::ModuleResult, Vector<ParserError>>> rust_result, Realm& realm, StringView filename, Script::HostDefined* host_defined)
Result<GC::Ref<SourceTextModule>, Vector<ParserError>> SourceTextModule::parse(StringView source_text, Realm& realm, StringView filename, Script::HostDefined* host_defined)
{ {
auto rust_result = RustIntegration::compile_module(source_text, realm, filename);
if (!rust_result.has_value()) if (!rust_result.has_value())
return Vector<ParserError> {}; return Vector<ParserError> {};
if (rust_result->is_error()) if (rust_result->is_error())
@ -288,6 +283,21 @@ Result<GC::Ref<SourceTextModule>, Vector<ParserError>> SourceTextModule::parse(S
module_result.executable.ptr(), module_result.tla_shared_data.ptr(), ExecutableBacking::source()); module_result.executable.ptr(), module_result.tla_shared_data.ptr(), ExecutableBacking::source());
} }
// 16.2.1.7.1 ParseModule ( sourceText, realm, hostDefined ), https://tc39.es/ecma262/#sec-parsemodule
Result<GC::Ref<SourceTextModule>, Vector<ParserError>> SourceTextModule::parse(Utf16View source_text, Realm& realm, StringView filename, Utf16View display_filename, Script::HostDefined* host_defined)
{
auto fallback_display_filename = display_filename.is_empty() ? Utf16String::from_utf8(filename) : Utf16String {};
if (display_filename.is_empty())
display_filename = fallback_display_filename.utf16_view();
return from_rust_result(RustIntegration::compile_module(source_text, realm, display_filename), realm, filename, host_defined);
}
Result<GC::Ref<SourceTextModule>, Vector<ParserError>> SourceTextModule::parse(NonnullRefPtr<SourceCode const> source_code, Realm& realm, StringView filename, Script::HostDefined* host_defined)
{
return from_rust_result(RustIntegration::compile_module(move(source_code), realm), realm, filename, host_defined);
}
void SourceTextModule::verify_executable_backing_invariants() void SourceTextModule::verify_executable_backing_invariants()
{ {
VERIFY(m_executable || (m_tla_shared_data && m_tla_shared_data->m_executable)); VERIFY(m_executable || (m_tla_shared_data && m_tla_shared_data->m_executable));

View file

@ -28,6 +28,7 @@ struct DecodedBytecodeCacheBlob;
namespace RustIntegration { namespace RustIntegration {
class DecodedBytecodeCache; class DecodedBytecodeCache;
struct ModuleResult;
} }
@ -39,10 +40,11 @@ class JS_API SourceTextModule final : public CyclicModule {
public: public:
virtual ~SourceTextModule() override; virtual ~SourceTextModule() override;
static Result<GC::Ref<SourceTextModule>, Vector<ParserError>> parse(StringView source_text, Realm&, StringView filename = {}, Script::HostDefined* host_defined = nullptr); static Result<GC::Ref<SourceTextModule>, Vector<ParserError>> parse(Utf16View source_text, Realm&, StringView filename = {}, Utf16View display_filename = {}, Script::HostDefined* host_defined = nullptr);
static Result<GC::Ref<SourceTextModule>, Vector<ParserError>> parse_from_pre_parsed(FFI::ParsedProgram* parsed, NonnullRefPtr<SourceCode const> source_code, Realm&, Script::HostDefined* host_defined = nullptr); static Result<GC::Ref<SourceTextModule>, Vector<ParserError>> parse(NonnullRefPtr<SourceCode const>, Realm&, StringView filename = {}, Script::HostDefined* host_defined = nullptr);
static Result<GC::Ref<SourceTextModule>, Vector<ParserError>> parse_from_pre_compiled(FFI::CompiledProgram* compiled, NonnullRefPtr<SourceCode const> source_code, Realm&, Script::HostDefined* host_defined = nullptr); static Result<GC::Ref<SourceTextModule>, Vector<ParserError>> parse_from_pre_parsed(FFI::ParsedProgram* parsed, NonnullRefPtr<SourceCode const> source_code, Realm&, StringView filename, Script::HostDefined* host_defined = nullptr);
static Result<GC::Ref<SourceTextModule>, Vector<ParserError>> parse_from_bytecode_cache(NonnullRefPtr<RustIntegration::DecodedBytecodeCache>, NonnullRefPtr<SourceCode const> source_code, Realm&, Script::HostDefined* host_defined = nullptr); static Result<GC::Ref<SourceTextModule>, Vector<ParserError>> parse_from_pre_compiled(FFI::CompiledProgram* compiled, NonnullRefPtr<SourceCode const> source_code, Realm&, StringView filename, Script::HostDefined* host_defined = nullptr);
static Result<GC::Ref<SourceTextModule>, Vector<ParserError>> parse_from_bytecode_cache(NonnullRefPtr<RustIntegration::DecodedBytecodeCache>, NonnullRefPtr<SourceCode const> source_code, Realm&, StringView filename, Script::HostDefined* host_defined = nullptr);
virtual Vector<Utf16FlyString> get_exported_names(VM& vm, GC::RootHashTable<GC::Ref<Module const>>& export_star_set) override; virtual Vector<Utf16FlyString> get_exported_names(VM& vm, GC::RootHashTable<GC::Ref<Module const>>& export_star_set) override;
virtual ResolvedBinding resolve_export(VM& vm, Utf16FlyString const& export_name, Vector<ResolvedBinding> resolve_set = {}) override; virtual ResolvedBinding resolve_export(VM& vm, Utf16FlyString const& export_name, Vector<ResolvedBinding> resolve_set = {}) override;
@ -79,6 +81,7 @@ protected:
private: private:
SourceTextModule(Realm&, StringView filename, Script::HostDefined* host_defined, bool has_top_level_await, Vector<ModuleRequest> requested_modules, Vector<ImportEntry> import_entries, Vector<ExportEntry> local_export_entries, Vector<ExportEntry> indirect_export_entries, Vector<ExportEntry> star_export_entries, Optional<Utf16FlyString> default_export_binding_name, Vector<Utf16FlyString> var_declared_names, Vector<LexicalBinding> lexical_bindings, Vector<FunctionToInitialize> functions_to_initialize, Vector<GC::Root<SharedFunctionInstanceData>> shared_function_data, GC::Ptr<Bytecode::Executable> executable, GC::Ptr<SharedFunctionInstanceData> tla_shared_data, ExecutableBacking); SourceTextModule(Realm&, StringView filename, Script::HostDefined* host_defined, bool has_top_level_await, Vector<ModuleRequest> requested_modules, Vector<ImportEntry> import_entries, Vector<ExportEntry> local_export_entries, Vector<ExportEntry> indirect_export_entries, Vector<ExportEntry> star_export_entries, Optional<Utf16FlyString> default_export_binding_name, Vector<Utf16FlyString> var_declared_names, Vector<LexicalBinding> lexical_bindings, Vector<FunctionToInitialize> functions_to_initialize, Vector<GC::Root<SharedFunctionInstanceData>> shared_function_data, GC::Ptr<Bytecode::Executable> executable, GC::Ptr<SharedFunctionInstanceData> tla_shared_data, ExecutableBacking);
static Result<GC::Ref<SourceTextModule>, Vector<ParserError>> from_rust_result(Optional<Result<RustIntegration::ModuleResult, Vector<ParserError>>>, Realm&, StringView filename, Script::HostDefined*);
virtual void visit_edges(Cell::Visitor&) override; virtual void visit_edges(Cell::Visitor&) override;
virtual size_t external_memory_size() const override; virtual size_t external_memory_size() const override;

View file

@ -7,12 +7,13 @@
#include <AK/Debug.h> #include <AK/Debug.h>
#include <AK/UnicodeUtils.h> #include <AK/UnicodeUtils.h>
#include <AK/Utf16String.h> #include <LibCore/ImmutableBytes.h>
#include <LibGfx/Palette.h> #include <LibGfx/Palette.h>
#include <LibJS/RustFFI.h> #include <LibJS/RustFFI.h>
#include <LibJS/SourceCode.h> #include <LibJS/SourceCode.h>
#include <LibJS/SyntaxHighlighter.h> #include <LibJS/SyntaxHighlighter.h>
#include <LibJS/Token.h> #include <LibJS/Token.h>
#include <LibTextCodec/Decoder.h>
namespace JS { namespace JS {
@ -101,8 +102,13 @@ static void on_token(void* ctx, FFI::FFIToken const* ffi_token)
void SyntaxHighlighter::rehighlight(Palette const& palette) void SyntaxHighlighter::rehighlight(Palette const& palette)
{ {
auto text = m_client->get_text(); auto text = m_client->get_text();
auto source_utf16 = Utf16String::from_utf8(text); auto source_bytes = MUST(Core::ImmutableBytes::copy(text.bytes()));
auto source_code = SourceCode::create({}, move(source_utf16)); auto decoder = TextCodec::decoder_for("UTF-8"sv);
VERIFY(decoder.has_value());
auto source_length = TextCodec::convert_input_to_utf16_length_using_given_decoder_unless_there_is_a_byte_order_mark(
*decoder, StringView { source_bytes.bytes() })
.release_value_but_fixme_should_propagate_errors();
auto source_code = SourceCode::create({}, source_length, "UTF-8"sv, move(source_bytes));
auto const* source_data = source_code->utf16_data(); auto const* source_data = source_code->utf16_data();
auto source_len = source_code->length_in_code_units(); auto source_len = source_code->length_in_code_units();

View file

@ -58,15 +58,12 @@ GC::Ref<SyntheticModule> SyntheticModule::create_default_export_synthetic_module
} }
// 16.2.1.8.2 ParseJSONModule ( source ), https://tc39.es/ecma262/#sec-create-default-export-synthetic-module // 16.2.1.8.2 ParseJSONModule ( source ), https://tc39.es/ecma262/#sec-create-default-export-synthetic-module
ThrowCompletionOr<GC::Ref<SyntheticModule>> parse_json_module(Realm& realm, StringView source_text, ByteString filename) ThrowCompletionOr<GC::Ref<SyntheticModule>> parse_json_module(Realm& realm, Utf16View source_text, ByteString filename)
{ {
auto& vm = realm.vm(); auto& vm = realm.vm();
// 1. Let json be ? ParseJSON(source). // 1. Let json be ? ParseJSON(source).
auto json_text = Utf16String::try_from_utf8(source_text); auto json = TRY(JSONObject::parse_json(vm, source_text));
if (json_text.is_error())
return vm.throw_completion<SyntaxError>(ErrorType::JsonMalformed);
auto json = TRY(JSONObject::parse_json(vm, json_text.release_value()));
// 3. Return CreateDefaultExportSyntheticModule(json). // 3. Return CreateDefaultExportSyntheticModule(json).
return SyntheticModule::create_default_export_synthetic_module(realm, json, move(filename)); return SyntheticModule::create_default_export_synthetic_module(realm, json, move(filename));

View file

@ -6,6 +6,7 @@
#pragma once #pragma once
#include <AK/Utf16View.h>
#include <LibGC/Function.h> #include <LibGC/Function.h>
#include <LibGC/Ptr.h> #include <LibGC/Ptr.h>
#include <LibJS/Module.h> #include <LibJS/Module.h>
@ -40,6 +41,6 @@ private:
EvaluationFunction m_evaluation_steps; // [[EvaluationSteps]] EvaluationFunction m_evaluation_steps; // [[EvaluationSteps]]
}; };
ThrowCompletionOr<GC::Ref<SyntheticModule>> JS_API parse_json_module(Realm& realm, StringView source_text, ByteString filename); ThrowCompletionOr<GC::Ref<SyntheticModule>> JS_API parse_json_module(Realm& realm, Utf16View source_text, ByteString filename);
} }

View file

@ -10,6 +10,7 @@
#include <AK/Noncopyable.h> #include <AK/Noncopyable.h>
#include <AK/OwnPtr.h> #include <AK/OwnPtr.h>
#include <AK/String.h> #include <AK/String.h>
#include <AK/Utf16FlyString.h>
#include <AK/Utf16View.h> #include <AK/Utf16View.h>
#include <AK/Vector.h> #include <AK/Vector.h>
#include <LibRegex/Export.h> #include <LibRegex/Export.h>
@ -34,7 +35,7 @@ struct ECMAScriptCompileFlags {
}; };
struct ECMAScriptNamedCaptureGroup { struct ECMAScriptNamedCaptureGroup {
String name; Utf16FlyString name;
unsigned int index; unsigned int index;
}; };

View file

@ -47,8 +47,8 @@ ErrorOr<CompiledRustRegex, String> CompiledRustRegex::compile(Utf16View pattern,
if (groups) { if (groups) {
result.m_named_groups.ensure_capacity(group_count); result.m_named_groups.ensure_capacity(group_count);
for (unsigned int i = 0; i < group_count; ++i) { for (unsigned int i = 0; i < group_count; ++i) {
auto name = String::from_utf8({ reinterpret_cast<char const*>(groups[i].name), groups[i].name_len }); auto name = Utf16FlyString::from_utf8({ reinterpret_cast<char const*>(groups[i].name), groups[i].name_len });
result.m_named_groups.append(RustNamedCaptureGroup { MUST(name), groups[i].index }); result.m_named_groups.append(RustNamedCaptureGroup { move(name), groups[i].index });
} }
rust_regex_free_named_groups(groups, group_count); rust_regex_free_named_groups(groups, group_count);
} }

View file

@ -9,6 +9,7 @@
#include <AK/Error.h> #include <AK/Error.h>
#include <AK/Noncopyable.h> #include <AK/Noncopyable.h>
#include <AK/String.h> #include <AK/String.h>
#include <AK/Utf16FlyString.h>
#include <AK/Utf16View.h> #include <AK/Utf16View.h>
#include <AK/Vector.h> #include <AK/Vector.h>
#include <LibRegex/Export.h> #include <LibRegex/Export.h>
@ -17,7 +18,7 @@
namespace regex { namespace regex {
struct RustNamedCaptureGroup { struct RustNamedCaptureGroup {
String name; Utf16FlyString name;
unsigned int index; unsigned int index;
}; };

View file

@ -125,7 +125,7 @@ extern HashMap<bool*, Tuple<ByteString, ByteString, char>> g_extra_args;
struct ParserError { struct ParserError {
JS::ParserError error; JS::ParserError error;
ByteString hint; Utf16String hint;
}; };
struct JSFileResult { struct JSFileResult {
@ -236,7 +236,8 @@ inline AK::Result<GC::Ref<JS::Script>, ParserError> parse_script(StringView path
{ {
auto contents = load_entire_file(path); auto contents = load_entire_file(path);
auto source_text = Utf16String::from_utf8(StringView { contents.bytes() }); auto source_text = Utf16String::from_utf8(StringView { contents.bytes() });
auto script_or_errors = JS::Script::parse(source_text.utf16_view(), realm, path); auto display_filename = Utf16String::from_utf8(path);
auto script_or_errors = JS::Script::parse(source_text.utf16_view(), realm, path, display_filename.utf16_view());
if (script_or_errors.is_error()) { if (script_or_errors.is_error()) {
auto errors = script_or_errors.release_error(); auto errors = script_or_errors.release_error();
@ -249,11 +250,13 @@ inline AK::Result<GC::Ref<JS::Script>, ParserError> parse_script(StringView path
inline AK::Result<GC::Ref<JS::SourceTextModule>, ParserError> parse_module(StringView path, JS::Realm& realm) inline AK::Result<GC::Ref<JS::SourceTextModule>, ParserError> parse_module(StringView path, JS::Realm& realm)
{ {
auto contents = load_entire_file(path); auto contents = load_entire_file(path);
auto script_or_errors = JS::SourceTextModule::parse(contents, realm, path); auto source_text = Utf16String::from_utf8(StringView { contents.bytes() });
auto display_filename = Utf16String::from_utf8(path);
auto script_or_errors = JS::SourceTextModule::parse(source_text.utf16_view(), realm, path, display_filename.utf16_view());
if (script_or_errors.is_error()) { if (script_or_errors.is_error()) {
auto errors = script_or_errors.release_error(); auto errors = script_or_errors.release_error();
return ParserError { errors[0], errors[0].source_location_hint(Utf16String::from_utf8(contents)) }; return ParserError { errors[0], errors[0].source_location_hint(source_text) };
} }
return script_or_errors.release_value(); return script_or_errors.release_value();
@ -383,7 +386,7 @@ inline JSFileResult TestRunner::run_file_test(ByteString const& test_path)
auto result = parse_script(m_common_path, *realm); auto result = parse_script(m_common_path, *realm);
if (result.is_error()) { if (result.is_error()) {
warnln("Unable to parse test-common.js"); warnln("Unable to parse test-common.js");
warnln("{}", result.error().error.to_byte_string()); warnln("{}", result.error().error.to_utf16_string().to_byte_string());
warnln("{}", result.error().hint); warnln("{}", result.error().hint);
cleanup_and_exit(); cleanup_and_exit();
} }
@ -575,11 +578,11 @@ inline void TestRunner::print_file_result(JSFileResult const& file_result) const
outln(" ❌ The file failed to parse"); outln(" ❌ The file failed to parse");
outln(); outln();
print_modifiers({ FG_GRAY }); print_modifiers({ FG_GRAY });
for (auto& message : test_error.hint.split('\n', SplitBehavior::KeepEmpty)) { for (auto& message : test_error.hint.split_view('\n', SplitBehavior::KeepEmpty)) {
outln(" {}", message); outln(" {}", message);
} }
print_modifiers({ FG_RED }); print_modifiers({ FG_RED });
outln(" {}", test_error.error.to_byte_string()); outln(" {}", test_error.error.to_utf16_string().to_byte_string());
outln(); outln();
return; return;
} }

View file

@ -8,6 +8,7 @@
*/ */
#include <AK/StringBuilder.h> #include <AK/StringBuilder.h>
#include <AK/Utf16StringBuilder.h>
#include <AK/Utf8View.h> #include <AK/Utf8View.h>
#include <LibTextCodec/Decoder.h> #include <LibTextCodec/Decoder.h>
#include <RustFFI.h> #include <RustFFI.h>
@ -372,6 +373,19 @@ ErrorOr<String> convert_input_to_utf8_using_given_decoder_unless_there_is_a_byte
return output; return output;
} }
ErrorOr<Utf16String> convert_input_to_utf16_using_given_decoder_unless_there_is_a_byte_order_mark(Decoder& fallback_decoder, StringView input)
{
Decoder* actual_decoder = &fallback_decoder;
if (auto unicode_decoder = bom_sniff_to_decoder(input); unicode_decoder.has_value()) {
actual_decoder = &unicode_decoder.value();
input = input.substring_view(&unicode_decoder.value() == &s_utf8_decoder ? 3 : 2);
}
VERIFY(actual_decoder);
return actual_decoder->to_utf16(input);
}
ErrorOr<size_t> convert_input_to_utf16_length_using_given_decoder_unless_there_is_a_byte_order_mark(Decoder& fallback_decoder, StringView input) ErrorOr<size_t> convert_input_to_utf16_length_using_given_decoder_unless_there_is_a_byte_order_mark(Decoder& fallback_decoder, StringView input)
{ {
Decoder* actual_decoder = &fallback_decoder; Decoder* actual_decoder = &fallback_decoder;
@ -414,6 +428,16 @@ ErrorOr<String> Decoder::to_utf8(StringView input)
return builder.to_string_without_validation(); return builder.to_string_without_validation();
} }
ErrorOr<Utf16String> Decoder::to_utf16(StringView input)
{
Utf16StringBuilder builder;
TRY(process(input, [&builder](u32 c) -> ErrorOr<void> {
builder.append_code_point(c);
return {};
}));
return builder.to_string();
}
ErrorOr<size_t> Decoder::length_in_utf16_code_units(StringView input) ErrorOr<size_t> Decoder::length_in_utf16_code_units(StringView input)
{ {
size_t length = 0; size_t length = 0;

View file

@ -13,6 +13,7 @@
#include <AK/Noncopyable.h> #include <AK/Noncopyable.h>
#include <AK/Optional.h> #include <AK/Optional.h>
#include <AK/String.h> #include <AK/String.h>
#include <AK/Utf16String.h>
#include <LibTextCodec/Export.h> #include <LibTextCodec/Export.h>
#include <LibTextCodec/Forward.h> #include <LibTextCodec/Forward.h>
@ -22,6 +23,7 @@ class TEXTCODEC_API Decoder {
public: public:
virtual bool validate(StringView); virtual bool validate(StringView);
virtual ErrorOr<String> to_utf8(StringView); virtual ErrorOr<String> to_utf8(StringView);
virtual ErrorOr<Utf16String> to_utf16(StringView);
virtual ErrorOr<size_t> length_in_utf16_code_units(StringView); virtual ErrorOr<size_t> length_in_utf16_code_units(StringView);
ErrorOr<void> process_code_points(StringView, Function<ErrorOr<void>(u32)>); ErrorOr<void> process_code_points(StringView, Function<ErrorOr<void>(u32)>);
@ -57,6 +59,7 @@ TEXTCODEC_API Optional<Decoder&> bom_sniff_to_decoder(StringView);
// NOTE: This has an obnoxious name to discourage usage. Only use this if you absolutely must! For example, XHR in LibWeb uses this. // NOTE: This has an obnoxious name to discourage usage. Only use this if you absolutely must! For example, XHR in LibWeb uses this.
// This will use the given decoder unless there is a byte order mark in the input, in which we will instead use the appropriate Unicode decoder. // This will use the given decoder unless there is a byte order mark in the input, in which we will instead use the appropriate Unicode decoder.
TEXTCODEC_API ErrorOr<String> convert_input_to_utf8_using_given_decoder_unless_there_is_a_byte_order_mark(Decoder&, StringView); TEXTCODEC_API ErrorOr<String> convert_input_to_utf8_using_given_decoder_unless_there_is_a_byte_order_mark(Decoder&, StringView);
TEXTCODEC_API ErrorOr<Utf16String> convert_input_to_utf16_using_given_decoder_unless_there_is_a_byte_order_mark(Decoder&, StringView);
TEXTCODEC_API ErrorOr<size_t> convert_input_to_utf16_length_using_given_decoder_unless_there_is_a_byte_order_mark(Decoder&, StringView); TEXTCODEC_API ErrorOr<size_t> convert_input_to_utf16_length_using_given_decoder_unless_there_is_a_byte_order_mark(Decoder&, StringView);
TEXTCODEC_API StringView get_output_encoding(StringView encoding); TEXTCODEC_API StringView get_output_encoding(StringView encoding);

View file

@ -31,14 +31,14 @@ Utf16String usage_to_string(Usage usage)
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} }
static NonnullOwnPtr<icu::Locale> apply_usage_to_locale(icu::Locale const& locale, Usage usage, StringView collation) static NonnullOwnPtr<icu::Locale> apply_usage_to_locale(icu::Locale const& locale, Usage usage, Utf16View collation)
{ {
auto result = adopt_own(*locale.clone()); auto result = adopt_own(*locale.clone());
UErrorCode status = U_ZERO_ERROR; UErrorCode status = U_ZERO_ERROR;
switch (usage) { switch (usage) {
case Usage::Sort: case Usage::Sort:
result->setUnicodeKeywordValue("co", icu_string_piece(collation), status); result->setUnicodeKeywordValue("co", icu_string_piece(StringView { collation.bytes() }), status);
break; break;
case Usage::Search: case Usage::Search:
result->setUnicodeKeywordValue("co", "search", status); result->setUnicodeKeywordValue("co", "search", status);
@ -205,9 +205,9 @@ private:
}; };
NonnullOwnPtr<Collator> Collator::create( NonnullOwnPtr<Collator> Collator::create(
StringView locale, Utf16View locale,
Usage usage, Usage usage,
StringView collation, Utf16View collation,
Optional<Sensitivity> sensitivity, Optional<Sensitivity> sensitivity,
CaseFirst case_first, CaseFirst case_first,
bool numeric, bool numeric,
@ -215,7 +215,7 @@ NonnullOwnPtr<Collator> Collator::create(
{ {
UErrorCode status = U_ZERO_ERROR; UErrorCode status = U_ZERO_ERROR;
auto locale_data = LocaleData::for_locale(locale); auto locale_data = LocaleData::for_locale(locale.bytes());
VERIFY(locale_data.has_value()); VERIFY(locale_data.has_value());
auto locale_with_usage = apply_usage_to_locale(locale_data->locale(), usage, collation); auto locale_with_usage = apply_usage_to_locale(locale_data->locale(), usage, collation);

View file

@ -7,7 +7,6 @@
#pragma once #pragma once
#include <AK/NonnullOwnPtr.h> #include <AK/NonnullOwnPtr.h>
#include <AK/StringView.h>
#include <AK/Utf16String.h> #include <AK/Utf16String.h>
#include <AK/Utf16View.h> #include <AK/Utf16View.h>
@ -40,9 +39,9 @@ Utf16String case_first_to_string(CaseFirst);
class Collator { class Collator {
public: public:
static NonnullOwnPtr<Collator> create( static NonnullOwnPtr<Collator> create(
StringView locale, Utf16View locale,
Usage, Usage,
StringView collation, Utf16View collation,
Optional<Sensitivity>, Optional<Sensitivity>,
CaseFirst, CaseFirst,
bool numeric, bool numeric,

View file

@ -521,8 +521,8 @@ static T find_regional_values_for_locale(StringView locale, GetRegionalValues&&
return return_default_values(); return return_default_values();
if (!language->region.has_value()) { if (!language->region.has_value()) {
if (auto maximized = add_likely_subtags(language->to_string()); maximized.has_value()) if (auto maximized = add_likely_subtags(language->to_utf16_string()); maximized.has_value())
language = parse_unicode_language_id(maximized->utf16_view().bytes()); language = parse_unicode_language_id(maximized->utf16_view());
} }
if (!language.has_value() || !language->region.has_value()) if (!language.has_value() || !language->region.has_value())
@ -622,7 +622,7 @@ static bool apply_hour_cycle_to_skeleton(icu::UnicodeString& skeleton, Optional<
return changed_hour_cycle; return changed_hour_cycle;
} }
static void apply_time_zone_to_formatter(icu::SimpleDateFormat& formatter, icu::Locale const& locale, StringView time_zone_identifier) static void apply_time_zone_to_formatter(icu::SimpleDateFormat& formatter, icu::Locale const& locale, Utf16View time_zone_identifier)
{ {
UErrorCode status = U_ZERO_ERROR; UErrorCode status = U_ZERO_ERROR;
@ -672,7 +672,7 @@ static bool is_formatted_range_actually_a_range(icu::FormattedDateInterval const
class DateTimeFormatImpl : public DateTimeFormat { class DateTimeFormatImpl : public DateTimeFormat {
public: public:
DateTimeFormatImpl(icu::Locale& locale, icu::UnicodeString const& pattern, StringView time_zone_identifier, NonnullOwnPtr<icu::SimpleDateFormat> formatter) DateTimeFormatImpl(icu::Locale& locale, icu::UnicodeString const& pattern, Utf16View time_zone_identifier, NonnullOwnPtr<icu::SimpleDateFormat> formatter)
: m_locale(locale) : m_locale(locale)
, m_pattern(CalendarPattern::create_from_pattern(icu_string_to_string(pattern))) , m_pattern(CalendarPattern::create_from_pattern(icu_string_to_string(pattern)))
, m_formatter(move(formatter)) , m_formatter(move(formatter))
@ -883,8 +883,8 @@ private:
}; };
NonnullOwnPtr<DateTimeFormat> DateTimeFormat::create_for_date_and_time_style( NonnullOwnPtr<DateTimeFormat> DateTimeFormat::create_for_date_and_time_style(
StringView locale, Utf16View locale,
StringView time_zone_identifier, Utf16View time_zone_identifier,
Optional<HourCycle> const& hour_cycle, Optional<HourCycle> const& hour_cycle,
Optional<bool> const& hour12, Optional<bool> const& hour12,
Optional<DateTimeStyle> const& date_style, Optional<DateTimeStyle> const& date_style,
@ -892,7 +892,7 @@ NonnullOwnPtr<DateTimeFormat> DateTimeFormat::create_for_date_and_time_style(
{ {
UErrorCode status = U_ZERO_ERROR; UErrorCode status = U_ZERO_ERROR;
auto locale_data = LocaleData::for_locale(locale); auto locale_data = LocaleData::for_locale(locale.bytes());
VERIFY(locale_data.has_value()); VERIFY(locale_data.has_value());
auto formatter = adopt_own(*as<icu::SimpleDateFormat>([&]() { auto formatter = adopt_own(*as<icu::SimpleDateFormat>([&]() {
@ -931,13 +931,13 @@ NonnullOwnPtr<DateTimeFormat> DateTimeFormat::create_for_date_and_time_style(
} }
NonnullOwnPtr<DateTimeFormat> DateTimeFormat::create_for_pattern_options( NonnullOwnPtr<DateTimeFormat> DateTimeFormat::create_for_pattern_options(
StringView locale, Utf16View locale,
StringView time_zone_identifier, Utf16View time_zone_identifier,
CalendarPattern const& options) CalendarPattern const& options)
{ {
UErrorCode status = U_ZERO_ERROR; UErrorCode status = U_ZERO_ERROR;
auto locale_data = LocaleData::for_locale(locale); auto locale_data = LocaleData::for_locale(locale.bytes());
VERIFY(locale_data.has_value()); VERIFY(locale_data.has_value());
icu::UnicodeString pattern; icu::UnicodeString pattern;

View file

@ -9,7 +9,6 @@
#include <AK/IterationDecision.h> #include <AK/IterationDecision.h>
#include <AK/Optional.h> #include <AK/Optional.h>
#include <AK/String.h> #include <AK/String.h>
#include <AK/StringView.h>
#include <AK/Time.h> #include <AK/Time.h>
#include <AK/Types.h> #include <AK/Types.h>
#include <AK/Utf16String.h> #include <AK/Utf16String.h>
@ -139,16 +138,16 @@ struct CalendarPattern {
class DateTimeFormat { class DateTimeFormat {
public: public:
static NonnullOwnPtr<DateTimeFormat> create_for_date_and_time_style( static NonnullOwnPtr<DateTimeFormat> create_for_date_and_time_style(
StringView locale, Utf16View locale,
StringView time_zone_identifier, Utf16View time_zone_identifier,
Optional<HourCycle> const& hour_cycle, Optional<HourCycle> const& hour_cycle,
Optional<bool> const& hour12, Optional<bool> const& hour12,
Optional<DateTimeStyle> const& date_style, Optional<DateTimeStyle> const& date_style,
Optional<DateTimeStyle> const& time_style); Optional<DateTimeStyle> const& time_style);
static NonnullOwnPtr<DateTimeFormat> create_for_pattern_options( static NonnullOwnPtr<DateTimeFormat> create_for_pattern_options(
StringView locale, Utf16View locale,
StringView time_zone_identifier, Utf16View time_zone_identifier,
CalendarPattern const&); CalendarPattern const&);
virtual ~DateTimeFormat() = default; virtual ~DateTimeFormat() = default;

View file

@ -5,6 +5,7 @@
*/ */
#include <AK/Array.h> #include <AK/Array.h>
#include <AK/ByteString.h>
#include <LibUnicode/DisplayNames.h> #include <LibUnicode/DisplayNames.h>
#include <LibUnicode/ICU.h> #include <LibUnicode/ICU.h>
@ -37,19 +38,13 @@ Utf16String language_display_to_string(LanguageDisplay language_display)
} }
} }
static String string_from_ascii_view(Utf16View string) Optional<Utf16String> language_display_name(StringView locale, StringView language, LanguageDisplay display)
{
return MUST(string.to_utf8());
}
Optional<Utf16String> language_display_name(StringView locale, Utf16View language, LanguageDisplay display)
{ {
auto locale_data = LocaleData::for_locale(locale); auto locale_data = LocaleData::for_locale(locale);
if (!locale_data.has_value()) if (!locale_data.has_value())
return {}; return {};
auto language_string = string_from_ascii_view(language); auto language_data = LocaleData::for_locale(language);
auto language_data = LocaleData::for_locale(language_string.bytes_as_string_view());
if (!language_data.has_value()) if (!language_data.has_value())
return {}; return {};
@ -63,7 +58,7 @@ Optional<Utf16String> language_display_name(StringView locale, Utf16View languag
return icu_string_to_utf16_string(result); return icu_string_to_utf16_string(result);
} }
Optional<Utf16String> region_display_name(StringView locale, Utf16View region) Optional<Utf16String> region_display_name(StringView locale, StringView region)
{ {
UErrorCode status = U_ZERO_ERROR; UErrorCode status = U_ZERO_ERROR;
@ -71,8 +66,7 @@ Optional<Utf16String> region_display_name(StringView locale, Utf16View region)
if (!locale_data.has_value()) if (!locale_data.has_value())
return {}; return {};
auto region_string = string_from_ascii_view(region); auto icu_region = icu::LocaleBuilder().setRegion(icu_string_piece(region)).build(status);
auto icu_region = icu::LocaleBuilder().setRegion(icu_string_piece(region_string.bytes_as_string_view())).build(status);
if (icu_failure(status)) if (icu_failure(status))
return {}; return {};
@ -82,7 +76,7 @@ Optional<Utf16String> region_display_name(StringView locale, Utf16View region)
return icu_string_to_utf16_string(result); return icu_string_to_utf16_string(result);
} }
Optional<Utf16String> script_display_name(StringView locale, Utf16View script) Optional<Utf16String> script_display_name(StringView locale, StringView script)
{ {
UErrorCode status = U_ZERO_ERROR; UErrorCode status = U_ZERO_ERROR;
@ -90,8 +84,7 @@ Optional<Utf16String> script_display_name(StringView locale, Utf16View script)
if (!locale_data.has_value()) if (!locale_data.has_value())
return {}; return {};
auto script_string = string_from_ascii_view(script); auto icu_script = icu::LocaleBuilder().setScript(icu_string_piece(script)).build(status);
auto icu_script = icu::LocaleBuilder().setScript(icu_string_piece(script_string.bytes_as_string_view())).build(status);
if (icu_failure(status)) if (icu_failure(status))
return {}; return {};
@ -101,27 +94,26 @@ Optional<Utf16String> script_display_name(StringView locale, Utf16View script)
return icu_string_to_utf16_string(result); return icu_string_to_utf16_string(result);
} }
Optional<Utf16String> calendar_display_name(StringView locale, Utf16View calendar) Optional<Utf16String> calendar_display_name(StringView locale, StringView calendar)
{ {
auto locale_data = LocaleData::for_locale(locale); auto locale_data = LocaleData::for_locale(locale);
if (!locale_data.has_value()) if (!locale_data.has_value())
return {}; return {};
if (calendar == "gregory"sv) if (calendar == "gregory"sv)
return calendar_display_name(locale, "gregorian"sv); calendar = "gregorian"sv;
if (calendar == "islamicc"sv) if (calendar == "islamicc"sv)
return calendar_display_name(locale, "islamic-civil"sv); calendar = "islamic-civil"sv;
if (calendar == "ethioaa"sv) if (calendar == "ethioaa"sv)
return calendar_display_name(locale, "ethiopic-amete-alem"sv); calendar = "ethiopic-amete-alem"sv;
icu::UnicodeString result; icu::UnicodeString result;
auto calendar_string = string_from_ascii_view(calendar); locale_data->standard_display_names().keyValueDisplayName("calendar", ByteString(calendar).characters(), result);
locale_data->standard_display_names().keyValueDisplayName("calendar", calendar_string.to_byte_string().characters(), result);
return icu_string_to_utf16_string(result); return icu_string_to_utf16_string(result);
} }
static constexpr UDateTimePatternField icu_date_time_field(Utf16View field) static constexpr UDateTimePatternField icu_date_time_field(StringView field)
{ {
if (field == "day"sv) if (field == "day"sv)
return UDATPG_DAY_FIELD; return UDATPG_DAY_FIELD;
@ -164,7 +156,7 @@ static constexpr UDateTimePGDisplayWidth icu_date_time_style(Style style)
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} }
Optional<Utf16String> date_time_field_display_name(StringView locale, Utf16View field, Style style) Optional<Utf16String> date_time_field_display_name(StringView locale, StringView field, Style style)
{ {
auto locale_data = LocaleData::for_locale(locale); auto locale_data = LocaleData::for_locale(locale);
if (!locale_data.has_value()) if (!locale_data.has_value())
@ -195,29 +187,16 @@ Optional<Utf16String> time_zone_display_name(StringView locale, StringView time_
return icu_string_to_utf16_string(time_zone_name); return icu_string_to_utf16_string(time_zone_name);
} }
template<typename ViewType> static constexpr Array<UChar, 4> icu_currency_code(StringView currency)
static Array<UChar, 4> icu_currency_code(ViewType currency)
{ {
if constexpr (IsSame<ViewType, Utf16View>)
VERIFY(currency.length_in_code_units() == 3);
else
VERIFY(currency.length() == 3); VERIFY(currency.length() == 3);
if constexpr (IsSame<ViewType, Utf16View>) {
return to_array({
static_cast<UChar>(currency.code_unit_at(0)),
static_cast<UChar>(currency.code_unit_at(1)),
static_cast<UChar>(currency.code_unit_at(2)),
u'\0',
});
} else {
return to_array({ return to_array({
static_cast<UChar>(currency[0]), static_cast<UChar>(currency[0]),
static_cast<UChar>(currency[1]), static_cast<UChar>(currency[1]),
static_cast<UChar>(currency[2]), static_cast<UChar>(currency[2]),
u'\0', u'\0',
}); });
}
} }
static constexpr UCurrNameStyle icu_currency_style(Style style) static constexpr UCurrNameStyle icu_currency_style(Style style)
@ -234,7 +213,7 @@ static constexpr UCurrNameStyle icu_currency_style(Style style)
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} }
Optional<Utf16String> currency_display_name(StringView locale, Utf16View currency, Style style) Optional<Utf16String> currency_display_name(StringView locale, StringView currency, Style style)
{ {
UErrorCode status = U_ZERO_ERROR; UErrorCode status = U_ZERO_ERROR;
@ -255,25 +234,4 @@ Optional<Utf16String> currency_display_name(StringView locale, Utf16View currenc
return icu_string_to_utf16_string(result, length); return icu_string_to_utf16_string(result, length);
} }
Optional<Utf16String> currency_numeric_display_name(StringView locale, StringView currency)
{
UErrorCode status = U_ZERO_ERROR;
auto locale_data = LocaleData::for_locale(locale);
if (!locale_data.has_value())
return {};
auto icu_currency = icu_currency_code(currency);
i32 length = 0;
UChar const* result = ucurr_getPluralName(icu_currency.data(), locale_data->locale().getName(), nullptr, "other", &length, &status);
if (icu_failure(status))
return {};
if ((status == U_USING_DEFAULT_WARNING) && (result == icu_currency.data()))
return {};
return icu_string_to_utf16_string(result, length);
}
} }

View file

@ -23,13 +23,12 @@ enum class LanguageDisplay {
LanguageDisplay language_display_from_string(Utf16View language_display); LanguageDisplay language_display_from_string(Utf16View language_display);
Utf16String language_display_to_string(LanguageDisplay language_display); Utf16String language_display_to_string(LanguageDisplay language_display);
Optional<Utf16String> language_display_name(StringView locale, Utf16View language, LanguageDisplay); Optional<Utf16String> language_display_name(StringView locale, StringView language, LanguageDisplay);
Optional<Utf16String> region_display_name(StringView locale, Utf16View region); Optional<Utf16String> region_display_name(StringView locale, StringView region);
Optional<Utf16String> script_display_name(StringView locale, Utf16View script); Optional<Utf16String> script_display_name(StringView locale, StringView script);
Optional<Utf16String> calendar_display_name(StringView locale, Utf16View calendar); Optional<Utf16String> calendar_display_name(StringView locale, StringView calendar);
Optional<Utf16String> date_time_field_display_name(StringView locale, Utf16View field, Style); Optional<Utf16String> date_time_field_display_name(StringView locale, StringView field, Style);
Optional<Utf16String> time_zone_display_name(StringView locale, StringView time_zone_identifier, TimeZoneOffset::InDST, double time); Optional<Utf16String> time_zone_display_name(StringView locale, StringView time_zone_identifier, TimeZoneOffset::InDST, double time);
Optional<Utf16String> currency_display_name(StringView locale, Utf16View currency, Style); Optional<Utf16String> currency_display_name(StringView locale, StringView currency, Style);
Optional<Utf16String> currency_numeric_display_name(StringView locale, StringView currency);
} }

View file

@ -38,7 +38,7 @@ DigitalFormat digital_format(Utf16View locale)
rounding_options.min_significant_digits = 1; rounding_options.min_significant_digits = 1;
rounding_options.max_significant_digits = 2; rounding_options.max_significant_digits = 2;
auto number_formatter = NumberFormat::create(locale.bytes(), {}, rounding_options); auto number_formatter = NumberFormat::create(locale, {}, rounding_options);
auto icu_locale = adopt_own(*locale_data->locale().clone()); auto icu_locale = adopt_own(*locale_data->locale().clone());
icu_locale->setUnicodeKeywordValue("nu", "latn", status); icu_locale->setUnicodeKeywordValue("nu", "latn", status);

View file

@ -152,11 +152,11 @@ private:
NonnullOwnPtr<icu::ListFormatter> m_formatter; NonnullOwnPtr<icu::ListFormatter> m_formatter;
}; };
NonnullOwnPtr<ListFormat> ListFormat::create(StringView locale, ListFormatType type, Style style) NonnullOwnPtr<ListFormat> ListFormat::create(Utf16View locale, ListFormatType type, Style style)
{ {
UErrorCode status = U_ZERO_ERROR; UErrorCode status = U_ZERO_ERROR;
auto locale_data = LocaleData::for_locale(locale); auto locale_data = LocaleData::for_locale(locale.bytes());
VERIFY(locale_data.has_value()); VERIFY(locale_data.has_value());
auto formatter = adopt_own(*icu::ListFormatter::createInstance(locale_data->locale(), icu_list_format_type(type), icu_list_format_width(style), status)); auto formatter = adopt_own(*icu::ListFormatter::createInstance(locale_data->locale(), icu_list_format_type(type), icu_list_format_width(style), status));

View file

@ -24,7 +24,7 @@ Utf16String list_format_type_to_string(ListFormatType);
class ListFormat { class ListFormat {
public: public:
static NonnullOwnPtr<ListFormat> create(StringView locale, ListFormatType, Style); static NonnullOwnPtr<ListFormat> create(Utf16View locale, ListFormatType, Style);
virtual ~ListFormat() = default; virtual ~ListFormat() = default;
struct Partition { struct Partition {

View file

@ -713,11 +713,11 @@ static void apply_extensions_to_locale(icu::Locale& locale, icu::Locale const& l
verify_icu_success(status); verify_icu_success(status);
} }
Optional<Utf16String> add_likely_subtags(StringView locale) Optional<Utf16String> add_likely_subtags(Utf16View locale)
{ {
UErrorCode status = U_ZERO_ERROR; UErrorCode status = U_ZERO_ERROR;
auto locale_data = LocaleData::for_locale(locale); auto locale_data = LocaleData::for_locale(locale.bytes());
if (!locale_data.has_value()) if (!locale_data.has_value())
return {}; return {};
@ -740,11 +740,11 @@ Optional<Utf16String> add_likely_subtags(StringView locale)
return Utf16String::from_ascii_without_validation(result.string_view().bytes()); return Utf16String::from_ascii_without_validation(result.string_view().bytes());
} }
Optional<Utf16String> remove_likely_subtags(StringView locale) Optional<Utf16String> remove_likely_subtags(Utf16View locale)
{ {
UErrorCode status = U_ZERO_ERROR; UErrorCode status = U_ZERO_ERROR;
auto locale_data = LocaleData::for_locale(locale); auto locale_data = LocaleData::for_locale(locale.bytes());
if (!locale_data.has_value()) if (!locale_data.has_value())
return {}; return {};

View file

@ -193,8 +193,8 @@ Style style_from_string(StringView style);
Style style_from_string(Utf16View style); Style style_from_string(Utf16View style);
Utf16String style_to_string(Style style); Utf16String style_to_string(Style style);
Optional<Utf16String> add_likely_subtags(StringView); Optional<Utf16String> add_likely_subtags(Utf16View);
Optional<Utf16String> remove_likely_subtags(StringView); Optional<Utf16String> remove_likely_subtags(Utf16View);
bool is_locale_character_ordering_right_to_left(Utf16View locale); bool is_locale_character_ordering_right_to_left(Utf16View locale);

View file

@ -986,11 +986,11 @@ private:
}; };
NonnullOwnPtr<NumberFormat> NumberFormat::create( NonnullOwnPtr<NumberFormat> NumberFormat::create(
StringView locale, Utf16View locale,
DisplayOptions const& display_options, DisplayOptions const& display_options,
RoundingOptions const& rounding_options) RoundingOptions const& rounding_options)
{ {
auto locale_data = LocaleData::for_locale(locale); auto locale_data = LocaleData::for_locale(locale.bytes());
VERIFY(locale_data.has_value()); VERIFY(locale_data.has_value());
auto formatter = icu::number::NumberFormatter::withLocale(locale_data->locale()); auto formatter = icu::number::NumberFormatter::withLocale(locale_data->locale());

View file

@ -151,7 +151,7 @@ struct RoundingOptions {
class NumberFormat { class NumberFormat {
public: public:
static NonnullOwnPtr<NumberFormat> create( static NonnullOwnPtr<NumberFormat> create(
StringView locale, Utf16View locale,
DisplayOptions const&, DisplayOptions const&,
RoundingOptions const&); RoundingOptions const&);

View file

@ -264,11 +264,11 @@ private:
NonnullOwnPtr<icu::RelativeDateTimeFormatter> m_formatter; NonnullOwnPtr<icu::RelativeDateTimeFormatter> m_formatter;
}; };
NonnullOwnPtr<RelativeTimeFormat> RelativeTimeFormat::create(StringView locale, Style style) NonnullOwnPtr<RelativeTimeFormat> RelativeTimeFormat::create(Utf16View locale, Style style)
{ {
UErrorCode status = U_ZERO_ERROR; UErrorCode status = U_ZERO_ERROR;
auto locale_data = LocaleData::for_locale(locale); auto locale_data = LocaleData::for_locale(locale.bytes());
VERIFY(locale_data.has_value()); VERIFY(locale_data.has_value());
auto* number_formatter = icu::NumberFormat::createInstance(locale_data->locale(), UNUM_DECIMAL, status); auto* number_formatter = icu::NumberFormat::createInstance(locale_data->locale(), UNUM_DECIMAL, status);

View file

@ -42,7 +42,7 @@ Utf16String numeric_display_to_string(NumericDisplay);
class RelativeTimeFormat { class RelativeTimeFormat {
public: public:
static NonnullOwnPtr<RelativeTimeFormat> create(StringView locale, Style style); static NonnullOwnPtr<RelativeTimeFormat> create(Utf16View locale, Style style);
virtual ~RelativeTimeFormat() = default; virtual ~RelativeTimeFormat() = default;
struct Partition { struct Partition {

View file

@ -688,14 +688,14 @@ private:
NonnullOwnPtr<Segmenter> Segmenter::create(SegmenterGranularity segmenter_granularity) NonnullOwnPtr<Segmenter> Segmenter::create(SegmenterGranularity segmenter_granularity)
{ {
return Segmenter::create(default_locale().bytes(), segmenter_granularity); return Segmenter::create(default_locale(), segmenter_granularity);
} }
NonnullOwnPtr<Segmenter> Segmenter::create(StringView locale, SegmenterGranularity segmenter_granularity) NonnullOwnPtr<Segmenter> Segmenter::create(Utf16View locale, SegmenterGranularity segmenter_granularity)
{ {
UErrorCode status = U_ZERO_ERROR; UErrorCode status = U_ZERO_ERROR;
auto locale_data = LocaleData::for_locale(locale); auto locale_data = LocaleData::for_locale(locale.bytes());
VERIFY(locale_data.has_value()); VERIFY(locale_data.has_value());
auto segmenter = adopt_own_if_nonnull([&]() { auto segmenter = adopt_own_if_nonnull([&]() {

View file

@ -29,7 +29,7 @@ Utf16String segmenter_granularity_to_string(SegmenterGranularity);
class Segmenter { class Segmenter {
public: public:
static NonnullOwnPtr<Segmenter> create(SegmenterGranularity segmenter_granularity); static NonnullOwnPtr<Segmenter> create(SegmenterGranularity segmenter_granularity);
static NonnullOwnPtr<Segmenter> create(StringView locale, SegmenterGranularity segmenter_granularity); static NonnullOwnPtr<Segmenter> create(Utf16View locale, SegmenterGranularity segmenter_granularity);
static NonnullOwnPtr<Segmenter> create_for_ascii_grapheme(size_t length); static NonnullOwnPtr<Segmenter> create_for_ascii_grapheme(size_t length);
static OwnPtr<Segmenter> try_create_for_ascii_line(Utf16View const&); static OwnPtr<Segmenter> try_create_for_ascii_line(Utf16View const&);
virtual ~Segmenter() = default; virtual ~Segmenter() = default;

View file

@ -164,7 +164,7 @@ Vector<Utf16String> available_time_zones_in_region(Utf16View region)
return icu_available_time_zones(StringView { region_buffer.data(), region.length_in_code_units() }); return icu_available_time_zones(StringView { region_buffer.data(), region.length_in_code_units() });
} }
Optional<Utf16String> resolve_primary_time_zone(StringView time_zone) Optional<Utf16String> resolve_primary_time_zone(Utf16View time_zone)
{ {
UErrorCode status = U_ZERO_ERROR; UErrorCode status = U_ZERO_ERROR;
@ -186,7 +186,7 @@ static UDate to_icu_time(UnixDateTime time)
return clamp(static_cast<UDate>(time.milliseconds_since_epoch()), min_time, max_time); return clamp(static_cast<UDate>(time.milliseconds_since_epoch()), min_time, max_time);
} }
Optional<TimeZoneOffset> time_zone_offset(StringView time_zone, UnixDateTime time) Optional<TimeZoneOffset> time_zone_offset(Utf16View time_zone, UnixDateTime time)
{ {
UErrorCode status = U_ZERO_ERROR; UErrorCode status = U_ZERO_ERROR;
@ -209,7 +209,7 @@ Optional<TimeZoneOffset> time_zone_offset(StringView time_zone, UnixDateTime tim
}; };
} }
Vector<TimeZoneOffset> disambiguated_time_zone_offsets(StringView time_zone, UnixDateTime time) Vector<TimeZoneOffset> disambiguated_time_zone_offsets(Utf16View time_zone, UnixDateTime time)
{ {
UErrorCode status = U_ZERO_ERROR; UErrorCode status = U_ZERO_ERROR;
@ -253,7 +253,7 @@ Vector<TimeZoneOffset> disambiguated_time_zone_offsets(StringView time_zone, Uni
return offsets; return offsets;
} }
Optional<TimeZoneTransition> get_time_zone_transition(StringView time_zone, UnixDateTime time, TimeZoneTransition::Options options) Optional<TimeZoneTransition> get_time_zone_transition(Utf16View time_zone, UnixDateTime time, TimeZoneTransition::Options options)
{ {
auto time_zone_data = TimeZoneData::for_time_zone(time_zone); auto time_zone_data = TimeZoneData::for_time_zone(time_zone);
if (!time_zone_data.has_value()) if (!time_zone_data.has_value())

View file

@ -56,9 +56,9 @@ ErrorOr<void> set_current_time_zone(Utf16View);
void clear_system_time_zone_cache(); void clear_system_time_zone_cache();
Vector<Utf16String> const& available_time_zones(); Vector<Utf16String> const& available_time_zones();
Vector<Utf16String> available_time_zones_in_region(Utf16View region); Vector<Utf16String> available_time_zones_in_region(Utf16View region);
Optional<Utf16String> resolve_primary_time_zone(StringView time_zone); Optional<Utf16String> resolve_primary_time_zone(Utf16View time_zone);
Optional<TimeZoneOffset> time_zone_offset(StringView time_zone, UnixDateTime time); Optional<TimeZoneOffset> time_zone_offset(Utf16View time_zone, UnixDateTime time);
Vector<TimeZoneOffset> disambiguated_time_zone_offsets(StringView time_zone, UnixDateTime time); Vector<TimeZoneOffset> disambiguated_time_zone_offsets(Utf16View time_zone, UnixDateTime time);
Optional<TimeZoneTransition> get_time_zone_transition(StringView time_zone, UnixDateTime time, TimeZoneTransition::Options options); Optional<TimeZoneTransition> get_time_zone_transition(Utf16View time_zone, UnixDateTime time, TimeZoneTransition::Options options);
} }

View file

@ -7,6 +7,7 @@
#pragma once #pragma once
#include <AK/Optional.h> #include <AK/Optional.h>
#include <AK/Utf16String.h>
#include <LibJS/Runtime/VM.h> #include <LibJS/Runtime/VM.h>
#include <LibWeb/WebIDL/ExceptionOr.h> #include <LibWeb/WebIDL/ExceptionOr.h>
@ -62,11 +63,11 @@ ALWAYS_INLINE JS::Completion exception_to_throw_completion(JS::VM& vm, auto&& ex
{ {
return exception.visit( return exception.visit(
[&](WebIDL::SimpleException const& exception) { [&](WebIDL::SimpleException const& exception) {
auto message = exception.message.visit([](auto const& s) -> StringView { return s; }); auto message = exception.message.visit([](auto const& s) { return Utf16String::from_utf8(s); });
switch (exception.type) { switch (exception.type) {
#define E(x) \ #define E(x) \
case WebIDL::SimpleExceptionType::x: \ case WebIDL::SimpleExceptionType::x: \
return vm.template throw_completion<JS::x>(message); return vm.template throw_completion<JS::x>(message.utf16_view());
ENUMERATE_SIMPLE_WEBIDL_EXCEPTION_TYPES(E) ENUMERATE_SIMPLE_WEBIDL_EXCEPTION_TYPES(E)

View file

@ -104,7 +104,7 @@ void initialize_main_thread_vm(AgentType type)
main_thread_vm_ptr()->set_agent(create_agent(main_thread_vm_ptr()->heap(), type)); main_thread_vm_ptr()->set_agent(create_agent(main_thread_vm_ptr()->heap(), type));
main_thread_vm_ptr()->on_unimplemented_property_access = [](auto const& object, auto const& property_key) { main_thread_vm_ptr()->on_unimplemented_property_access = [](auto const& object, auto const& property_key) {
dbgln("FIXME: Unimplemented IDL interface: '{}.{}'", object.class_name(), property_key.to_string()); dbgln("FIXME: Unimplemented IDL interface: '{}.{}'", object.class_name(), property_key.to_utf16_string());
}; };
// NOTE: We intentionally leak the main thread JavaScript VM. // NOTE: We intentionally leak the main thread JavaScript VM.
@ -115,7 +115,7 @@ void initialize_main_thread_vm(AgentType type)
main_thread_vm_ptr()->host_ensure_can_add_private_element = [](JS::Object const& object) -> JS::ThrowCompletionOr<void> { main_thread_vm_ptr()->host_ensure_can_add_private_element = [](JS::Object const& object) -> JS::ThrowCompletionOr<void> {
// 1. If O is a WindowProxy object, or implements Location, then return ThrowCompletion(a new TypeError). // 1. If O is a WindowProxy object, or implements Location, then return ThrowCompletion(a new TypeError).
if (is<HTML::WindowProxy>(object) || is<HTML::Location>(object)) if (is<HTML::WindowProxy>(object) || is<HTML::Location>(object))
return main_thread_vm_ptr()->throw_completion<JS::TypeError>("Cannot add private elements to window or location object"sv); return main_thread_vm_ptr()->throw_completion<JS::TypeError>("Cannot add private elements to window or location object"_utf16);
// 2. Return NormalCompletion(unused). // 2. Return NormalCompletion(unused).
return {}; return {};
@ -440,7 +440,7 @@ void initialize_main_thread_vm(AgentType type)
// 2. If settingsObject's global object implements WorkletGlobalScope or ServiceWorkerGlobalScope and loadState is undefined, then: // 2. If settingsObject's global object implements WorkletGlobalScope or ServiceWorkerGlobalScope and loadState is undefined, then:
if ((is<HTML::WorkletGlobalScope>(settings_object->global_object()) || is<ServiceWorker::ServiceWorkerGlobalScope>(settings_object->global_object())) && !load_state) { if ((is<HTML::WorkletGlobalScope>(settings_object->global_object()) || is<ServiceWorker::ServiceWorkerGlobalScope>(settings_object->global_object())) && !load_state) {
// 1. Perform FinishLoadingImportedModule(referrer, moduleRequest, payload, ThrowCompletion(a new TypeError)). // 1. Perform FinishLoadingImportedModule(referrer, moduleRequest, payload, ThrowCompletion(a new TypeError)).
auto completion = JS::throw_completion(JS::TypeError::create(settings_object->realm(), "Dynamic Import not available for Worklets or ServiceWorkers"_string)); auto completion = JS::throw_completion(JS::TypeError::create(settings_object->realm(), "Dynamic Import not available for Worklets or ServiceWorkers"_utf16));
JS::finish_loading_imported_module(referrer, module_request, payload, completion); JS::finish_loading_imported_module(referrer, module_request, payload, completion);
// 2. Return. // 2. Return.
@ -481,7 +481,7 @@ void initialize_main_thread_vm(AgentType type)
continue; continue;
// 1. Let error be a new SyntaxError exception. // 1. Let error be a new SyntaxError exception.
auto error = JS::SyntaxError::create(settings_object->realm(), "Module request attributes must only contain a type attribute"_string); auto error = JS::SyntaxError::create(settings_object->realm(), "Module request attributes must only contain a type attribute"_utf16);
// 2. If loadState is not undefined and loadState.[[ErrorToRethrow]] is null, set loadState.[[ErrorToRethrow]] to error. // 2. If loadState is not undefined and loadState.[[ErrorToRethrow]] is null, set loadState.[[ErrorToRethrow]] to error.
if (auto* load_state_as_fetch_context = as<HTML::FetchContext>(load_state.ptr()); if (auto* load_state_as_fetch_context = as<HTML::FetchContext>(load_state.ptr());
@ -523,7 +523,7 @@ void initialize_main_thread_vm(AgentType type)
// 5. If the result of running the module type allowed steps given moduleType and settingsObject is false, then: // 5. If the result of running the module type allowed steps given moduleType and settingsObject is false, then:
if (!HTML::module_type_allowed(settings_object, module_type)) { if (!HTML::module_type_allowed(settings_object, module_type)) {
// 1. Let error be a new TypeError exception. // 1. Let error be a new TypeError exception.
auto error = JS::TypeError::create(settings_object->realm(), MUST(String::formatted("Module type '{}' is not supported", module_type))); auto error = JS::TypeError::create(settings_object->realm(), Utf16String::formatted("Module type '{}' is not supported", module_type));
// 2. If loadState is not undefined and loadState.[[ErrorToRethrow]] is null, set loadState.[[ErrorToRethrow]] to error. // 2. If loadState is not undefined and loadState.[[ErrorToRethrow]] is null, set loadState.[[ErrorToRethrow]] to error.
if (auto* load_state_as_fetch_context = as<HTML::FetchContext>(load_state.ptr()); if (auto* load_state_as_fetch_context = as<HTML::FetchContext>(load_state.ptr());
@ -605,7 +605,7 @@ void initialize_main_thread_vm(AgentType type)
auto completion = [&]() -> JS::ThrowCompletionOr<GC::Ref<JS::Module>> { auto completion = [&]() -> JS::ThrowCompletionOr<GC::Ref<JS::Module>> {
// 2. If moduleScript is null, then set completion to ThrowCompletion(a new TypeError). // 2. If moduleScript is null, then set completion to ThrowCompletion(a new TypeError).
if (!module_script) { if (!module_script) {
return JS::throw_completion(JS::TypeError::create(realm, ByteString::formatted("Loading imported module '{}' failed.", module_request.module_specifier))); return JS::throw_completion(JS::TypeError::create(realm, Utf16String::formatted("Loading imported module '{}' failed.", module_request.module_specifier)));
} }
// 3. Otherwise, if moduleScript's parse error is not null, then: // 3. Otherwise, if moduleScript's parse error is not null, then:
else if (!module_script->parse_error().is_null()) { else if (!module_script->parse_error().is_null()) {

View file

@ -41,7 +41,7 @@ JS::ThrowCompletionOr<bool> PlatformObject::is_named_property_exposed_on_object(
// 1. If P is not a supported property name of O, then return false. // 1. If P is not a supported property name of O, then return false.
// NOTE: This is in it's own variable to enforce the type. // NOTE: This is in it's own variable to enforce the type.
if (!is_supported_property_name(property_key.to_string().to_utf8_but_should_be_ported_to_utf16())) if (!is_supported_property_name(property_key.to_utf16_string().to_utf8_but_should_be_ported_to_utf16()))
return false; return false;
// 2. If O has an own property named P, then return false. // 2. If O has an own property named P, then return false.
@ -118,7 +118,7 @@ JS::ThrowCompletionOr<Optional<JS::PropertyDescriptor>> PlatformObject::legacy_p
// 1. If the result of running the named property visibility algorithm with property name P and object O is true, then: // 1. If the result of running the named property visibility algorithm with property name P and object O is true, then:
if (TRY(is_named_property_exposed_on_object(property_name))) { if (TRY(is_named_property_exposed_on_object(property_name))) {
// FIXME: It's unfortunate that this is done twice, once in is_named_property_exposed_on_object and here. // FIXME: It's unfortunate that this is done twice, once in is_named_property_exposed_on_object and here.
auto property_name_string = property_name.to_string().to_utf8_but_should_be_ported_to_utf16(); auto property_name_string = property_name.to_utf16_string().to_utf8_but_should_be_ported_to_utf16();
// 1. Let operation be the operation used to declare the named property getter. // 1. Let operation be the operation used to declare the named property getter.
// 2. Let value be an uninitialized variable. // 2. Let value be an uninitialized variable.
@ -237,7 +237,7 @@ JS::ThrowCompletionOr<bool> PlatformObject::internal_set(JS::PropertyKey const&
// NB: A PropertyKey containing a number is a String (it can only be a String or a Symbol, the number representation is an optimization). // NB: A PropertyKey containing a number is a String (it can only be a String or a Symbol, the number representation is an optimization).
if (m_legacy_platform_object_flags->has_named_property_setter && (property_name.is_string() || property_name.is_number())) { if (m_legacy_platform_object_flags->has_named_property_setter && (property_name.is_string() || property_name.is_number())) {
// 1. Invoke the named property setter on O with P and V. // 1. Invoke the named property setter on O with P and V.
TRY(throw_dom_exception_if_needed(vm, [&] { return invoke_named_property_setter(property_name.to_string().to_utf8_but_should_be_ported_to_utf16(), value); })); TRY(throw_dom_exception_if_needed(vm, [&] { return invoke_named_property_setter(property_name.to_utf16_string().to_utf8_but_should_be_ported_to_utf16(), value); }));
// 2. Return true. // 2. Return true.
return true; return true;
@ -284,7 +284,7 @@ JS::ThrowCompletionOr<bool> PlatformObject::internal_define_own_property(JS::Pro
// NB: A PropertyKey containing a number is a String (it can only be a String or a Symbol, the number representation is an optimization). // NB: A PropertyKey containing a number is a String (it can only be a String or a Symbol, the number representation is an optimization).
// FIXME: Check if P is not an unforgeable property name of O // FIXME: Check if P is not an unforgeable property name of O
if (m_legacy_platform_object_flags->supports_named_properties && !m_legacy_platform_object_flags->has_global_interface_extended_attribute && (property_name.is_string() || property_name.is_number())) { if (m_legacy_platform_object_flags->supports_named_properties && !m_legacy_platform_object_flags->has_global_interface_extended_attribute && (property_name.is_string() || property_name.is_number())) {
auto const property_name_as_string = property_name.to_string().to_utf8_but_should_be_ported_to_utf16(); auto const property_name_as_string = property_name.to_utf16_string().to_utf8_but_should_be_ported_to_utf16();
// 1. Let creating be true if P is not a supported property name, and false otherwise. // 1. Let creating be true if P is not a supported property name, and false otherwise.
bool creating = !is_supported_property_name(property_name_as_string); bool creating = !is_supported_property_name(property_name_as_string);
@ -353,7 +353,7 @@ JS::ThrowCompletionOr<bool> PlatformObject::internal_delete(JS::PropertyKey cons
return false; return false;
// FIXME: It's unfortunate that this is done twice, once in is_named_property_exposed_on_object and here. // FIXME: It's unfortunate that this is done twice, once in is_named_property_exposed_on_object and here.
auto property_name_string = property_name.to_string().to_utf8_but_should_be_ported_to_utf16(); auto property_name_string = property_name.to_utf16_string().to_utf8_but_should_be_ported_to_utf16();
// 2. Let operation be the operation used to declare the named property deleter. // 2. Let operation be the operation used to declare the named property deleter.
// 3. If operation was defined without an identifier, then: // 3. If operation was defined without an identifier, then:

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