mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 03:37:43 +00:00
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.
This commit is contained in:
parent
d1ffeaf66d
commit
596324ae9c
3 changed files with 29 additions and 20 deletions
|
@ -150,7 +150,6 @@
|
||||||
M(SpeciesConstructorDidNotCreate, "Species constructor did not create {}") \
|
M(SpeciesConstructorDidNotCreate, "Species constructor did not create {}") \
|
||||||
M(SpeciesConstructorReturned, "Species constructor returned {}") \
|
M(SpeciesConstructorReturned, "Species constructor returned {}") \
|
||||||
M(StringMatchAllNonGlobalRegExp, "RegExp argument is non-global") \
|
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(StringRepeatCountMustBe, "repeat count must be a {} number") \
|
||||||
M(ThisHasNotBeenInitialized, "|this| has not been initialized") \
|
M(ThisHasNotBeenInitialized, "|this| has not been initialized") \
|
||||||
M(ThisIsAlreadyInitialized, "|this| is already initialized") \
|
M(ThisIsAlreadyInitialized, "|this| is already initialized") \
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include <AK/StringBuilder.h>
|
#include <AK/StringBuilder.h>
|
||||||
#include <AK/Utf32View.h>
|
#include <AK/Utf32View.h>
|
||||||
|
#include <LibJS/Runtime/AbstractOperations.h>
|
||||||
#include <LibJS/Runtime/Array.h>
|
#include <LibJS/Runtime/Array.h>
|
||||||
#include <LibJS/Runtime/Error.h>
|
#include <LibJS/Runtime/Error.h>
|
||||||
#include <LibJS/Runtime/GlobalObject.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
|
// 22.1.2.4 String.raw ( template, ...substitutions ), https://tc39.es/ecma262/#sec-string.raw
|
||||||
JS_DEFINE_NATIVE_FUNCTION(StringConstructor::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())
|
if (vm.exception())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
auto raw = template_object->get(vm.names.raw);
|
auto raw_value = cooked->get(vm.names.raw).value_or(js_undefined());
|
||||||
if (vm.exception())
|
if (vm.exception())
|
||||||
return {};
|
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 {};
|
return {};
|
||||||
}
|
|
||||||
// FIXME: This should use length_of_array_like() and work with any object
|
auto literal_segments = length_of_array_like(global_object, *raw);
|
||||||
if (!raw.is_object() || !raw.as_object().is_array())
|
if (vm.exception())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if (literal_segments == 0)
|
||||||
return js_string(vm, "");
|
return js_string(vm, "");
|
||||||
|
|
||||||
auto* array = static_cast<Array*>(raw.to_object(global_object));
|
const auto number_of_substituions = vm.argument_count() - 1;
|
||||||
auto& raw_array_elements = array->indexed_properties();
|
|
||||||
StringBuilder builder;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < raw_array_elements.array_like_size(); ++i) {
|
StringBuilder builder;
|
||||||
auto result = raw_array_elements.get(array, i);
|
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())
|
if (vm.exception())
|
||||||
return {};
|
return {};
|
||||||
if (!result.has_value())
|
auto next_segment = next_segment_value.to_string(global_object);
|
||||||
continue;
|
|
||||||
builder.append(result.value().value.to_string(global_object));
|
|
||||||
if (vm.exception())
|
if (vm.exception())
|
||||||
return {};
|
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())
|
if (vm.exception())
|
||||||
return {};
|
return {};
|
||||||
|
builder.append(next_sub);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return js_string(vm, builder.build());
|
return js_string(vm, builder.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,5 +27,5 @@ test("basic functionality", () => {
|
||||||
test("passing object with no 'raw' property", () => {
|
test("passing object with no 'raw' property", () => {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
String.raw({});
|
String.raw({});
|
||||||
}).toThrowWithMessage(TypeError, "Cannot convert property 'raw' to object from undefined");
|
}).toThrowWithMessage(TypeError, "ToObject on null or undefined");
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue