summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibJS
diff options
context:
space:
mode:
authorIdan Horowitz <idan.horowitz@gmail.com>2021-06-28 13:14:41 +0300
committerLinus Groh <mail@linusgroh.de>2021-06-28 13:27:13 +0100
commit596324ae9ccebea07304c1175d07943736ec2b73 (patch)
tree9572217a4c569624d2d99cc187ed53e4ed6294a3 /Userland/Libraries/LibJS
parentd1ffeaf66dc1bac23eec8144e7c963ca1ada76cb (diff)
downloadserenity-596324ae9ccebea07304c1175d07943736ec2b73.zip
LibJS: Rewrite String.raw() closer to the specification
This includes not throwing a custom exception and using the length_of_array_like abstract operation where required.
Diffstat (limited to 'Userland/Libraries/LibJS')
-rw-r--r--Userland/Libraries/LibJS/Runtime/ErrorTypes.h1
-rw-r--r--Userland/Libraries/LibJS/Runtime/StringConstructor.cpp46
-rw-r--r--Userland/Libraries/LibJS/Tests/builtins/String/String.raw.js2
3 files changed, 29 insertions, 20 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/ErrorTypes.h b/Userland/Libraries/LibJS/Runtime/ErrorTypes.h
index 93434515ba..fd8477593e 100644
--- a/Userland/Libraries/LibJS/Runtime/ErrorTypes.h
+++ b/Userland/Libraries/LibJS/Runtime/ErrorTypes.h
@@ -150,7 +150,6 @@
M(SpeciesConstructorDidNotCreate, "Species constructor did not create {}") \
M(SpeciesConstructorReturned, "Species constructor returned {}") \
M(StringMatchAllNonGlobalRegExp, "RegExp argument is non-global") \
- M(StringRawCannotConvert, "Cannot convert property 'raw' to object from {}") \
M(StringRepeatCountMustBe, "repeat count must be a {} number") \
M(ThisHasNotBeenInitialized, "|this| has not been initialized") \
M(ThisIsAlreadyInitialized, "|this| is already initialized") \
diff --git a/Userland/Libraries/LibJS/Runtime/StringConstructor.cpp b/Userland/Libraries/LibJS/Runtime/StringConstructor.cpp
index 7762f8c69e..c89bdd22af 100644
--- a/Userland/Libraries/LibJS/Runtime/StringConstructor.cpp
+++ b/Userland/Libraries/LibJS/Runtime/StringConstructor.cpp
@@ -6,6 +6,7 @@
#include <AK/StringBuilder.h>
#include <AK/Utf32View.h>
+#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/GlobalObject.h>
@@ -68,41 +69,50 @@ Value StringConstructor::construct(FunctionObject&)
// 22.1.2.4 String.raw ( template, ...substitutions ), https://tc39.es/ecma262/#sec-string.raw
JS_DEFINE_NATIVE_FUNCTION(StringConstructor::raw)
{
- auto* template_object = vm.argument(0).to_object(global_object);
+ auto* cooked = vm.argument(0).to_object(global_object);
if (vm.exception())
return {};
- auto raw = template_object->get(vm.names.raw);
+ auto raw_value = cooked->get(vm.names.raw).value_or(js_undefined());
if (vm.exception())
return {};
- if (raw.is_empty() || raw.is_nullish()) {
- vm.throw_exception<TypeError>(global_object, ErrorType::StringRawCannotConvert, raw.is_null() ? "null" : "undefined");
+
+ auto* raw = raw_value.to_object(global_object);
+ if (vm.exception())
return {};
- }
- // FIXME: This should use length_of_array_like() and work with any object
- if (!raw.is_object() || !raw.as_object().is_array())
+
+ auto literal_segments = length_of_array_like(global_object, *raw);
+ if (vm.exception())
+ return {};
+
+ if (literal_segments == 0)
return js_string(vm, "");
- auto* array = static_cast<Array*>(raw.to_object(global_object));
- auto& raw_array_elements = array->indexed_properties();
- StringBuilder builder;
+ const auto number_of_substituions = vm.argument_count() - 1;
- for (size_t i = 0; i < raw_array_elements.array_like_size(); ++i) {
- auto result = raw_array_elements.get(array, i);
+ StringBuilder builder;
+ for (size_t i = 0; i < literal_segments; ++i) {
+ auto next_key = String::number(i);
+ auto next_segment_value = raw->get(next_key).value_or(js_undefined());
if (vm.exception())
return {};
- if (!result.has_value())
- continue;
- builder.append(result.value().value.to_string(global_object));
+ auto next_segment = next_segment_value.to_string(global_object);
if (vm.exception())
return {};
- if (i + 1 < vm.argument_count() && i < raw_array_elements.array_like_size() - 1) {
- builder.append(vm.argument(i + 1).to_string(global_object));
+
+ builder.append(next_segment);
+
+ if (i + 1 == literal_segments)
+ break;
+
+ if (i < number_of_substituions) {
+ auto next = vm.argument(i + 1);
+ auto next_sub = next.to_string(global_object);
if (vm.exception())
return {};
+ builder.append(next_sub);
}
}
-
return js_string(vm, builder.build());
}
diff --git a/Userland/Libraries/LibJS/Tests/builtins/String/String.raw.js b/Userland/Libraries/LibJS/Tests/builtins/String/String.raw.js
index a866276034..27414d57ce 100644
--- a/Userland/Libraries/LibJS/Tests/builtins/String/String.raw.js
+++ b/Userland/Libraries/LibJS/Tests/builtins/String/String.raw.js
@@ -27,5 +27,5 @@ test("basic functionality", () => {
test("passing object with no 'raw' property", () => {
expect(() => {
String.raw({});
- }).toThrowWithMessage(TypeError, "Cannot convert property 'raw' to object from undefined");
+ }).toThrowWithMessage(TypeError, "ToObject on null or undefined");
});