mirror of
https://github.com/RGBCube/serenity
synced 2025-07-24 15:27:42 +00:00
LibJS: Handle Proxy with Array target in IsArray() abstract operation
This was missing from Value::is_array(), which is equivalent to the spec's IsArray() abstract operation - it treats a Proxy value with an Array target object as being an Array. It can throw, so needs both the global object and an exception check now.
This commit is contained in:
parent
9b35231453
commit
83be39c91a
11 changed files with 52 additions and 24 deletions
|
@ -1669,7 +1669,7 @@ Value ObjectExpression::execute(Interpreter& interpreter, GlobalObject& global_o
|
|||
return {};
|
||||
|
||||
if (property.type() == ObjectProperty::Type::Spread) {
|
||||
if (key.is_array()) {
|
||||
if (key.is_object() && key.as_object().is_array()) {
|
||||
auto& array_to_spread = static_cast<Array&>(key.as_object());
|
||||
for (auto& entry : array_to_spread.indexed_properties()) {
|
||||
object->indexed_properties().put(object, entry.index(), entry.value_and_attributes(&array_to_spread).value);
|
||||
|
@ -1695,7 +1695,6 @@ Value ObjectExpression::execute(Interpreter& interpreter, GlobalObject& global_o
|
|||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -58,11 +58,10 @@ void MarkupGenerator::value_to_html(Value value, StringBuilder& output_html, Has
|
|||
seen_objects.set(&value.as_object());
|
||||
}
|
||||
|
||||
if (value.is_array())
|
||||
return array_to_html(static_cast<const Array&>(value.as_object()), output_html, seen_objects);
|
||||
|
||||
if (value.is_object()) {
|
||||
auto& object = value.as_object();
|
||||
if (object.is_array())
|
||||
return array_to_html(static_cast<const Array&>(object), output_html, seen_objects);
|
||||
output_html.append(wrap_string_in_style(object.class_name(), StyleType::ObjectType));
|
||||
if (object.is_function())
|
||||
return function_to_html(object, output_html, seen_objects);
|
||||
|
|
|
@ -142,7 +142,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from)
|
|||
JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::is_array)
|
||||
{
|
||||
auto value = vm.argument(0);
|
||||
return Value(value.is_array());
|
||||
return Value(value.is_array(global_object));
|
||||
}
|
||||
|
||||
JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::of)
|
||||
|
|
|
@ -358,15 +358,15 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::concat)
|
|||
|
||||
for (size_t i = 0; i < vm.argument_count(); ++i) {
|
||||
auto argument = vm.argument(i);
|
||||
if (argument.is_array()) {
|
||||
if (argument.is_array(global_object)) {
|
||||
auto& argument_object = argument.as_object();
|
||||
new_array->indexed_properties().append_all(&argument_object, argument_object.indexed_properties());
|
||||
continue;
|
||||
}
|
||||
if (vm.exception())
|
||||
return {};
|
||||
} else {
|
||||
new_array->indexed_properties().append(argument);
|
||||
}
|
||||
}
|
||||
|
||||
return Value(new_array);
|
||||
}
|
||||
|
@ -1027,10 +1027,12 @@ static void recursive_array_flat(VM& vm, GlobalObject& global_object, Array& new
|
|||
if (vm.exception())
|
||||
return;
|
||||
|
||||
if (depth > 0 && value.is_array()) {
|
||||
if (depth > 0 && value.is_array(global_object)) {
|
||||
recursive_array_flat(vm, global_object, new_array, value.as_array(), depth - 1);
|
||||
continue;
|
||||
}
|
||||
if (vm.exception())
|
||||
return;
|
||||
if (!value.is_empty()) {
|
||||
new_array.indexed_properties().append(value);
|
||||
if (vm.exception())
|
||||
|
|
|
@ -48,7 +48,7 @@ String JSONObject::stringify_impl(GlobalObject& global_object, Value value, Valu
|
|||
if (replacer.is_object()) {
|
||||
if (replacer.as_object().is_function()) {
|
||||
state.replacer_function = &replacer.as_function();
|
||||
} else if (replacer.is_array()) {
|
||||
} else if (replacer.is_array(global_object)) {
|
||||
auto& replacer_object = replacer.as_object();
|
||||
auto replacer_length = length_of_array_like(global_object, replacer_object);
|
||||
if (vm.exception())
|
||||
|
@ -77,6 +77,8 @@ String JSONObject::stringify_impl(GlobalObject& global_object, Value value, Valu
|
|||
}
|
||||
state.property_list = list;
|
||||
}
|
||||
if (vm.exception())
|
||||
return {};
|
||||
}
|
||||
|
||||
if (space.is_object()) {
|
||||
|
@ -172,8 +174,10 @@ String JSONObject::serialize_json_property(GlobalObject& global_object, Stringif
|
|||
return "null";
|
||||
}
|
||||
if (value.is_object() && !value.is_function()) {
|
||||
if (value.is_array())
|
||||
if (value.is_array(global_object))
|
||||
return serialize_json_array(global_object, state, static_cast<Array&>(value.as_object()));
|
||||
if (vm.exception())
|
||||
return {};
|
||||
return serialize_json_object(global_object, state, value.as_object());
|
||||
}
|
||||
if (value.is_bigint())
|
||||
|
|
|
@ -46,7 +46,6 @@ private:
|
|||
virtual void visit_edges(Visitor&) override;
|
||||
|
||||
virtual bool is_function() const override { return m_target.is_function(); }
|
||||
virtual bool is_array() const override { return m_target.is_array(); };
|
||||
|
||||
Object& m_target;
|
||||
Object& m_handler;
|
||||
|
|
|
@ -72,7 +72,8 @@ JS_DEFINE_NATIVE_FUNCTION(StringConstructor::raw)
|
|||
vm.throw_exception<TypeError>(global_object, ErrorType::StringRawCannotConvert, raw.is_null() ? "null" : "undefined");
|
||||
return {};
|
||||
}
|
||||
if (!raw.is_array())
|
||||
// FIXME: This should use length_of_array_like() and work with any object
|
||||
if (!raw.is_object() || !raw.as_object().is_array())
|
||||
return js_string(vm, "");
|
||||
|
||||
auto* array = static_cast<Array*>(raw.to_object(global_object));
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <LibJS/Runtime/NumberObject.h>
|
||||
#include <LibJS/Runtime/Object.h>
|
||||
#include <LibJS/Runtime/PrimitiveString.h>
|
||||
#include <LibJS/Runtime/ProxyObject.h>
|
||||
#include <LibJS/Runtime/RegExpObject.h>
|
||||
#include <LibJS/Runtime/StringObject.h>
|
||||
#include <LibJS/Runtime/Symbol.h>
|
||||
|
@ -196,14 +197,29 @@ static String double_to_string(double d)
|
|||
return builder.to_string();
|
||||
}
|
||||
|
||||
bool Value::is_array() const
|
||||
// 7.2.2 IsArray, https://tc39.es/ecma262/#sec-isarray
|
||||
bool Value::is_array(GlobalObject& global_object) const
|
||||
{
|
||||
return is_object() && as_object().is_array();
|
||||
if (!is_object())
|
||||
return false;
|
||||
auto& object = as_object();
|
||||
if (object.is_array())
|
||||
return true;
|
||||
if (is<ProxyObject>(object)) {
|
||||
auto& proxy = static_cast<ProxyObject const&>(object);
|
||||
if (proxy.is_revoked()) {
|
||||
auto& vm = global_object.vm();
|
||||
vm.throw_exception<TypeError>(global_object, ErrorType::ProxyRevoked);
|
||||
return false;
|
||||
}
|
||||
return Value(&proxy.target()).is_array(global_object);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Array& Value::as_array()
|
||||
{
|
||||
VERIFY(is_array());
|
||||
VERIFY(is_object() && as_object().is_array());
|
||||
return static_cast<Array&>(*m_value.as_object);
|
||||
}
|
||||
|
||||
|
|
|
@ -59,10 +59,10 @@ public:
|
|||
bool is_native_property() const { return m_type == Type::NativeProperty; }
|
||||
bool is_nullish() const { return is_null() || is_undefined(); }
|
||||
bool is_cell() const { return is_string() || is_accessor() || is_object() || is_bigint() || is_symbol() || is_native_property(); }
|
||||
bool is_array() const;
|
||||
bool is_array(GlobalObject&) const;
|
||||
bool is_function() const;
|
||||
bool is_constructor() const;
|
||||
bool is_regexp(GlobalObject& global_object) const;
|
||||
bool is_regexp(GlobalObject&) const;
|
||||
|
||||
bool is_nan() const { return is_number() && __builtin_isnan(as_double()); }
|
||||
bool is_infinity() const { return is_number() && __builtin_isinf(as_double()); }
|
||||
|
|
|
@ -22,4 +22,13 @@ test("arguments that evaluate to true", () => {
|
|||
expect(Array.isArray(new Array(10))).toBeTrue();
|
||||
expect(Array.isArray(new Array("a", "b", "c"))).toBeTrue();
|
||||
expect(Array.isArray(Array.prototype)).toBeTrue();
|
||||
expect(Array.isArray(new Proxy([], {}))).toBeTrue();
|
||||
});
|
||||
|
||||
test("Revoked Proxy as argument throws", () => {
|
||||
const revocable = Proxy.revocable([], {});
|
||||
revocable.revoke();
|
||||
expect(() => {
|
||||
Array.isArray(revocable.proxy);
|
||||
}).toThrowWithMessage(TypeError, "An operation was performed on a revoked Proxy object");
|
||||
});
|
||||
|
|
|
@ -386,11 +386,10 @@ static void print_value(JS::Value value, HashTable<JS::Object*>& seen_objects)
|
|||
seen_objects.set(&value.as_object());
|
||||
}
|
||||
|
||||
if (value.is_array())
|
||||
return print_array(static_cast<JS::Array&>(value.as_object()), seen_objects);
|
||||
|
||||
if (value.is_object()) {
|
||||
auto& object = value.as_object();
|
||||
if (object.is_array())
|
||||
return print_array(static_cast<JS::Array&>(object), seen_objects);
|
||||
if (object.is_function())
|
||||
return print_function(object, seen_objects);
|
||||
if (is<JS::Date>(object))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue