LibJS+LibWeb: Replace JS::Utf16String with AK::Utf16String

This commit is contained in:
Timothy Flynn 2025-07-09 14:13:22 -04:00 committed by Tim Flynn
parent d40e3af697
commit a43cb15e81
Notes: github-actions[bot] 2025-07-18 16:46:59 +00:00
24 changed files with 172 additions and 526 deletions

View file

@ -245,7 +245,6 @@ set(SOURCES
Runtime/TypedArrayConstructor.cpp Runtime/TypedArrayConstructor.cpp
Runtime/TypedArrayPrototype.cpp Runtime/TypedArrayPrototype.cpp
Runtime/Uint8Array.cpp Runtime/Uint8Array.cpp
Runtime/Utf16String.cpp
Runtime/Value.cpp Runtime/Value.cpp
Runtime/VM.cpp Runtime/VM.cpp
Runtime/WeakMap.cpp Runtime/WeakMap.cpp

View file

@ -219,7 +219,6 @@ struct SourceRange;
class SourceTextModule; class SourceTextModule;
class Symbol; class Symbol;
class Token; class Token;
class Utf16String;
class VM; class VM;
class PrototypeChainValidity; class PrototypeChainValidity;
class Value; class Value;

View file

@ -774,11 +774,9 @@ ErrorOr<void> print_intl_segmenter(JS::PrintContext& print_context, JS::Intl::Se
ErrorOr<void> print_intl_segments(JS::PrintContext& print_context, JS::Intl::Segments const& segments, HashTable<JS::Object*>& seen_objects) ErrorOr<void> print_intl_segments(JS::PrintContext& print_context, JS::Intl::Segments const& segments, HashTable<JS::Object*>& seen_objects)
{ {
auto segments_string = JS::Utf16String::create(segments.segments_string());
TRY(print_type(print_context, "Segments"sv)); TRY(print_type(print_context, "Segments"sv));
TRY(js_out(print_context, "\n string: ")); TRY(js_out(print_context, "\n string: "));
TRY(print_value(print_context, JS::PrimitiveString::create(segments.vm(), move(segments_string)), seen_objects)); TRY(print_value(print_context, JS::PrimitiveString::create(segments.vm(), segments.segments_string()), seen_objects));
return {}; return {};
} }

View file

@ -1284,7 +1284,7 @@ ThrowCompletionOr<String> get_substitution(VM& vm, Utf16View const& matched, Utf
// 4. Let templateRemainder be replacementTemplate. // 4. Let templateRemainder be replacementTemplate.
auto replace_template_string = TRY(replacement_template.to_utf16_string(vm)); auto replace_template_string = TRY(replacement_template.to_utf16_string(vm));
auto template_remainder = replace_template_string.view(); 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()) {
@ -1387,7 +1387,7 @@ ThrowCompletionOr<String> get_substitution(VM& vm, Utf16View const& matched, Utf
else { else {
// a. Let refReplacement be capture. // a. Let refReplacement be capture.
capture_string = TRY(capture.to_utf16_string(vm)); capture_string = TRY(capture.to_utf16_string(vm));
ref_replacement = capture_string->view(); ref_replacement = *capture_string;
} }
} }
// ix. Else, // ix. Else,
@ -1434,7 +1434,7 @@ ThrowCompletionOr<String> get_substitution(VM& vm, Utf16View const& matched, Utf
else { else {
// a. Let refReplacement be ? ToString(capture). // a. Let refReplacement be ? ToString(capture).
capture_string = TRY(capture.to_utf16_string(vm)); capture_string = TRY(capture.to_utf16_string(vm));
ref_replacement = capture_string->view(); ref_replacement = *capture_string;
} }
} }
} }

View file

@ -387,7 +387,7 @@ JS_DEFINE_NATIVE_FUNCTION(GlobalObject::parse_int)
// 19.2.6.5 Encode ( string, extraUnescaped ), https://tc39.es/ecma262/#sec-encode // 19.2.6.5 Encode ( string, extraUnescaped ), https://tc39.es/ecma262/#sec-encode
static ThrowCompletionOr<ByteString> encode(VM& vm, ByteString const& string, StringView unescaped_set) static ThrowCompletionOr<ByteString> encode(VM& vm, ByteString const& string, StringView unescaped_set)
{ {
auto utf16_string = Utf16String::create(string); auto utf16_string = Utf16String::from_utf8(string);
// 1. Let strLen be the length of string. // 1. Let strLen be the length of string.
auto string_length = utf16_string.length_in_code_units(); auto string_length = utf16_string.length_in_code_units();
@ -421,7 +421,7 @@ static ThrowCompletionOr<ByteString> encode(VM& vm, ByteString const& string, St
// d. Else, // d. Else,
else { else {
// i. Let cp be CodePointAt(string, k). // i. Let cp be CodePointAt(string, k).
auto code_point = code_point_at(utf16_string.view(), k); auto code_point = code_point_at(utf16_string, k);
// ii. If cp.[[IsUnpairedSurrogate]] is true, throw a URIError exception. // ii. If cp.[[IsUnpairedSurrogate]] is true, throw a URIError exception.
if (code_point.is_unpaired_surrogate) if (code_point.is_unpaired_surrogate)
return vm.throw_completion<URIError>(ErrorType::URIMalformed); return vm.throw_completion<URIError>(ErrorType::URIMalformed);

View file

@ -57,13 +57,13 @@ ThrowCompletionOr<GC::Ref<Object>> create_segment_data_object(VM& vm, Unicode::S
auto segment = string.substring_view(start_index, end_index - start_index); auto segment = string.substring_view(start_index, end_index - start_index);
// 7. Perform ! CreateDataPropertyOrThrow(result, "segment", segment). // 7. Perform ! CreateDataPropertyOrThrow(result, "segment", segment).
MUST(result->create_data_property_or_throw(vm.names.segment, PrimitiveString::create(vm, Utf16String::create(segment)))); MUST(result->create_data_property_or_throw(vm.names.segment, PrimitiveString::create(vm, segment)));
// 8. Perform ! CreateDataPropertyOrThrow(result, "index", 𝔽(startIndex)). // 8. Perform ! CreateDataPropertyOrThrow(result, "index", 𝔽(startIndex)).
MUST(result->create_data_property_or_throw(vm.names.index, Value(start_index))); MUST(result->create_data_property_or_throw(vm.names.index, Value(start_index)));
// 9. Perform ! CreateDataPropertyOrThrow(result, "input", string). // 9. Perform ! CreateDataPropertyOrThrow(result, "input", string).
MUST(result->create_data_property_or_throw(vm.names.input, PrimitiveString::create(vm, Utf16String::create(string)))); MUST(result->create_data_property_or_throw(vm.names.input, PrimitiveString::create(vm, string)));
// 10. Let granularity be segmenter.[[SegmenterGranularity]]. // 10. Let granularity be segmenter.[[SegmenterGranularity]].
auto granularity = segmenter.segmenter_granularity(); auto granularity = segmenter.segmenter_granularity();

View file

@ -29,7 +29,7 @@ Segments::Segments(Realm& realm, Unicode::Segmenter const& segmenter, Utf16Strin
, m_segments_segmenter(segmenter.clone()) , m_segments_segmenter(segmenter.clone())
, m_segments_string(move(string)) , m_segments_string(move(string))
{ {
m_segments_segmenter->set_segmented_text(m_segments_string.view()); m_segments_segmenter->set_segmented_text(m_segments_string);
} }
} }

View file

@ -24,7 +24,7 @@ public:
Unicode::Segmenter& segments_segmenter() const { return *m_segments_segmenter; } Unicode::Segmenter& segments_segmenter() const { return *m_segments_segmenter; }
Utf16View segments_string() const { return m_segments_string.view(); } Utf16String const& segments_string() const { return m_segments_string; }
private: private:
Segments(Realm&, Unicode::Segmenter const&, Utf16String); Segments(Realm&, Unicode::Segmenter const&, Utf16String);

View file

@ -76,7 +76,7 @@ JS_DEFINE_NATIVE_FUNCTION(SegmentsPrototype::symbol_iterator)
auto& segmenter = segments->segments_segmenter(); auto& segmenter = segments->segments_segmenter();
// 4. Let string be segments.[[SegmentsString]]. // 4. Let string be segments.[[SegmentsString]].
auto string = segments->segments_string(); auto const& string = segments->segments_string();
// 5. Return ! CreateSegmentIterator(segmenter, string). // 5. Return ! CreateSegmentIterator(segmenter, string).
return SegmentIterator::create(realm, segmenter, string, segments); return SegmentIterator::create(realm, segmenter, string, segments);

View file

@ -9,6 +9,7 @@
#include <AK/FlyString.h> #include <AK/FlyString.h>
#include <AK/StringBuilder.h> #include <AK/StringBuilder.h>
#include <AK/UnicodeUtils.h> #include <AK/UnicodeUtils.h>
#include <AK/Utf16FlyString.h>
#include <AK/Utf16View.h> #include <AK/Utf16View.h>
#include <AK/Utf8View.h> #include <AK/Utf8View.h>
#include <LibJS/Runtime/AbstractOperations.h> #include <LibJS/Runtime/AbstractOperations.h>
@ -23,18 +24,80 @@ namespace JS {
GC_DEFINE_ALLOCATOR(PrimitiveString); GC_DEFINE_ALLOCATOR(PrimitiveString);
GC_DEFINE_ALLOCATOR(RopeString); GC_DEFINE_ALLOCATOR(RopeString);
RopeString::RopeString(GC::Ref<PrimitiveString> lhs, GC::Ref<PrimitiveString> rhs) GC::Ref<PrimitiveString> PrimitiveString::create(VM& vm, Utf16String string)
: PrimitiveString(RopeTag::Rope)
, m_lhs(lhs)
, m_rhs(rhs)
{ {
if (string.is_empty())
return vm.empty_string();
if (string.length_in_code_units() == 1) {
if (auto code_unit = string.code_unit_at(0); is_ascii(code_unit))
return vm.single_ascii_character_string(static_cast<u8>(code_unit));
}
auto& string_cache = vm.utf16_string_cache();
if (auto it = string_cache.find(string); it != string_cache.end())
return *it->value;
auto new_string = vm.heap().allocate<PrimitiveString>(string);
string_cache.set(move(string), new_string);
return *new_string;
} }
RopeString::~RopeString() = default; GC::Ref<PrimitiveString> PrimitiveString::create(VM& vm, Utf16View const& string)
PrimitiveString::PrimitiveString(String string)
: m_utf8_string(move(string))
{ {
return create(vm, Utf16String::from_utf16(string));
}
GC::Ref<PrimitiveString> PrimitiveString::create(VM& vm, Utf16FlyString const& string)
{
return create(vm, string.to_utf16_string());
}
GC::Ref<PrimitiveString> PrimitiveString::create(VM& vm, String string)
{
if (string.is_empty())
return vm.empty_string();
if (auto bytes = string.bytes_as_string_view(); bytes.length() == 1) {
if (auto ch = static_cast<u8>(bytes[0]); is_ascii(ch))
return vm.single_ascii_character_string(ch);
}
auto& string_cache = vm.string_cache();
if (auto it = string_cache.find(string); it != string_cache.end())
return *it->value;
auto new_string = vm.heap().allocate<PrimitiveString>(string);
string_cache.set(move(string), new_string);
return *new_string;
}
GC::Ref<PrimitiveString> PrimitiveString::create(VM& vm, StringView string)
{
return create(vm, String::from_utf8(string).release_value());
}
GC::Ref<PrimitiveString> PrimitiveString::create(VM& vm, FlyString const& string)
{
return create(vm, string.to_string());
}
GC::Ref<PrimitiveString> PrimitiveString::create(VM& vm, PrimitiveString& lhs, PrimitiveString& rhs)
{
// We're here to concatenate two strings into a new rope string. However, if any of them are empty, no rope is required.
bool lhs_empty = lhs.is_empty();
bool rhs_empty = rhs.is_empty();
if (lhs_empty && rhs_empty)
return vm.empty_string();
if (lhs_empty)
return rhs;
if (rhs_empty)
return lhs;
return vm.heap().allocate<RopeString>(lhs, rhs);
} }
PrimitiveString::PrimitiveString(Utf16String string) PrimitiveString::PrimitiveString(Utf16String string)
@ -42,19 +105,17 @@ PrimitiveString::PrimitiveString(Utf16String string)
{ {
} }
PrimitiveString::~PrimitiveString() PrimitiveString::PrimitiveString(String string)
: m_utf8_string(move(string))
{ {
if (has_utf8_string())
vm().string_cache().remove(*m_utf8_string);
if (has_utf16_string())
vm().utf16_string_cache().remove(*m_utf16_string);
} }
void RopeString::visit_edges(Cell::Visitor& visitor) PrimitiveString::~PrimitiveString()
{ {
Base::visit_edges(visitor); if (has_utf16_string())
visitor.visit(m_lhs); vm().utf16_string_cache().remove(*m_utf16_string);
visitor.visit(m_rhs); if (has_utf8_string())
vm().string_cache().remove(*m_utf8_string);
} }
bool PrimitiveString::is_empty() const bool PrimitiveString::is_empty() const
@ -95,7 +156,7 @@ Utf16String PrimitiveString::utf16_string() const
if (!has_utf16_string()) { if (!has_utf16_string()) {
VERIFY(has_utf8_string()); VERIFY(has_utf8_string());
m_utf16_string = Utf16String::create(m_utf8_string->bytes_as_string_view()); m_utf16_string = Utf16String::from_utf8(*m_utf8_string);
} }
return *m_utf16_string; return *m_utf16_string;
@ -104,7 +165,7 @@ Utf16String PrimitiveString::utf16_string() const
Utf16View PrimitiveString::utf16_string_view() const Utf16View PrimitiveString::utf16_string_view() const
{ {
(void)utf16_string(); (void)utf16_string();
return m_utf16_string->view(); return *m_utf16_string;
} }
size_t PrimitiveString::length_in_utf16_code_units() const size_t PrimitiveString::length_in_utf16_code_units() const
@ -119,7 +180,7 @@ bool PrimitiveString::operator==(PrimitiveString const& other) const
if (m_utf8_string.has_value() && other.m_utf8_string.has_value()) if (m_utf8_string.has_value() && other.m_utf8_string.has_value())
return m_utf8_string->bytes_as_string_view() == other.m_utf8_string->bytes_as_string_view(); return m_utf8_string->bytes_as_string_view() == other.m_utf8_string->bytes_as_string_view();
if (m_utf16_string.has_value() && other.m_utf16_string.has_value()) if (m_utf16_string.has_value() && other.m_utf16_string.has_value())
return m_utf16_string->string() == other.m_utf16_string->string(); return *m_utf16_string == *other.m_utf16_string;
return utf8_string_view() == other.utf8_string_view(); return utf8_string_view() == other.utf8_string_view();
} }
@ -127,89 +188,22 @@ ThrowCompletionOr<Optional<Value>> PrimitiveString::get(VM& vm, PropertyKey cons
{ {
if (property_key.is_symbol()) if (property_key.is_symbol())
return Optional<Value> {}; return Optional<Value> {};
if (property_key.is_string()) { if (property_key.is_string()) {
if (property_key.as_string() == vm.names.length.as_string()) { if (property_key.as_string() == vm.names.length.as_string()) {
return Value(static_cast<double>(length_in_utf16_code_units())); return Value(static_cast<double>(length_in_utf16_code_units()));
} }
} }
auto index = canonical_numeric_index_string(property_key, CanonicalIndexMode::IgnoreNumericRoundtrip); auto index = canonical_numeric_index_string(property_key, CanonicalIndexMode::IgnoreNumericRoundtrip);
if (!index.is_index()) if (!index.is_index())
return Optional<Value> {}; return Optional<Value> {};
auto str = utf16_string_view();
auto length = str.length_in_code_units(); auto string = utf16_string_view();
if (length <= index.as_index()) if (string.length_in_code_units() <= index.as_index())
return Optional<Value> {}; return Optional<Value> {};
return create(vm, Utf16String::create(str.substring_view(index.as_index(), 1)));
}
GC::Ref<PrimitiveString> PrimitiveString::create(VM& vm, Utf16String string) return create(vm, string.substring_view(index.as_index(), 1));
{
if (string.is_empty())
return vm.empty_string();
if (string.length_in_code_units() == 1) {
u16 code_unit = string.code_unit_at(0);
if (is_ascii(code_unit))
return vm.single_ascii_character_string(static_cast<u8>(code_unit));
}
auto& string_cache = vm.utf16_string_cache();
if (auto it = string_cache.find(string); it != string_cache.end())
return *it->value;
auto new_string = vm.heap().allocate<PrimitiveString>(string);
string_cache.set(move(string), new_string);
return *new_string;
}
GC::Ref<PrimitiveString> PrimitiveString::create(VM& vm, String string)
{
if (string.is_empty())
return vm.empty_string();
if (auto bytes = string.bytes_as_string_view(); bytes.length() == 1) {
auto ch = static_cast<u8>(bytes[0]);
if (is_ascii(ch))
return vm.single_ascii_character_string(ch);
}
auto& string_cache = vm.string_cache();
if (auto it = string_cache.find(string); it != string_cache.end())
return *it->value;
auto new_string = vm.heap().allocate<PrimitiveString>(string);
string_cache.set(move(string), new_string);
return *new_string;
}
GC::Ref<PrimitiveString> PrimitiveString::create(VM& vm, FlyString const& string)
{
return create(vm, string.to_string());
}
GC::Ref<PrimitiveString> PrimitiveString::create(VM& vm, StringView string)
{
return create(vm, String::from_utf8(string).release_value());
}
GC::Ref<PrimitiveString> PrimitiveString::create(VM& vm, PrimitiveString& lhs, PrimitiveString& rhs)
{
// We're here to concatenate two strings into a new rope string.
// However, if any of them are empty, no rope is required.
bool lhs_empty = lhs.is_empty();
bool rhs_empty = rhs.is_empty();
if (lhs_empty && rhs_empty)
return vm.empty_string();
if (lhs_empty)
return rhs;
if (rhs_empty)
return lhs;
return vm.heap().allocate<RopeString>(lhs, rhs);
} }
void PrimitiveString::resolve_rope_if_needed(EncodingPreference preference) const void PrimitiveString::resolve_rope_if_needed(EncodingPreference preference) const
@ -218,7 +212,7 @@ void PrimitiveString::resolve_rope_if_needed(EncodingPreference preference) cons
return; return;
auto const& rope_string = static_cast<RopeString const&>(*this); auto const& rope_string = static_cast<RopeString const&>(*this);
return rope_string.resolve(preference); rope_string.resolve(preference);
} }
void RopeString::resolve(EncodingPreference preference) const void RopeString::resolve(EncodingPreference preference) const
@ -251,12 +245,16 @@ void RopeString::resolve(EncodingPreference preference) const
if (preference == EncodingPreference::UTF16) { if (preference == EncodingPreference::UTF16) {
// The caller wants a UTF-16 string, so we can simply concatenate all the pieces // The caller wants a UTF-16 string, so we can simply concatenate all the pieces
// into a UTF-16 code unit buffer and create a Utf16String from it. // into a UTF-16 code unit buffer and create a Utf16String from it.
StringBuilder builder(StringBuilder::Mode::UTF16);
Utf16Data code_units; for (auto const* current : pieces) {
for (auto const* current : pieces) if (current->has_utf8_string())
code_units.extend(current->utf16_string().string()); builder.append(current->utf8_string_view());
else
builder.append(current->utf16_string_view());
}
m_utf16_string = Utf16String::create(move(code_units)); m_utf16_string = builder.to_utf16_string_without_validation();
m_is_rope = false; m_is_rope = false;
m_lhs = nullptr; m_lhs = nullptr;
m_rhs = nullptr; m_rhs = nullptr;
@ -331,4 +329,20 @@ void RopeString::resolve(EncodingPreference preference) const
m_rhs = nullptr; m_rhs = nullptr;
} }
RopeString::RopeString(GC::Ref<PrimitiveString> lhs, GC::Ref<PrimitiveString> rhs)
: PrimitiveString(RopeTag::Rope)
, m_lhs(lhs)
, m_rhs(rhs)
{
}
RopeString::~RopeString() = default;
void RopeString::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_lhs);
visitor.visit(m_rhs);
}
} }

View file

@ -10,11 +10,11 @@
#include <AK/Optional.h> #include <AK/Optional.h>
#include <AK/String.h> #include <AK/String.h>
#include <AK/StringView.h> #include <AK/StringView.h>
#include <AK/Utf16String.h>
#include <LibGC/CellAllocator.h> #include <LibGC/CellAllocator.h>
#include <LibJS/Forward.h> #include <LibJS/Forward.h>
#include <LibJS/Heap/Cell.h> #include <LibJS/Heap/Cell.h>
#include <LibJS/Runtime/Completion.h> #include <LibJS/Runtime/Completion.h>
#include <LibJS/Runtime/Utf16String.h>
#include <LibJS/Runtime/Value.h> #include <LibJS/Runtime/Value.h>
namespace JS { namespace JS {
@ -25,10 +25,14 @@ class JS_API PrimitiveString : public Cell {
public: public:
[[nodiscard]] static GC::Ref<PrimitiveString> create(VM&, Utf16String); [[nodiscard]] static GC::Ref<PrimitiveString> create(VM&, Utf16String);
[[nodiscard]] static GC::Ref<PrimitiveString> create(VM&, Utf16View const&);
[[nodiscard]] static GC::Ref<PrimitiveString> create(VM&, Utf16FlyString const&);
[[nodiscard]] static GC::Ref<PrimitiveString> create(VM&, String); [[nodiscard]] static GC::Ref<PrimitiveString> create(VM&, String);
[[nodiscard]] static GC::Ref<PrimitiveString> create(VM&, FlyString const&);
[[nodiscard]] static GC::Ref<PrimitiveString> create(VM&, PrimitiveString&, PrimitiveString&);
[[nodiscard]] static GC::Ref<PrimitiveString> create(VM&, StringView); [[nodiscard]] static GC::Ref<PrimitiveString> create(VM&, StringView);
[[nodiscard]] static GC::Ref<PrimitiveString> create(VM&, FlyString const&);
[[nodiscard]] static GC::Ref<PrimitiveString> create(VM&, PrimitiveString&, PrimitiveString&);
virtual ~PrimitiveString(); virtual ~PrimitiveString();
@ -71,8 +75,8 @@ protected:
private: private:
friend class RopeString; friend class RopeString;
explicit PrimitiveString(String);
explicit PrimitiveString(Utf16String); explicit PrimitiveString(Utf16String);
explicit PrimitiveString(String);
void resolve_rope_if_needed(EncodingPreference) const; void resolve_rope_if_needed(EncodingPreference) const;
}; };

View file

@ -94,7 +94,7 @@ void update_legacy_regexp_static_properties(RegExpConstructor& constructor, Utf1
legacy_static_properties.set_input(string); legacy_static_properties.set_input(string);
// 8. Set the value of Cs [[RegExpLastMatch]] internal slot to a String whose length is endIndex - startIndex and containing the code units from S with indices startIndex through endIndex - 1, in ascending order. // 8. Set the value of Cs [[RegExpLastMatch]] internal slot to a String whose length is endIndex - startIndex and containing the code units from S with indices startIndex through endIndex - 1, in ascending order.
auto last_match = string.view().substring_view(start_index, end_index - start_index); auto last_match = string.substring_view(start_index, end_index - start_index);
legacy_static_properties.set_last_match(last_match); legacy_static_properties.set_last_match(last_match);
// 9. If n > 0, set the value of Cs [[RegExpLastParen]] internal slot to the last element of capturedValues. // 9. If n > 0, set the value of Cs [[RegExpLastParen]] internal slot to the last element of capturedValues.
@ -104,22 +104,22 @@ void update_legacy_regexp_static_properties(RegExpConstructor& constructor, Utf1
} }
// 10. Else, set the value of Cs [[RegExpLastParen]] internal slot to the empty String. // 10. Else, set the value of Cs [[RegExpLastParen]] internal slot to the empty String.
else { else {
legacy_static_properties.set_last_paren(Utf16String::create()); legacy_static_properties.set_last_paren({});
} }
// 11. Set the value of Cs [[RegExpLeftContext]] internal slot to a String whose length is startIndex and containing the code units from S with indices 0 through startIndex - 1, in ascending order. // 11. Set the value of Cs [[RegExpLeftContext]] internal slot to a String whose length is startIndex and containing the code units from S with indices 0 through startIndex - 1, in ascending order.
auto left_context = string.view().substring_view(0, start_index); auto left_context = string.substring_view(0, start_index);
legacy_static_properties.set_left_context(left_context); legacy_static_properties.set_left_context(left_context);
// 12. Set the value of Cs [[RegExpRightContext]] internal slot to a String whose length is len - endIndex and containing the code units from S with indices endIndex through len - 1, in ascending order. // 12. Set the value of Cs [[RegExpRightContext]] internal slot to a String whose length is len - endIndex and containing the code units from S with indices endIndex through len - 1, in ascending order.
auto right_context = string.view().substring_view(end_index, len - end_index); auto right_context = string.substring_view(end_index, len - end_index);
legacy_static_properties.set_right_context(right_context); legacy_static_properties.set_right_context(right_context);
// 13. For each integer i such that 1 ≤ i ≤ 9 // 13. For each integer i such that 1 ≤ i ≤ 9
for (size_t i = 1; i <= 9; i++) { for (size_t i = 1; i <= 9; i++) {
// i. If i ≤ n, set the value of Cs [[RegExpPareni]] internal slot to the ith element of capturedValues. // i. If i ≤ n, set the value of Cs [[RegExpPareni]] internal slot to the ith element of capturedValues.
// ii. Else, set the value of Cs [[RegExpPareni]] internal slot to the empty String. // ii. Else, set the value of Cs [[RegExpPareni]] internal slot to the empty String.
auto value = (i <= group_count) ? captured_values[i - 1] : Utf16String::create(); auto value = (i <= group_count) ? captured_values[i - 1] : Utf16String {};
if (i == 1) { if (i == 1) {
legacy_static_properties.set_$1(move(value)); legacy_static_properties.set_$1(move(value));

View file

@ -7,8 +7,8 @@
#pragma once #pragma once
#include <AK/Optional.h> #include <AK/Optional.h>
#include <AK/Utf16String.h>
#include <LibJS/Forward.h> #include <LibJS/Forward.h>
#include <LibJS/Runtime/Utf16String.h>
namespace JS { namespace JS {
@ -26,20 +26,20 @@ public:
Optional<Utf16String> const& last_match() const Optional<Utf16String> const& last_match() const
{ {
if (!m_last_match_string.has_value()) if (!m_last_match_string.has_value())
m_last_match_string = Utf16String::create(m_last_match); m_last_match_string = Utf16String::from_utf16_without_validation(m_last_match);
return m_last_match_string; return m_last_match_string;
} }
Optional<Utf16String> const& last_paren() const { return m_last_paren; } Optional<Utf16String> const& last_paren() const { return m_last_paren; }
Optional<Utf16String> const& left_context() const Optional<Utf16String> const& left_context() const
{ {
if (!m_left_context_string.has_value()) if (!m_left_context_string.has_value())
m_left_context_string = Utf16String::create(m_left_context); m_left_context_string = Utf16String::from_utf16_without_validation(m_left_context);
return m_left_context_string; return m_left_context_string;
} }
Optional<Utf16String> const& right_context() const Optional<Utf16String> const& right_context() const
{ {
if (!m_right_context_string.has_value()) if (!m_right_context_string.has_value())
m_right_context_string = Utf16String::create(m_right_context); m_right_context_string = Utf16String::from_utf16_without_validation(m_right_context);
return m_right_context_string; return m_right_context_string;
} }
Optional<Utf16String> const& $1() const { return m_$1; } Optional<Utf16String> const& $1() const { return m_$1; }

View file

@ -8,6 +8,7 @@
#include <AK/CharacterTypes.h> #include <AK/CharacterTypes.h>
#include <AK/Function.h> #include <AK/Function.h>
#include <AK/Utf16String.h>
#include <AK/Utf16View.h> #include <AK/Utf16View.h>
#include <LibJS/Runtime/AbstractOperations.h> #include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Array.h> #include <LibJS/Runtime/Array.h>
@ -280,7 +281,7 @@ static ThrowCompletionOr<Value> regexp_builtin_exec(VM& vm, RegExpObject& regexp
// 28. Let matchedValue be ! GetMatchString(S, match). // 28. Let matchedValue be ! GetMatchString(S, match).
// 29. Perform ! CreateDataPropertyOrThrow(A, "0", matchedValue). // 29. Perform ! CreateDataPropertyOrThrow(A, "0", matchedValue).
MUST(array->create_data_property_or_throw(0, PrimitiveString::create(vm, Utf16String::create(match.view.u16_view())))); MUST(array->create_data_property_or_throw(0, PrimitiveString::create(vm, match.view.u16_view())));
// 30. If R contains any GroupName, then // 30. If R contains any GroupName, then
// a. Let groups be OrdinaryObjectCreate(null). // a. Let groups be OrdinaryObjectCreate(null).
@ -305,7 +306,7 @@ static ThrowCompletionOr<Value> regexp_builtin_exec(VM& vm, RegExpObject& regexp
// ii. Append undefined to indices. // ii. Append undefined to indices.
indices.append({}); indices.append({});
// iii. Append capture to indices. // iii. Append capture to indices.
captured_values.append(Utf16String::create()); captured_values.append({});
} }
// c. Else, // c. Else,
else { else {
@ -316,7 +317,7 @@ static ThrowCompletionOr<Value> regexp_builtin_exec(VM& vm, RegExpObject& regexp
// 2. Set captureEnd to ! GetStringIndex(S, Input, captureEnd). // 2. Set captureEnd to ! GetStringIndex(S, Input, captureEnd).
// iv. Let capture be the Match { [[StartIndex]]: captureStart, [[EndIndex]: captureEnd }. // iv. Let capture be the Match { [[StartIndex]]: captureStart, [[EndIndex]: captureEnd }.
// v. Let capturedValue be ! GetMatchString(S, capture). // v. Let capturedValue be ! GetMatchString(S, capture).
auto capture_as_utf16_string = Utf16String::create(capture.view.u16_view()); auto capture_as_utf16_string = Utf16String::from_utf16_without_validation(capture.view.u16_view());
captured_value = PrimitiveString::create(vm, capture_as_utf16_string); captured_value = PrimitiveString::create(vm, capture_as_utf16_string);
// vi. Append capture to indices. // vi. Append capture to indices.
indices.append(Match::create(capture)); indices.append(Match::create(capture));
@ -987,7 +988,7 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_split)
auto substring = string->utf16_string_view().substring_view(last_match_end, next_search_from - last_match_end); auto substring = string->utf16_string_view().substring_view(last_match_end, next_search_from - last_match_end);
// 2. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(lengthA)), T). // 2. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(lengthA)), T).
MUST(array->create_data_property_or_throw(array_length, PrimitiveString::create(vm, Utf16String::create(substring)))); MUST(array->create_data_property_or_throw(array_length, PrimitiveString::create(vm, substring)));
// 3. Set lengthA to lengthA + 1. // 3. Set lengthA to lengthA + 1.
++array_length; ++array_length;
@ -1033,7 +1034,7 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_split)
auto substring = string->utf16_string_view().substring_view(last_match_end); auto substring = string->utf16_string_view().substring_view(last_match_end);
// 21. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(lengthA)), T). // 21. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(lengthA)), T).
MUST(array->create_data_property_or_throw(array_length, PrimitiveString::create(vm, Utf16String::create(substring)))); MUST(array->create_data_property_or_throw(array_length, PrimitiveString::create(vm, substring)));
// 22. Return A. // 22. Return A.
return array; return array;

View file

@ -8,7 +8,6 @@
#include <LibJS/Runtime/PrototypeObject.h> #include <LibJS/Runtime/PrototypeObject.h>
#include <LibJS/Runtime/RegExpObject.h> #include <LibJS/Runtime/RegExpObject.h>
#include <LibJS/Runtime/Utf16String.h>
namespace JS { namespace JS {

View file

@ -4,11 +4,11 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/Utf16View.h>
#include <LibJS/Runtime/GlobalObject.h> #include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Iterator.h> #include <LibJS/Runtime/Iterator.h>
#include <LibJS/Runtime/RegExpPrototype.h> #include <LibJS/Runtime/RegExpPrototype.h>
#include <LibJS/Runtime/RegExpStringIteratorPrototype.h> #include <LibJS/Runtime/RegExpStringIteratorPrototype.h>
#include <LibJS/Runtime/Utf16String.h>
namespace JS { namespace JS {

View file

@ -6,6 +6,7 @@
#include <AK/StringBuilder.h> #include <AK/StringBuilder.h>
#include <AK/UnicodeUtils.h> #include <AK/UnicodeUtils.h>
#include <AK/Utf16String.h>
#include <AK/Utf16View.h> #include <AK/Utf16View.h>
#include <LibJS/Runtime/AbstractOperations.h> #include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Array.h> #include <LibJS/Runtime/Array.h>
@ -13,7 +14,6 @@
#include <LibJS/Runtime/GlobalObject.h> #include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/StringConstructor.h> #include <LibJS/Runtime/StringConstructor.h>
#include <LibJS/Runtime/StringObject.h> #include <LibJS/Runtime/StringObject.h>
#include <LibJS/Runtime/Utf16String.h>
#include <LibJS/Runtime/ValueInlines.h> #include <LibJS/Runtime/ValueInlines.h>
namespace JS { namespace JS {
@ -89,8 +89,7 @@ ThrowCompletionOr<GC::Ref<Object>> StringConstructor::construct(FunctionObject&
JS_DEFINE_NATIVE_FUNCTION(StringConstructor::from_char_code) JS_DEFINE_NATIVE_FUNCTION(StringConstructor::from_char_code)
{ {
// 1. Let result be the empty String. // 1. Let result be the empty String.
Utf16Data string; StringBuilder builder(StringBuilder::Mode::UTF16, vm.argument_count());
string.ensure_capacity(vm.argument_count());
// 2. For each element next of codeUnits, do // 2. For each element next of codeUnits, do
for (size_t i = 0; i < vm.argument_count(); ++i) { for (size_t i = 0; i < vm.argument_count(); ++i) {
@ -98,20 +97,19 @@ JS_DEFINE_NATIVE_FUNCTION(StringConstructor::from_char_code)
auto next_code_unit = TRY(vm.argument(i).to_u16(vm)); auto next_code_unit = TRY(vm.argument(i).to_u16(vm));
// b. Set result to the string-concatenation of result and nextCU. // b. Set result to the string-concatenation of result and nextCU.
string.append(next_code_unit); builder.append_code_unit(next_code_unit);
} }
// 3. Return result. // 3. Return result.
return PrimitiveString::create(vm, Utf16String::create(move(string))); return PrimitiveString::create(vm, builder.to_utf16_string());
} }
// 22.1.2.2 String.fromCodePoint ( ...codePoints ), https://tc39.es/ecma262/#sec-string.fromcodepoint // 22.1.2.2 String.fromCodePoint ( ...codePoints ), https://tc39.es/ecma262/#sec-string.fromcodepoint
JS_DEFINE_NATIVE_FUNCTION(StringConstructor::from_code_point) JS_DEFINE_NATIVE_FUNCTION(StringConstructor::from_code_point)
{ {
// 1. Let result be the empty String. // 1. Let result be the empty String.
Utf16Data string; // NOTE: This will be an under-estimate if any code point is > 0xffff.
// This will be an under-estimate if any code point is > 0xffff. StringBuilder builder(StringBuilder::Mode::UTF16, vm.argument_count());
string.ensure_capacity(vm.argument_count());
// 2. For each element next of codePoints, do // 2. For each element next of codePoints, do
for (size_t i = 0; i < vm.argument_count(); ++i) { for (size_t i = 0; i < vm.argument_count(); ++i) {
@ -130,16 +128,16 @@ JS_DEFINE_NATIVE_FUNCTION(StringConstructor::from_code_point)
// d. Set result to the string-concatenation of result and UTF16EncodeCodePoint((nextCP)). // d. Set result to the string-concatenation of result and UTF16EncodeCodePoint((nextCP)).
(void)AK::UnicodeUtils::code_point_to_utf16(static_cast<u32>(code_point), [&](auto code_unit) { (void)AK::UnicodeUtils::code_point_to_utf16(static_cast<u32>(code_point), [&](auto code_unit) {
string.append(code_unit); builder.append_code_unit(code_unit);
}); });
} }
// 3. Assert: If codePoints is empty, then result is the empty String. // 3. Assert: If codePoints is empty, then result is the empty String.
if (!vm.argument_count()) if (!vm.argument_count())
VERIFY(string.is_empty()); VERIFY(builder.is_empty());
// 4. Return result. // 4. Return result.
return PrimitiveString::create(vm, Utf16String::create(move(string))); return PrimitiveString::create(vm, builder.to_utf16_string());
} }
// 22.1.2.4 String.raw ( template, ...substitutions ), https://tc39.es/ecma262/#sec-string.raw // 22.1.2.4 String.raw ( template, ...substitutions ), https://tc39.es/ecma262/#sec-string.raw

View file

@ -83,7 +83,7 @@ static ThrowCompletionOr<Optional<PropertyDescriptor>> string_get_own_property(S
return Optional<PropertyDescriptor> {}; return Optional<PropertyDescriptor> {};
// 10. Let resultStr be the substring of str from (index) to (index) + 1. // 10. Let resultStr be the substring of str from (index) to (index) + 1.
auto result_str = PrimitiveString::create(vm, Utf16String::create(str.substring_view(index.as_index(), 1))); auto result_str = PrimitiveString::create(vm, str.substring_view(index.as_index(), 1));
// 11. Return the PropertyDescriptor { [[Value]]: resultStr, [[Writable]]: false, [[Enumerable]]: true, [[Configurable]]: false }. // 11. Return the PropertyDescriptor { [[Value]]: resultStr, [[Writable]]: false, [[Enumerable]]: true, [[Configurable]]: false }.
return PropertyDescriptor { return PropertyDescriptor {

View file

@ -25,7 +25,6 @@
#include <LibJS/Runtime/StringIterator.h> #include <LibJS/Runtime/StringIterator.h>
#include <LibJS/Runtime/StringObject.h> #include <LibJS/Runtime/StringObject.h>
#include <LibJS/Runtime/StringPrototype.h> #include <LibJS/Runtime/StringPrototype.h>
#include <LibJS/Runtime/Utf16String.h>
#include <LibJS/Runtime/Value.h> #include <LibJS/Runtime/Value.h>
#include <LibJS/Runtime/ValueInlines.h> #include <LibJS/Runtime/ValueInlines.h>
#include <LibUnicode/CharacterTypes.h> #include <LibUnicode/CharacterTypes.h>
@ -259,7 +258,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::at)
return js_undefined(); return js_undefined();
// 7. Return ? Get(O, ! ToString(𝔽(k))). // 7. Return ? Get(O, ! ToString(𝔽(k))).
return PrimitiveString::create(vm, Utf16String::create(string->utf16_string_view().substring_view(index.value(), 1))); return PrimitiveString::create(vm, string->utf16_string_view().substring_view(index.value(), 1));
} }
// 22.1.3.2 String.prototype.charAt ( pos ), https://tc39.es/ecma262/#sec-string.prototype.charat // 22.1.3.2 String.prototype.charAt ( pos ), https://tc39.es/ecma262/#sec-string.prototype.charat
@ -278,7 +277,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::char_at)
return PrimitiveString::create(vm, String {}); return PrimitiveString::create(vm, String {});
// 6. Return the substring of S from position to position + 1. // 6. Return the substring of S from position to position + 1.
return PrimitiveString::create(vm, Utf16String::create(string->utf16_string_view().substring_view(position, 1))); return PrimitiveString::create(vm, string->utf16_string_view().substring_view(position, 1));
} }
// 22.1.3.3 String.prototype.charCodeAt ( pos ), https://tc39.es/ecma262/#sec-string.prototype.charcodeat // 22.1.3.3 String.prototype.charCodeAt ( pos ), https://tc39.es/ecma262/#sec-string.prototype.charcodeat
@ -686,7 +685,7 @@ static ThrowCompletionOr<Value> pad_string(VM& vm, GC::Ref<PrimitiveString> stri
return string; return string;
// 5. If fillString is undefined, let filler be the String value consisting solely of the code unit 0x0020 (SPACE). // 5. If fillString is undefined, let filler be the String value consisting solely of the code unit 0x0020 (SPACE).
auto filler = Utf16String::create(Utf16Data { 0x20 }); auto filler = " "_utf16;
if (!fill_string.is_undefined()) { if (!fill_string.is_undefined()) {
// 6. Else, let filler be ? ToString(fillString). // 6. Else, let filler be ? ToString(fillString).
filler = TRY(fill_string.to_utf16_string(vm)); filler = TRY(fill_string.to_utf16_string(vm));
@ -702,7 +701,7 @@ static ThrowCompletionOr<Value> pad_string(VM& vm, GC::Ref<PrimitiveString> stri
StringBuilder truncated_string_filler_builder; StringBuilder truncated_string_filler_builder;
auto fill_code_units = filler.length_in_code_units(); auto fill_code_units = filler.length_in_code_units();
for (size_t i = 0; i < fill_length / fill_code_units; ++i) for (size_t i = 0; i < fill_length / fill_code_units; ++i)
truncated_string_filler_builder.append(filler.view()); truncated_string_filler_builder.append(filler);
// 9. Let truncatedStringFiller be the String value consisting of repeated concatenations of filler truncated to length fillLen. // 9. Let truncatedStringFiller be the String value consisting of repeated concatenations of filler truncated to length fillLen.
truncated_string_filler_builder.append(filler.substring_view(0, fill_length % fill_code_units)); truncated_string_filler_builder.append(filler.substring_view(0, fill_length % fill_code_units));
@ -1046,7 +1045,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::slice)
return PrimitiveString::create(vm, String {}); return PrimitiveString::create(vm, String {});
// 13. Return the substring of S from from to to. // 13. Return the substring of S from from to to.
return PrimitiveString::create(vm, Utf16String::create(string->utf16_string_view().substring_view(int_start, int_end - int_start))); return PrimitiveString::create(vm, string->utf16_string_view().substring_view(int_start, int_end - int_start));
} }
// 22.1.3.23 String.prototype.split ( separator, limit ), https://tc39.es/ecma262/#sec-string.prototype.split // 22.1.3.23 String.prototype.split ( separator, limit ), https://tc39.es/ecma262/#sec-string.prototype.split
@ -1127,7 +1126,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::split)
auto segment = string->utf16_string_view().substring_view(start, position - start); auto segment = string->utf16_string_view().substring_view(start, position - start);
// b. Append T to substrings. // b. Append T to substrings.
MUST(array->create_data_property_or_throw(array_length, PrimitiveString::create(vm, Utf16String::create(segment)))); MUST(array->create_data_property_or_throw(array_length, PrimitiveString::create(vm, segment)));
++array_length; ++array_length;
// c. If the number of elements in substrings is lim, return CreateArrayFromList(substrings). // c. If the number of elements in substrings is lim, return CreateArrayFromList(substrings).
@ -1145,7 +1144,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::split)
auto rest = string->utf16_string_view().substring_view(start); auto rest = string->utf16_string_view().substring_view(start);
// 16. Append T to substrings. // 16. Append T to substrings.
MUST(array->create_data_property_or_throw(array_length, PrimitiveString::create(vm, Utf16String::create(rest)))); MUST(array->create_data_property_or_throw(array_length, PrimitiveString::create(vm, rest)));
// 17. Return CreateArrayFromList(substrings). // 17. Return CreateArrayFromList(substrings).
return array; return array;
@ -1237,7 +1236,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::substring)
size_t to = max(final_start, final_end); size_t to = max(final_start, final_end);
// 10. Return the substring of S from from to to. // 10. Return the substring of S from from to to.
return PrimitiveString::create(vm, Utf16String::create(string->utf16_string_view().substring_view(from, to - from))); return PrimitiveString::create(vm, string->utf16_string_view().substring_view(from, to - from));
} }
enum class TargetCase { enum class TargetCase {
@ -1382,7 +1381,7 @@ String to_well_formed_string(Utf16String const& string)
// 6. Repeat, while k < strLen, // 6. Repeat, while k < strLen,
while (k < length) { while (k < length) {
// a. Let cp be CodePointAt(S, k). // a. Let cp be CodePointAt(S, k).
auto code_point = JS::code_point_at(string.view(), k); auto code_point = JS::code_point_at(string, k);
// b. If cp.[[IsUnpairedSurrogate]] is true, then // b. If cp.[[IsUnpairedSurrogate]] is true, then
if (code_point.is_unpaired_surrogate) { if (code_point.is_unpaired_surrogate) {
@ -1507,7 +1506,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::substr)
return PrimitiveString::create(vm, String {}); return PrimitiveString::create(vm, String {});
// 11. Return the substring of S from intStart to intEnd. // 11. Return the substring of S from intStart to intEnd.
return PrimitiveString::create(vm, Utf16String::create(string->utf16_string_view().substring_view(int_start, int_end - int_start))); return PrimitiveString::create(vm, string->utf16_string_view().substring_view(int_start, int_end - int_start));
} }
// B.2.2.2.1 CreateHTML ( string, tag, attribute, value ), https://tc39.es/ecma262/#sec-createhtml // B.2.2.2.1 CreateHTML ( string, tag, attribute, value ), https://tc39.es/ecma262/#sec-createhtml

View file

@ -1,157 +0,0 @@
/*
* Copyright (c) 2021-2023, Tim Flynn <trflynn89@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/StringView.h>
#include <LibJS/Runtime/Utf16String.h>
#include <LibJS/Runtime/VM.h>
namespace JS {
namespace Detail {
static NonnullRefPtr<Utf16StringImpl> the_empty_utf16_string()
{
static NonnullRefPtr<Utf16StringImpl> empty_string = Utf16StringImpl::create();
return empty_string;
}
Utf16StringImpl::Utf16StringImpl(Utf16Data string)
: m_string(move(string))
{
}
NonnullRefPtr<Utf16StringImpl> Utf16StringImpl::create()
{
return adopt_ref(*new Utf16StringImpl);
}
NonnullRefPtr<Utf16StringImpl> Utf16StringImpl::create(Utf16Data string)
{
return adopt_ref(*new Utf16StringImpl(move(string)));
}
NonnullRefPtr<Utf16StringImpl> Utf16StringImpl::create(StringView string)
{
auto result = MUST(utf8_to_utf16(string));
auto impl = create(move(result.data));
impl->m_cached_view.unsafe_set_code_point_length(result.code_point_count);
return impl;
}
NonnullRefPtr<Utf16StringImpl> Utf16StringImpl::create(Utf16View const& view)
{
Utf16Data string;
string.ensure_capacity(view.length_in_code_units());
if (view.has_ascii_storage()) {
for (size_t i = 0; i < view.length_in_code_units(); ++i)
string.unchecked_append(static_cast<char16_t>(view.code_unit_at(i)));
} else {
string.unchecked_append(view.utf16_span().data(), view.length_in_code_units());
}
auto impl = create(move(string));
if (auto length_in_code_points = view.length_in_code_points_if_known(); length_in_code_points.has_value())
impl->m_cached_view.unsafe_set_code_point_length(*length_in_code_points);
return impl;
}
Utf16Data const& Utf16StringImpl::string() const
{
return m_string;
}
Utf16View Utf16StringImpl::view() const
{
return m_cached_view;
}
u32 Utf16StringImpl::compute_hash() const
{
if (m_string.is_empty())
return 0;
return string_hash((char const*)m_string.data(), m_string.size() * sizeof(u16));
}
}
Utf16String Utf16String::create()
{
return Utf16String { Detail::the_empty_utf16_string() };
}
Utf16String Utf16String::create(Utf16Data string)
{
return Utf16String { Detail::Utf16StringImpl::create(move(string)) };
}
Utf16String Utf16String::create(StringView string)
{
return Utf16String { Detail::Utf16StringImpl::create(string) };
}
Utf16String Utf16String::create(Utf16View const& string)
{
return Utf16String { Detail::Utf16StringImpl::create(string) };
}
Utf16String Utf16String::invalid()
{
static auto invalid = Utf16String {};
return invalid;
}
Utf16String::Utf16String(NonnullRefPtr<Detail::Utf16StringImpl> string)
: m_string(move(string))
{
}
Utf16Data const& Utf16String::string() const
{
return m_string->string();
}
Utf16View Utf16String::view() const
{
return m_string->view();
}
Utf16View Utf16String::substring_view(size_t code_unit_offset, size_t code_unit_length) const
{
return view().substring_view(code_unit_offset, code_unit_length);
}
Utf16View Utf16String::substring_view(size_t code_unit_offset) const
{
return view().substring_view(code_unit_offset);
}
String Utf16String::to_utf8() const
{
return MUST(view().to_utf8());
}
ByteString Utf16String::to_byte_string() const
{
return MUST(view().to_byte_string());
}
u16 Utf16String::code_unit_at(size_t index) const
{
return view().code_unit_at(index);
}
size_t Utf16String::length_in_code_units() const
{
return view().length_in_code_units();
}
bool Utf16String::is_empty() const
{
return view().is_empty();
}
}

View file

@ -1,197 +0,0 @@
/*
* Copyright (c) 2021-2023, Tim Flynn <trflynn89@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/ByteString.h>
#include <AK/NonnullRefPtr.h>
#include <AK/RefCounted.h>
#include <AK/Types.h>
#include <AK/Utf16View.h>
#include <AK/Vector.h>
#include <LibJS/Runtime/Completion.h>
namespace JS {
namespace Detail {
class JS_API Utf16StringImpl : public RefCounted<Utf16StringImpl> {
public:
~Utf16StringImpl() = default;
[[nodiscard]] static NonnullRefPtr<Utf16StringImpl> create();
[[nodiscard]] static NonnullRefPtr<Utf16StringImpl> create(Utf16Data);
[[nodiscard]] static NonnullRefPtr<Utf16StringImpl> create(StringView);
[[nodiscard]] static NonnullRefPtr<Utf16StringImpl> create(Utf16View const&);
Utf16Data const& string() const;
Utf16View view() const;
[[nodiscard]] u32 hash() const
{
if (!m_has_hash) {
m_hash = compute_hash();
m_has_hash = true;
}
return m_hash;
}
[[nodiscard]] bool operator==(Utf16StringImpl const& other) const { return string() == other.string(); }
private:
Utf16StringImpl() = default;
explicit Utf16StringImpl(Utf16Data string);
[[nodiscard]] u32 compute_hash() const;
mutable bool m_has_hash { false };
mutable u32 m_hash { 0 };
Utf16Data m_string;
Utf16View m_cached_view { m_string };
};
}
class JS_API Utf16String {
public:
[[nodiscard]] static Utf16String create();
[[nodiscard]] static Utf16String create(Utf16Data);
[[nodiscard]] static Utf16String create(StringView);
[[nodiscard]] static Utf16String create(Utf16View const&);
[[nodiscard]] static Utf16String invalid();
Utf16Data const& string() const;
Utf16View view() const;
Utf16View substring_view(size_t code_unit_offset, size_t code_unit_length) const;
Utf16View substring_view(size_t code_unit_offset) const;
[[nodiscard]] String to_utf8() const;
[[nodiscard]] ByteString to_byte_string() const;
u16 code_unit_at(size_t index) const;
size_t length_in_code_units() const;
bool is_empty() const;
bool is_valid() const { return m_string; }
[[nodiscard]] u32 hash() const { return m_string->hash(); }
[[nodiscard]] bool operator==(Utf16String const& other) const
{
if (m_string == other.m_string)
return true;
return *m_string == *other.m_string;
}
private:
Utf16String() = default;
explicit Utf16String(NonnullRefPtr<Detail::Utf16StringImpl>);
RefPtr<Detail::Utf16StringImpl> m_string;
};
}
namespace AK {
template<>
struct Traits<JS::Utf16String> : public DefaultTraits<JS::Utf16String> {
static unsigned hash(JS::Utf16String const& s) { return s.hash(); }
};
template<>
class Optional<JS::Utf16String> : public OptionalBase<JS::Utf16String> {
template<typename U>
friend class Optional;
public:
using ValueType = JS::Utf16String;
Optional() = default;
template<SameAs<OptionalNone> V>
Optional(V) { }
Optional(Optional<JS::Utf16String> const& other)
{
if (other.has_value())
m_value = other.m_value;
}
Optional(Optional&& other)
: m_value(other.m_value)
{
}
template<typename U = JS::Utf16String>
requires(!IsSame<OptionalNone, RemoveCVReference<U>>)
explicit(!IsConvertible<U&&, JS::Utf16String>) Optional(U&& value)
requires(!IsSame<RemoveCVReference<U>, Optional<JS::Utf16String>> && IsConstructible<JS::Utf16String, U &&>)
: m_value(forward<U>(value))
{
}
template<SameAs<OptionalNone> V>
Optional& operator=(V)
{
clear();
return *this;
}
Optional& operator=(Optional const& other)
{
if (this != &other) {
clear();
m_value = other.m_value;
}
return *this;
}
Optional& operator=(Optional&& other)
{
if (this != &other) {
clear();
m_value = other.m_value;
}
return *this;
}
void clear()
{
m_value = JS::Utf16String::invalid();
}
[[nodiscard]] bool has_value() const
{
return m_value.is_valid();
}
[[nodiscard]] JS::Utf16String& value() &
{
VERIFY(has_value());
return m_value;
}
[[nodiscard]] JS::Utf16String const& value() const&
{
VERIFY(has_value());
return m_value;
}
[[nodiscard]] JS::Utf16String value() &&
{
return release_value();
}
[[nodiscard]] JS::Utf16String release_value()
{
VERIFY(has_value());
JS::Utf16String released_value = m_value;
clear();
return released_value;
}
private:
JS::Utf16String m_value { JS::Utf16String::invalid() };
};
}

View file

@ -12,6 +12,7 @@
#include <AK/CharacterTypes.h> #include <AK/CharacterTypes.h>
#include <AK/StringBuilder.h> #include <AK/StringBuilder.h>
#include <AK/StringFloatingPointConversions.h> #include <AK/StringFloatingPointConversions.h>
#include <AK/Utf16String.h>
#include <AK/Utf8View.h> #include <AK/Utf8View.h>
#include <LibCrypto/BigInt/SignedBigInteger.h> #include <LibCrypto/BigInt/SignedBigInteger.h>
#include <LibJS/Runtime/AbstractOperations.h> #include <LibJS/Runtime/AbstractOperations.h>
@ -34,7 +35,6 @@
#include <LibJS/Runtime/StringObject.h> #include <LibJS/Runtime/StringObject.h>
#include <LibJS/Runtime/StringPrototype.h> #include <LibJS/Runtime/StringPrototype.h>
#include <LibJS/Runtime/SymbolObject.h> #include <LibJS/Runtime/SymbolObject.h>
#include <LibJS/Runtime/Utf16String.h>
#include <LibJS/Runtime/VM.h> #include <LibJS/Runtime/VM.h>
#include <LibJS/Runtime/Value.h> #include <LibJS/Runtime/Value.h>
#include <LibJS/Runtime/ValueInlines.h> #include <LibJS/Runtime/ValueInlines.h>
@ -59,12 +59,12 @@ static inline bool same_type_for_equality(Value const& lhs, Value const& rhs)
static Crypto::SignedBigInteger const BIGINT_ZERO { 0 }; static Crypto::SignedBigInteger const BIGINT_ZERO { 0 };
ALWAYS_INLINE bool both_number(Value const& lhs, Value const& rhs) static ALWAYS_INLINE bool both_number(Value const& lhs, Value const& rhs)
{ {
return lhs.is_number() && rhs.is_number(); return lhs.is_number() && rhs.is_number();
} }
ALWAYS_INLINE bool both_bigint(Value const& lhs, Value const& rhs) static ALWAYS_INLINE bool both_bigint(Value const& lhs, Value const& rhs)
{ {
return lhs.is_bigint() && rhs.is_bigint(); return lhs.is_bigint() && rhs.is_bigint();
} }
@ -455,7 +455,7 @@ ThrowCompletionOr<Utf16String> Value::to_utf16_string(VM& vm) const
return as_string().utf16_string(); return as_string().utf16_string();
auto utf8_string = TRY(to_string(vm)); auto utf8_string = TRY(to_string(vm));
return Utf16String::create(utf8_string.bytes_as_string_view()); return Utf16String::from_utf8(utf8_string);
} }
ThrowCompletionOr<String> Value::to_well_formed_string(VM& vm) const ThrowCompletionOr<String> Value::to_well_formed_string(VM& vm) const

View file

@ -5,16 +5,10 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/Utf16View.h>
#include <LibJS/Runtime/Completion.h>
#include <LibJS/Runtime/Utf16String.h>
#include <LibWeb/Bindings/SVGTextContentElementPrototype.h> #include <LibWeb/Bindings/SVGTextContentElementPrototype.h>
#include <LibWeb/CSS/Parser/Parser.h>
#include <LibWeb/DOM/Document.h> #include <LibWeb/DOM/Document.h>
#include <LibWeb/Layout/SVGTextBox.h> #include <LibWeb/Layout/Node.h>
#include <LibWeb/SVG/AttributeNames.h>
#include <LibWeb/SVG/AttributeParser.h> #include <LibWeb/SVG/AttributeParser.h>
#include <LibWeb/SVG/SVGGeometryElement.h>
#include <LibWeb/SVG/SVGTextContentElement.h> #include <LibWeb/SVG/SVGTextContentElement.h>
namespace Web::SVG { namespace Web::SVG {

View file

@ -5,15 +5,10 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/Utf16View.h>
#include <LibJS/Runtime/Completion.h>
#include <LibJS/Runtime/Utf16String.h>
#include <LibWeb/Bindings/SVGTextPositioningElementPrototype.h> #include <LibWeb/Bindings/SVGTextPositioningElementPrototype.h>
#include <LibWeb/CSS/Parser/Parser.h>
#include <LibWeb/DOM/Document.h> #include <LibWeb/DOM/Document.h>
#include <LibWeb/SVG/AttributeNames.h> #include <LibWeb/SVG/AttributeNames.h>
#include <LibWeb/SVG/AttributeParser.h> #include <LibWeb/SVG/AttributeParser.h>
#include <LibWeb/SVG/SVGGeometryElement.h>
#include <LibWeb/SVG/SVGTextPositioningElement.h> #include <LibWeb/SVG/SVGTextPositioningElement.h>
namespace Web::SVG { namespace Web::SVG {