summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Groh <mail@linusgroh.de>2021-04-11 17:16:00 +0200
committerAndreas Kling <kling@serenityos.org>2021-04-11 18:15:47 +0200
commit433a23cfdeda1ae3f2f8ee14ed841a6e7716381b (patch)
treeee2146d9ebcde770f2a4dbe3bcf0e1ee8c7c4521
parent72fbf26dd6011b93fd4b263764da0325fc639a80 (diff)
downloadserenity-433a23cfdeda1ae3f2f8ee14ed841a6e7716381b.zip
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.
-rw-r--r--Userland/Libraries/LibJS/Runtime/Object.cpp9
-rw-r--r--Userland/Libraries/LibJS/Tests/indexed-access-prototype-indirection.js29
2 files changed, 32 insertions, 6 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/Object.cpp b/Userland/Libraries/LibJS/Runtime/Object.cpp
index 3c989a7c7e..0e9bbaac85 100644
--- a/Userland/Libraries/LibJS/Runtime/Object.cpp
+++ b/Userland/Libraries/LibJS/Runtime/Object.cpp
@@ -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())
diff --git a/Userland/Libraries/LibJS/Tests/indexed-access-prototype-indirection.js b/Userland/Libraries/LibJS/Tests/indexed-access-prototype-indirection.js
new file mode 100644
index 0000000000..47444a3c0b
--- /dev/null
+++ b/Userland/Libraries/LibJS/Tests/indexed-access-prototype-indirection.js
@@ -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");
+ });
+});