1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-28 05:47:34 +00:00

LibJS: Replace standalone js_string() with PrimitiveString::create()

Note that js_rope_string() has been folded into this, the old name was
misleading - it would not always create a rope string, only if both
sides are not empty strings. Use a three-argument create() overload
instead.
This commit is contained in:
Linus Groh 2022-12-06 22:17:27 +00:00
parent 5db38d7ba1
commit 525f22d018
144 changed files with 656 additions and 672 deletions

View file

@ -33,7 +33,7 @@ JS_DEFINE_NATIVE_FUNCTION(CSSNamespace::escape)
return vm.throw_completion<JS::TypeError>(JS::ErrorType::BadArgCountAtLeastOne, "CSS.escape");
auto identifier = TRY(vm.argument(0).to_string(vm));
return JS::js_string(vm, Web::CSS::serialize_an_identifier(identifier));
return JS::PrimitiveString::create(vm, Web::CSS::serialize_an_identifier(identifier));
}
// https://www.w3.org/TR/css-conditional-3/#dom-css-supports

View file

@ -269,14 +269,14 @@ JS::ThrowCompletionOr<JS::MarkedVector<JS::Value>> LegacyPlatformObject::interna
for (u64 index = 0; index <= NumericLimits<u32>::max(); ++index) {
if (is_supported_property_index(index))
keys.append(js_string(vm, DeprecatedString::number(index)));
keys.append(JS::PrimitiveString::create(vm, DeprecatedString::number(index)));
else
break;
}
for (auto& named_property : supported_property_names()) {
if (TRY(is_named_property_exposed_on_object(named_property)))
keys.append(js_string(vm, named_property));
keys.append(JS::PrimitiveString::create(vm, named_property));
}
// 4. For each P of Os own property keys that is a String, in ascending chronological order of property creation, append P to keys.

View file

@ -97,7 +97,7 @@ JS_DEFINE_NATIVE_FUNCTION(LocationObject::href_getter)
// FIXME: 1. If this's relevant Document is non-null and its origin is not same origin-domain with the entry settings object's origin, then throw a "SecurityError" DOMException.
// 2. Return this's url, serialized.
return JS::js_string(vm, location_object->url().to_deprecated_string());
return JS::PrimitiveString::create(vm, location_object->url().to_deprecated_string());
}
// https://html.spec.whatwg.org/multipage/history.html#the-location-interface:dom-location-href-2
@ -127,7 +127,7 @@ JS_DEFINE_NATIVE_FUNCTION(LocationObject::pathname_getter)
// FIXME: 1. If this's relevant Document is non-null and its origin is not same origin-domain with the entry settings object's origin, then throw a "SecurityError" DOMException.
// 2. Return the result of URL path serializing this Location object's url.
return JS::js_string(vm, location_object->url().path());
return JS::PrimitiveString::create(vm, location_object->url().path());
}
// https://html.spec.whatwg.org/multipage/history.html#dom-location-hostname
@ -139,10 +139,10 @@ JS_DEFINE_NATIVE_FUNCTION(LocationObject::hostname_getter)
// 2. If this's url's host is null, return the empty string.
if (location_object->url().host().is_null())
return JS::js_string(vm, DeprecatedString::empty());
return JS::PrimitiveString::create(vm, DeprecatedString::empty());
// 3. Return this's url's host, serialized.
return JS::js_string(vm, location_object->url().host());
return JS::PrimitiveString::create(vm, location_object->url().host());
}
// https://html.spec.whatwg.org/multipage/history.html#dom-location-host
@ -157,14 +157,14 @@ JS_DEFINE_NATIVE_FUNCTION(LocationObject::host_getter)
// 3. If url's host is null, return the empty string.
if (url.host().is_null())
return JS::js_string(vm, DeprecatedString::empty());
return JS::PrimitiveString::create(vm, DeprecatedString::empty());
// 4. If url's port is null, return url's host, serialized.
if (!url.port().has_value())
return JS::js_string(vm, url.host());
return JS::PrimitiveString::create(vm, url.host());
// 5. Return url's host, serialized, followed by ":" and url's port, serialized.
return JS::js_string(vm, DeprecatedString::formatted("{}:{}", url.host(), *url.port()));
return JS::PrimitiveString::create(vm, DeprecatedString::formatted("{}:{}", url.host(), *url.port()));
}
// https://html.spec.whatwg.org/multipage/history.html#dom-location-hash
@ -176,10 +176,10 @@ JS_DEFINE_NATIVE_FUNCTION(LocationObject::hash_getter)
// 2. If this's url's fragment is either null or the empty string, return the empty string.
if (location_object->url().fragment().is_empty())
return JS::js_string(vm, DeprecatedString::empty());
return JS::PrimitiveString::create(vm, DeprecatedString::empty());
// 3. Return "#", followed by this's url's fragment.
return JS::js_string(vm, DeprecatedString::formatted("#{}", location_object->url().fragment()));
return JS::PrimitiveString::create(vm, DeprecatedString::formatted("#{}", location_object->url().fragment()));
}
// https://html.spec.whatwg.org/multipage/history.html#dom-location-search
@ -191,10 +191,10 @@ JS_DEFINE_NATIVE_FUNCTION(LocationObject::search_getter)
// 2. If this's url's query is either null or the empty string, return the empty string.
if (location_object->url().query().is_empty())
return JS::js_string(vm, DeprecatedString::empty());
return JS::PrimitiveString::create(vm, DeprecatedString::empty());
// 3. Return "?", followed by this's url's query.
return JS::js_string(vm, DeprecatedString::formatted("?{}", location_object->url().query()));
return JS::PrimitiveString::create(vm, DeprecatedString::formatted("?{}", location_object->url().query()));
}
// https://html.spec.whatwg.org/multipage/history.html#dom-location-protocol
@ -205,7 +205,7 @@ JS_DEFINE_NATIVE_FUNCTION(LocationObject::protocol_getter)
// FIXME: 1. If this's relevant Document is non-null and its origin is not same origin-domain with the entry settings object's origin, then throw a "SecurityError" DOMException.
// 2. Return this's url's scheme, followed by ":".
return JS::js_string(vm, DeprecatedString::formatted("{}:", location_object->url().scheme()));
return JS::PrimitiveString::create(vm, DeprecatedString::formatted("{}:", location_object->url().scheme()));
}
// https://html.spec.whatwg.org/multipage/history.html#dom-location-port
@ -217,10 +217,10 @@ JS_DEFINE_NATIVE_FUNCTION(LocationObject::port_getter)
// 2. If this's url's port is null, return the empty string.
if (!location_object->url().port().has_value())
return JS::js_string(vm, DeprecatedString::empty());
return JS::PrimitiveString::create(vm, DeprecatedString::empty());
// 3. Return this's url's port, serialized.
return JS::js_string(vm, DeprecatedString::number(*location_object->url().port()));
return JS::PrimitiveString::create(vm, DeprecatedString::number(*location_object->url().port()));
}
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-location-origin
@ -231,7 +231,7 @@ JS_DEFINE_NATIVE_FUNCTION(LocationObject::origin_getter)
// FIXME: 1. If this's relevant Document is non-null and its origin is not same origin-domain with the entry settings object's origin, then throw a "SecurityError" DOMException.
// 2. Return the serialization of this's url's origin.
return JS::js_string(vm, location_object->url().serialize_origin());
return JS::PrimitiveString::create(vm, location_object->url().serialize_origin());
}
// https://html.spec.whatwg.org/multipage/history.html#dom-location-reload
@ -297,7 +297,7 @@ JS::ThrowCompletionOr<Optional<JS::PropertyDescriptor>> LocationObject::internal
// 2. If the value of the [[DefaultProperties]] internal slot of this contains P, then set desc.[[Configurable]] to true.
auto property_key_value = property_key.is_symbol()
? JS::Value { property_key.as_symbol() }
: JS::js_string(vm, property_key.to_string());
: JS::PrimitiveString::create(vm, property_key.to_string());
if (m_default_properties.contains_slow(property_key_value))
descriptor->configurable = true;

View file

@ -340,8 +340,8 @@ JS::ThrowCompletionOr<JS::Value> CSSStyleDeclaration::internal_get(JS::PropertyK
if (property_id == CSS::PropertyID::Invalid)
return Base::internal_get(name, receiver);
if (auto maybe_property = property(property_id); maybe_property.has_value())
return { js_string(vm(), maybe_property->value->to_deprecated_string()) };
return { js_string(vm(), DeprecatedString::empty()) };
return { JS::PrimitiveString::create(vm(), maybe_property->value->to_deprecated_string()) };
return { JS::PrimitiveString::create(vm(), DeprecatedString::empty()) };
}
JS::ThrowCompletionOr<bool> CSSStyleDeclaration::internal_set(JS::PropertyKey const& name, JS::Value value, JS::Value receiver)

View file

@ -100,7 +100,7 @@ JS::Value MediaList::item_value(size_t index) const
{
if (index >= m_media.size())
return JS::js_undefined();
return JS::js_string(vm(), m_media[index].to_deprecated_string());
return JS::PrimitiveString::create(vm(), m_media[index].to_deprecated_string());
}
}

View file

@ -259,7 +259,7 @@ JS::Value DOMTokenList::item_value(size_t index) const
auto const& string = item(index);
if (string.is_null())
return JS::js_undefined();
return JS::js_string(vm(), string);
return JS::PrimitiveString::create(vm(), string);
}
}

View file

@ -624,8 +624,8 @@ JS::ThrowCompletionOr<void> EventTarget::process_event_handler_for_event(FlyStri
// the fourth having the value of event's colno attribute, the fifth having the value of event's error attribute, and with the callback this value set to event's currentTarget.
// Let return value be the callback's return value. [WEBIDL]
auto& error_event = verify_cast<HTML::ErrorEvent>(event);
auto* wrapped_message = JS::js_string(vm(), error_event.message());
auto* wrapped_filename = JS::js_string(vm(), error_event.filename());
auto wrapped_message = JS::PrimitiveString::create(vm(), error_event.message());
auto wrapped_filename = JS::PrimitiveString::create(vm(), error_event.filename());
auto wrapped_lineno = JS::Value(error_event.lineno());
auto wrapped_colno = JS::Value(error_event.colno());

View file

@ -134,7 +134,7 @@ WebIDL::ExceptionOr<JS::Value> package_data(JS::Realm& realm, ByteBuffer bytes,
return Infra::parse_json_bytes_to_javascript_value(vm, bytes);
case PackageDataType::Text:
// Return the result of running UTF-8 decode on bytes.
return JS::js_string(vm, DeprecatedString::copy(bytes));
return JS::PrimitiveString::create(vm, DeprecatedString::copy(bytes));
default:
VERIFY_NOT_REACHED();
}
@ -152,7 +152,7 @@ JS::NonnullGCPtr<JS::Promise> consume_body(JS::Realm& realm, BodyMixin const& ob
}
// 2. Let promise be a promise resolved with an empty byte sequence.
auto promise = WebIDL::create_resolved_promise(realm, JS::js_string(vm, DeprecatedString::empty()));
auto promise = WebIDL::create_resolved_promise(realm, JS::PrimitiveString::create(vm, DeprecatedString::empty()));
// 3. If objects body is non-null, then set promise to the result of fully reading body as promise given objects body.
auto const& body = object.body_impl();

View file

@ -53,11 +53,11 @@ JS::ThrowCompletionOr<JS::Object*> HeadersIterator::next()
switch (m_iteration_kind) {
case JS::Object::PropertyKind::Key:
return create_iterator_result_object(vm(), JS::js_string(vm(), StringView { pair.name }), false);
return create_iterator_result_object(vm(), JS::PrimitiveString::create(vm(), StringView { pair.name }), false);
case JS::Object::PropertyKind::Value:
return create_iterator_result_object(vm(), JS::js_string(vm(), StringView { pair.value }), false);
return create_iterator_result_object(vm(), JS::PrimitiveString::create(vm(), StringView { pair.value }), false);
case JS::Object::PropertyKind::KeyAndValue: {
auto* array = JS::Array::create_from(realm(), { JS::js_string(vm(), StringView { pair.name }), JS::js_string(vm(), StringView { pair.value }) });
auto* array = JS::Array::create_from(realm(), { JS::PrimitiveString::create(vm(), StringView { pair.name }), JS::PrimitiveString::create(vm(), StringView { pair.value }) });
return create_iterator_result_object(vm(), array, false);
}
default:

View file

@ -49,7 +49,7 @@ JS::NonnullGCPtr<JS::PromiseCapability> Body::fully_read_as_promise() const
// FIXME: Implement the streams spec - this is completely made up for now :^)
if (auto const* byte_buffer = m_source.get_pointer<ByteBuffer>()) {
auto result = DeprecatedString::copy(*byte_buffer);
return WebIDL::create_resolved_promise(realm, JS::js_string(vm, move(result)));
return WebIDL::create_resolved_promise(realm, JS::PrimitiveString::create(vm, move(result)));
}
// Empty, Blob, FormData
return WebIDL::create_rejected_promise(realm, JS::InternalError::create(realm, "Reading body isn't fully implemented"sv));

View file

@ -245,7 +245,7 @@ JS::Promise* Blob::text()
// FIXME: We still need to implement ReadableStream for this step to be fully valid.
// 3. Let promise be the result of reading all bytes from stream with reader
auto* promise = JS::Promise::create(realm());
auto* result = JS::js_string(vm(), DeprecatedString { m_byte_buffer.bytes() });
auto result = JS::PrimitiveString::create(vm(), DeprecatedString { m_byte_buffer.bytes() });
// 4. Return the result of transforming promise by a fulfillment handler that returns the result of running UTF-8 decode on its first argument.
promise->fulfill(result);

View file

@ -236,10 +236,10 @@ JS::MarkedVector<JS::Value> cross_origin_own_property_keys(Variant<Bindings::Loc
// 2. For each e of CrossOriginProperties(O), append e.[[Property]] to keys.
for (auto& entry : cross_origin_properties(object))
keys.append(JS::js_string(vm, move(entry.property)));
keys.append(JS::PrimitiveString::create(vm, move(entry.property)));
// 3. Return the concatenation of keys and « "then", @@toStringTag, @@hasInstance, @@isConcatSpreadable ».
keys.append(JS::js_string(vm, vm.names.then.as_string()));
keys.append(JS::PrimitiveString::create(vm, vm.names.then.as_string()));
keys.append(vm.well_known_symbol_to_string_tag());
keys.append(vm.well_known_symbol_has_instance());
keys.append(vm.well_known_symbol_is_concat_spreadable());

View file

@ -185,7 +185,7 @@ bool DOMStringMap::delete_existing_named_property(DeprecatedString const& name)
JS::Value DOMStringMap::named_item_value(FlyString const& name) const
{
return js_string(vm(), determine_value_of_named_property(name));
return JS::PrimitiveString::create(vm(), determine_value_of_named_property(name));
}
}

View file

@ -1252,7 +1252,7 @@ JS_DEFINE_NATIVE_FUNCTION(Window::prompt)
auto response = impl->prompt_impl(message, default_);
if (response.is_null())
return JS::js_null();
return JS::js_string(vm, response);
return JS::PrimitiveString::create(vm, response);
}
static JS::ThrowCompletionOr<TimerHandler> make_timer_handler(JS::VM& vm, JS::Value handler)
@ -1406,7 +1406,7 @@ JS_DEFINE_NATIVE_FUNCTION(Window::atob)
// decode_base64() returns a byte string. LibJS uses UTF-8 for strings. Use Latin1Decoder to convert bytes 128-255 to UTF-8.
auto decoder = TextCodec::decoder_for("windows-1252");
VERIFY(decoder);
return JS::js_string(vm, decoder->to_utf8(decoded.value()));
return JS::PrimitiveString::create(vm, decoder->to_utf8(decoded.value()));
}
JS_DEFINE_NATIVE_FUNCTION(Window::btoa)
@ -1424,7 +1424,7 @@ JS_DEFINE_NATIVE_FUNCTION(Window::btoa)
}
auto encoded = encode_base64(byte_string.span());
return JS::js_string(vm, move(encoded));
return JS::PrimitiveString::create(vm, move(encoded));
}
// https://html.spec.whatwg.org/multipage/interaction.html#dom-window-focus
@ -1752,7 +1752,7 @@ JS_DEFINE_NATIVE_FUNCTION(Window::scroll_by)
options = JS::Object::create(realm, nullptr);
MUST(options->set("left", vm.argument(0), ShouldThrowExceptions::No));
MUST(options->set("top", vm.argument(1), ShouldThrowExceptions::No));
MUST(options->set("behavior", JS::js_string(vm, "auto"), ShouldThrowExceptions::No));
MUST(options->set("behavior", JS::PrimitiveString::create(vm, "auto"), ShouldThrowExceptions::No));
}
auto left_value = TRY(options->get("left"));
@ -1845,7 +1845,7 @@ JS_DEFINE_NATIVE_FUNCTION(Window::structured_clone)
JS_DEFINE_NATIVE_FUNCTION(Window::origin_getter)
{
auto* impl = TRY(impl_from(vm));
return JS::js_string(vm, impl->associated_document().origin().serialize());
return JS::PrimitiveString::create(vm, impl->associated_document().origin().serialize());
}
JS_DEFINE_NATIVE_FUNCTION(Window::local_storage_getter)
@ -1863,7 +1863,7 @@ JS_DEFINE_NATIVE_FUNCTION(Window::session_storage_getter)
JS_DEFINE_NATIVE_FUNCTION(Window::name_getter)
{
auto* impl = TRY(impl_from(vm));
return JS::js_string(vm, impl->name());
return JS::PrimitiveString::create(vm, impl->name());
}
JS_DEFINE_NATIVE_FUNCTION(Window::name_setter)

View file

@ -233,7 +233,7 @@ JS::ThrowCompletionOr<JS::MarkedVector<JS::Value>> WindowProxy::internal_own_pro
// 5. Repeat while index < maxProperties,
for (size_t i = 0; i < max_properties; ++i) {
// 1. Add ! ToString(index) as the last element of keys.
keys.append(JS::js_string(vm, DeprecatedString::number(i)));
keys.append(JS::PrimitiveString::create(vm, DeprecatedString::number(i)));
// 2. Increment index by 1.
}

View file

@ -18,7 +18,7 @@ WebIDL::ExceptionOr<JS::Value> parse_json_string_to_javascript_value(JS::VM& vm,
auto& realm = *vm.current_realm();
// 1. Return ? Call(%JSON.parse%, undefined, « string »).
return TRY(JS::call(vm, realm.intrinsics().json_parse_function(), JS::js_undefined(), JS::js_string(vm, string)));
return TRY(JS::call(vm, realm.intrinsics().json_parse_function(), JS::js_undefined(), JS::PrimitiveString::create(vm, string)));
}
// https://infra.spec.whatwg.org/#parse-json-bytes-to-a-javascript-value

View file

@ -40,11 +40,11 @@ JS::Object* URLSearchParamsIterator::next()
auto& entry = m_url_search_params.m_list[m_index++];
if (m_iteration_kind == JS::Object::PropertyKind::Key)
return create_iterator_result_object(vm(), JS::js_string(vm(), entry.name), false);
return create_iterator_result_object(vm(), JS::PrimitiveString::create(vm(), entry.name), false);
else if (m_iteration_kind == JS::Object::PropertyKind::Value)
return create_iterator_result_object(vm(), JS::js_string(vm(), entry.value), false);
return create_iterator_result_object(vm(), JS::PrimitiveString::create(vm(), entry.value), false);
return create_iterator_result_object(vm(), JS::Array::create_from(realm(), { JS::js_string(vm(), entry.name), JS::js_string(vm(), entry.value) }), false);
return create_iterator_result_object(vm(), JS::Array::create_from(realm(), { JS::PrimitiveString::create(vm(), entry.name), JS::PrimitiveString::create(vm(), entry.value) }), false);
}
}

View file

@ -51,7 +51,7 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyMemoryPrototype::buffer_getter)
return JS::js_undefined();
auto* array_buffer = JS::ArrayBuffer::create(realm, &memory->data());
array_buffer->set_detach_key(JS::js_string(vm, "WebAssembly.Memory"));
array_buffer->set_detach_key(JS::PrimitiveString::create(vm, "WebAssembly.Memory"));
return array_buffer;
}

View file

@ -43,25 +43,25 @@ void WebAssemblyObject::initialize(JS::Realm& realm)
auto& vm = this->vm();
auto& memory_constructor = Bindings::ensure_web_constructor<WebAssemblyMemoryConstructor>(realm, "WebAssembly.Memory");
memory_constructor.define_direct_property(vm.names.name, js_string(vm, "WebAssembly.Memory"), JS::Attribute::Configurable);
memory_constructor.define_direct_property(vm.names.name, JS::PrimitiveString::create(vm, "WebAssembly.Memory"), JS::Attribute::Configurable);
auto& memory_prototype = Bindings::ensure_web_prototype<WebAssemblyMemoryPrototype>(realm, "WebAssemblyMemoryPrototype");
memory_prototype.define_direct_property(vm.names.constructor, &memory_constructor, JS::Attribute::Writable | JS::Attribute::Configurable);
define_direct_property("Memory", &memory_constructor, JS::Attribute::Writable | JS::Attribute::Configurable);
auto& instance_constructor = Bindings::ensure_web_constructor<WebAssemblyInstanceConstructor>(realm, "WebAssembly.Instance");
instance_constructor.define_direct_property(vm.names.name, js_string(vm, "WebAssembly.Instance"), JS::Attribute::Configurable);
instance_constructor.define_direct_property(vm.names.name, JS::PrimitiveString::create(vm, "WebAssembly.Instance"), JS::Attribute::Configurable);
auto& instance_prototype = Bindings::ensure_web_prototype<WebAssemblyInstancePrototype>(realm, "WebAssemblyInstancePrototype");
instance_prototype.define_direct_property(vm.names.constructor, &instance_constructor, JS::Attribute::Writable | JS::Attribute::Configurable);
define_direct_property("Instance", &instance_constructor, JS::Attribute::Writable | JS::Attribute::Configurable);
auto& module_constructor = Bindings::ensure_web_constructor<WebAssemblyModuleConstructor>(realm, "WebAssembly.Module");
module_constructor.define_direct_property(vm.names.name, js_string(vm, "WebAssembly.Module"), JS::Attribute::Configurable);
module_constructor.define_direct_property(vm.names.name, JS::PrimitiveString::create(vm, "WebAssembly.Module"), JS::Attribute::Configurable);
auto& module_prototype = Bindings::ensure_web_prototype<WebAssemblyModulePrototype>(realm, "WebAssemblyModulePrototype");
module_prototype.define_direct_property(vm.names.constructor, &module_constructor, JS::Attribute::Writable | JS::Attribute::Configurable);
define_direct_property("Module", &module_constructor, JS::Attribute::Writable | JS::Attribute::Configurable);
auto& table_constructor = Bindings::ensure_web_constructor<WebAssemblyTableConstructor>(realm, "WebAssembly.Table");
table_constructor.define_direct_property(vm.names.name, js_string(vm, "WebAssembly.Table"), JS::Attribute::Configurable);
table_constructor.define_direct_property(vm.names.name, JS::PrimitiveString::create(vm, "WebAssembly.Table"), JS::Attribute::Configurable);
auto& table_prototype = Bindings::ensure_web_prototype<WebAssemblyTablePrototype>(realm, "WebAssemblyTablePrototype");
table_prototype.define_direct_property(vm.names.constructor, &table_constructor, JS::Attribute::Writable | JS::Attribute::Configurable);
define_direct_property("Table", &table_constructor, JS::Attribute::Writable | JS::Attribute::Configurable);

View file

@ -207,7 +207,7 @@ void WebSocket::on_message(ByteBuffer message, bool is_text)
if (is_text) {
auto text_message = DeprecatedString(ReadonlyBytes(message));
HTML::MessageEventInit event_init;
event_init.data = JS::js_string(vm(), text_message);
event_init.data = JS::PrimitiveString::create(vm(), text_message);
event_init.origin = url();
dispatch_event(*HTML::MessageEvent::create(realm(), HTML::EventNames::message, event_init));
return;

View file

@ -121,10 +121,10 @@ WebIDL::ExceptionOr<JS::Value> XMLHttpRequest::response()
if (m_response_type == Bindings::XMLHttpRequestResponseType::Empty || m_response_type == Bindings::XMLHttpRequestResponseType::Text) {
// 1. If thiss state is not loading or done, then return the empty string.
if (m_state != State::Loading && m_state != State::Done)
return JS::js_string(vm, "");
return JS::PrimitiveString::create(vm, "");
// 2. Return the result of getting a text response for this.
return JS::js_string(vm, get_text_response());
return JS::PrimitiveString::create(vm, get_text_response());
}
// 2. If thiss state is not done, then return null.
if (m_state != State::Done)