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:
parent
b6bef6b688
commit
b81269e78b
157 changed files with 1165 additions and 1041 deletions
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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 };
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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) \
|
||||||
{ \
|
{ \
|
||||||
|
|
|
||||||
|
|
@ -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; \
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
|
|
|
||||||
|
|
@ -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());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
|
|
|
||||||
|
|
@ -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).
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
|
|
|
||||||
|
|
@ -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());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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; \
|
||||||
}); \
|
}); \
|
||||||
|
|
|
||||||
|
|
@ -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());
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 {};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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) };
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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}")
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)),
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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> {};
|
||||||
|
|
|
||||||
|
|
@ -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; }
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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 {};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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());
|
||||||
|
|
|
||||||
|
|
@ -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&);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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([&]() {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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())
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()) {
|
||||||
|
|
|
||||||
|
|
@ -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
Loading…
Reference in a new issue