mirror of
https://github.com/RGBCube/serenity
synced 2025-06-19 18:42:07 +00:00
LibJS: Rewrite Array.prototype.slice to be spec compliant
This makes it generic in the process (which is required by jQuery) This fixes 19 test262 test cases :^)
This commit is contained in:
parent
2e1a01a499
commit
d72aeb2e1a
3 changed files with 87 additions and 28 deletions
|
@ -387,46 +387,78 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::concat)
|
||||||
// 23.1.3.25 Array.prototype.slice ( start, end ), https://tc39.es/ecma262/#sec-array.prototype.slice
|
// 23.1.3.25 Array.prototype.slice ( start, end ), https://tc39.es/ecma262/#sec-array.prototype.slice
|
||||||
JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::slice)
|
JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::slice)
|
||||||
{
|
{
|
||||||
auto* array = Array::typed_this(vm, global_object);
|
auto* this_object = vm.this_value(global_object).to_object(global_object);
|
||||||
if (!array)
|
if (!this_object)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
auto* new_array = Array::create(global_object);
|
auto initial_length = length_of_array_like(global_object, *this_object);
|
||||||
if (vm.argument_count() == 0) {
|
|
||||||
new_array->indexed_properties().append_all(array, array->indexed_properties());
|
|
||||||
if (vm.exception())
|
|
||||||
return {};
|
|
||||||
return new_array;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t array_size = static_cast<ssize_t>(array->indexed_properties().array_like_size());
|
|
||||||
auto start_slice = vm.argument(0).to_i32(global_object);
|
|
||||||
if (vm.exception())
|
if (vm.exception())
|
||||||
return {};
|
return {};
|
||||||
auto end_slice = array_size;
|
|
||||||
|
|
||||||
if (start_slice > array_size)
|
auto relative_start = vm.argument(0).to_integer_or_infinity(global_object);
|
||||||
return new_array;
|
if (vm.exception())
|
||||||
|
return {};
|
||||||
|
|
||||||
if (start_slice < 0)
|
double actual_start;
|
||||||
start_slice = end_slice + start_slice;
|
|
||||||
|
|
||||||
if (vm.argument_count() >= 2) {
|
if (Value(relative_start).is_negative_infinity())
|
||||||
end_slice = vm.argument(1).to_i32(global_object);
|
actual_start = 0.0;
|
||||||
if (vm.exception())
|
else if (relative_start < 0.0)
|
||||||
return {};
|
actual_start = max((double)initial_length + relative_start, 0.0);
|
||||||
if (end_slice < 0)
|
else
|
||||||
end_slice = array_size + end_slice;
|
actual_start = min(relative_start, (double)initial_length);
|
||||||
else if (end_slice > array_size)
|
|
||||||
end_slice = array_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (ssize_t i = start_slice; i < end_slice; ++i) {
|
double relative_end;
|
||||||
new_array->indexed_properties().append(array->get(i));
|
|
||||||
|
if (vm.argument(1).is_undefined() || vm.argument(1).is_empty()) {
|
||||||
|
relative_end = (double)initial_length;
|
||||||
|
} else {
|
||||||
|
relative_end = vm.argument(1).to_integer_or_infinity(global_object);
|
||||||
if (vm.exception())
|
if (vm.exception())
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double final;
|
||||||
|
|
||||||
|
if (Value(relative_end).is_negative_infinity())
|
||||||
|
final = 0.0;
|
||||||
|
else if (relative_end < 0.0)
|
||||||
|
final = max((double)initial_length + relative_end, 0.0);
|
||||||
|
else
|
||||||
|
final = min(relative_end, (double)initial_length);
|
||||||
|
|
||||||
|
auto count = max(final - actual_start, 0.0);
|
||||||
|
|
||||||
|
// FIXME: Use ArraySpeciesCreate.
|
||||||
|
auto* new_array = Array::create(global_object, (size_t)count);
|
||||||
|
if (vm.exception())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
size_t index = 0;
|
||||||
|
|
||||||
|
while (actual_start < final) {
|
||||||
|
bool present = this_object->has_property(actual_start);
|
||||||
|
if (vm.exception())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if (present) {
|
||||||
|
auto value = this_object->get(actual_start).value_or(js_undefined());
|
||||||
|
if (vm.exception())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
new_array->define_property(index, value);
|
||||||
|
if (vm.exception())
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
++actual_start;
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_array->put(vm.names.length, Value(index));
|
||||||
|
if (vm.exception())
|
||||||
|
return {};
|
||||||
|
|
||||||
return new_array;
|
return new_array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,18 @@ describe("ability to work with generic non-array objects", () => {
|
||||||
expect(removed[1]).toBeUndefined();
|
expect(removed[1]).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("slice", () => {
|
||||||
|
const o = { length: 3, 0: "hello", 2: "serenity" };
|
||||||
|
const slice = Array.prototype.slice.call(o, 0, 2);
|
||||||
|
expect(o).toHaveLength(3);
|
||||||
|
expect(o[0]).toBe("hello");
|
||||||
|
expect(o[1]).toBeUndefined();
|
||||||
|
expect(o[2]).toBe("serenity");
|
||||||
|
expect(slice).toHaveLength(2);
|
||||||
|
expect(slice[0]).toBe("hello");
|
||||||
|
expect(slice[1]).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
test("join", () => {
|
test("join", () => {
|
||||||
expect(Array.prototype.join.call({})).toBe("");
|
expect(Array.prototype.join.call({})).toBe("");
|
||||||
expect(Array.prototype.join.call({ length: "foo" })).toBe("");
|
expect(Array.prototype.join.call({ length: "foo" })).toBe("");
|
||||||
|
|
|
@ -37,3 +37,18 @@ test("basic functionality", () => {
|
||||||
expect(array).toEqual(["hello", "friends", "serenity", 1]);
|
expect(array).toEqual(["hello", "friends", "serenity", 1]);
|
||||||
expect(slice).toEqual(["hello", "friends", "serenity", 1]);
|
expect(slice).toEqual(["hello", "friends", "serenity", 1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// FIXME: These tests are currently skipped because an invalid array length in this case is 2**32 or above.
|
||||||
|
// The codebase currently uses size_t for lengths, which is currently the same as u32 when building for Serenity.
|
||||||
|
// This means these lengths wrap around to 0, making the test not work correctly.
|
||||||
|
test.skip("Invalid lengths", () => {
|
||||||
|
var length = Math.pow(2, 32);
|
||||||
|
|
||||||
|
var obj = {
|
||||||
|
length: length,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
Array.prototype.slice.call(obj, 0);
|
||||||
|
}).toThrowWithMessage(RangeError, "Invalid array length");
|
||||||
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue