LibJS: Fix array hole and string object indexing prototype indirection

This fixes two cases of indexed access (array holes, out-of-bounds
string object access) where we would not follow the prototype chain and
incorrectly return undefined:

    // Should be "a", returned undefined
    Object.setPrototypeOf([,], ["a"])[0]

    // Should be "a", returned undefined
    Object.setPrototypeOf(new String(""), new String("a"))[0]

The actual fix is simple, instead of returning early if the requested
index is past the string's length or within the indexed properties size
but has no value, we just continue the prototype chain traversal and get
correct behaviour from that.
This commit is contained in:
Linus Groh 2021-04-11 17:16:00 +02:00 committed by Andreas Kling
parent 72fbf26dd6
commit 433a23cfde
Notes: sideshowbarker 2024-07-18 20:31:18 +09:00
2 changed files with 32 additions and 6 deletions

View file

@ -751,19 +751,16 @@ Value Object::get_by_index(u32 property_index) const
{
const Object* object = this;
while (object) {
if (is<StringObject>(*this)) {
auto& string = static_cast<const StringObject*>(this)->primitive_string().string();
if (is<StringObject>(*object)) {
auto& string = static_cast<const StringObject&>(*object).primitive_string().string();
if (property_index < string.length())
return js_string(heap(), string.substring(property_index, 1));
return js_undefined();
}
if (static_cast<size_t>(property_index) < object->m_indexed_properties.array_like_size()) {
} else if (static_cast<size_t>(property_index) < object->m_indexed_properties.array_like_size()) {
auto result = object->m_indexed_properties.get(const_cast<Object*>(this), property_index);
if (vm().exception())
return {};
if (result.has_value() && !result.value().value.is_empty())
return result.value().value;
return {};
}
object = object->prototype();
if (vm().exception())

View file

@ -0,0 +1,29 @@
describe("normal behavior", () => {
test("regular object indexing", () => {
const o = {};
const p = { 0: "foo" };
Object.setPrototypeOf(o, p);
expect(o[0]).toBe("foo");
});
test("array object indexing", () => {
const o = [];
const p = ["foo"];
Object.setPrototypeOf(o, p);
expect(o[0]).toBe("foo");
});
test("array object hole indexing", () => {
const o = [,];
const p = ["foo"];
Object.setPrototypeOf(o, p);
expect(o[0]).toBe("foo");
});
test("string object indexing", () => {
const o = new String("");
const p = new String("a");
Object.setPrototypeOf(o, p);
expect(o[0]).toBe("a");
});
});