summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibJS
diff options
context:
space:
mode:
authorAnonymous <anon@mous.org>2022-02-12 16:02:45 -0800
committerAndreas Kling <kling@serenityos.org>2022-02-13 14:44:36 +0100
commit44a2ebea00a15f12eb01cac8c822dc5de2614056 (patch)
treedd5fb13cf0c61556a1ecf9da65a420a1d1c31826 /Userland/Libraries/LibJS
parent77511627e9ec3f047ad647aedf2c09b770dbe4cb (diff)
downloadserenity-44a2ebea00a15f12eb01cac8c822dc5de2614056.zip
LibJS: Correct the handling of accessors on strings
Diffstat (limited to 'Userland/Libraries/LibJS')
-rw-r--r--Userland/Libraries/LibJS/Runtime/Reference.cpp9
-rw-r--r--Userland/Libraries/LibJS/Tests/string-basic.js300
2 files changed, 307 insertions, 2 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/Reference.cpp b/Userland/Libraries/LibJS/Runtime/Reference.cpp
index bb892d36ae..8986511909 100644
--- a/Userland/Libraries/LibJS/Runtime/Reference.cpp
+++ b/Userland/Libraries/LibJS/Runtime/Reference.cpp
@@ -65,10 +65,15 @@ ThrowCompletionOr<Value> Reference::get_value(GlobalObject& global_object) const
if (is_property_reference()) {
auto* base_obj = TRY(m_base_value.to_object(global_object));
- if (is_private_reference())
+ if (is_private_reference()) {
+ // FIXME: We need to be able to specify the receiver for this
+ // if we want to use it in error messages in future
+ // as things currently stand this does the "wrong thing" but
+ // the error is unobservable
return base_obj->private_get(m_private_name);
+ }
- return base_obj->get(m_name);
+ return base_obj->internal_get(m_name, m_base_value);
}
VERIFY(m_base_type == BaseType::Environment);
diff --git a/Userland/Libraries/LibJS/Tests/string-basic.js b/Userland/Libraries/LibJS/Tests/string-basic.js
new file mode 100644
index 0000000000..52c4b7171f
--- /dev/null
+++ b/Userland/Libraries/LibJS/Tests/string-basic.js
@@ -0,0 +1,300 @@
+String.prototype[5] = "five";
+String.prototype.foo = "foo";
+var last_set_this_value = null;
+class TerribleClass {
+ get #private_strict() {
+ "use strict";
+ last_set_this_value = this;
+ }
+ get #private_non_strict() {
+ last_set_this_value = this;
+ }
+ get 10() {
+ "use strict";
+ return this.#private_strict;
+ }
+ get 11() {
+ return this.#private_non_strict;
+ }
+ set 12(v) {
+ "use strict";
+ return this.#private_strict;
+ }
+ set 13(v) {
+ return this.#private_non_strict;
+ }
+ get strict_get_private() {
+ "use strict";
+ return this.#private_strict;
+ }
+ get non_strict_get_private() {
+ return this.#private_non_strict;
+ }
+ set strict_set_private(v) {
+ "use strict";
+ this.#private_strict;
+ }
+ set non_strict_set_private(v) {
+ this.#private_non_strict;
+ }
+}
+String.prototype.__proto__ = {
+ get non_strict_this() {
+ return this;
+ },
+ get strict_this() {
+ "use strict";
+ return this;
+ },
+ set set_non_strict_this(v) {
+ last_set_this_value = this;
+ },
+ set set_strict_this(v) {
+ "use strict";
+ last_set_this_value = this;
+ },
+ get 6() {
+ "use strict";
+ return this;
+ },
+ get 7() {
+ return this;
+ },
+ set 8(v) {
+ "use strict";
+ last_set_this_value = this;
+ },
+ set 9(v) {
+ last_set_this_value = this;
+ },
+};
+String.prototype.__proto__.__proto__ = new TerribleClass();
+
+test("primitive string: numeric indexing", () => {
+ expect(""[0]).toBeUndefined();
+ expect("foo"[0]).toBe("f");
+ expect("foo"[2]).toBe("o");
+ expect("foo"[3]).toBeUndefined();
+ expect("foo"[-1]).toBeUndefined();
+ expect("foo"[1.5]).toBeUndefined();
+ expect("foo"[5]).toBe("five");
+ expect(typeof "foo"[6]).toBe("string");
+ expect("foo"[6]).toBe("foo");
+ expect(typeof "foo"[7]).toBe("object");
+ expect("foo"[7] instanceof String).toBeTrue();
+ expect("foo"[7] !== "foo"[7]).toBeTrue();
+ expect("foo"[7] !== String.prototype).toBeTrue();
+ "foo"[8] = "test";
+ expect(typeof last_set_this_value).toBe("string");
+ expect(last_set_this_value).toBe("foo");
+ last_set_this_value = null;
+ "foo"[9] = "test";
+ expect(typeof last_set_this_value).toBe("object");
+ expect(last_set_this_value instanceof String).toBeTrue();
+ expect(last_set_this_value !== String.prototype);
+ let old_this_value = last_set_this_value;
+ last_set_this_value = null;
+ "foo"[9] = "test";
+ expect(last_set_this_value !== old_this_value).toBeTrue();
+ last_set_this_value = null;
+
+ expect(() => "foo"[10]).toThrow();
+ expect(last_set_this_value).toBeNull();
+ last_set_this_value = null;
+ expect(() => "foo"[11]).toThrow();
+ expect(last_set_this_value).toBeNull();
+ last_set_this_value = null;
+ expect(() => ("foo"[12] = "wat")).toThrow();
+ expect(last_set_this_value).toBeNull();
+ last_set_this_value = null;
+ expect(() => ("foo"[13] = "wat")).toThrow();
+ expect(last_set_this_value).toBeNull();
+ last_set_this_value = null;
+});
+test("primitive string: string property indexing", () => {
+ expect(""["0"]).toBeUndefined();
+ expect("foo"["0"]).toBe("f");
+ expect("foo"["01"]).toBeUndefined();
+ expect("foo"[" 1"]).toBeUndefined();
+ expect("foo"["1 "]).toBeUndefined();
+ expect("foo"["2"]).toBe("o");
+ expect("foo"["3"]).toBeUndefined();
+ expect("foo"["-1"]).toBeUndefined();
+ expect("foo"["1.5"]).toBeUndefined();
+ expect("foo"["5"]).toBe("five");
+ expect(typeof "foo"["6"]).toBe("string");
+ expect("foo"["6"]).toBe("foo");
+ expect(typeof "foo"["7"]).toBe("object");
+ expect("foo"["7"] instanceof String).toBeTrue();
+ expect("foo"["7"] !== "foo"[7]).toBeTrue();
+ expect("foo"["7"] !== String.prototope).toBeTrue();
+ expect(""["length"]).toBe(0);
+ expect("foo"["length"]).toBe(3);
+ "foo"["8"] = "test";
+ expect(typeof last_set_this_value).toBe("string");
+ expect(last_set_this_value).toBe("foo");
+ last_set_this_value = null;
+ "foo"["9"] = "test";
+ expect(typeof last_set_this_value).toBe("object");
+ expect(last_set_this_value instanceof String).toBeTrue();
+ expect(last_set_this_value !== String.prototype);
+ let old_this_value = last_set_this_value;
+ last_set_this_value = null;
+ "foo"["9"] = "test";
+ expect(last_set_this_value !== old_this_value).toBeTrue();
+ last_set_this_value = null;
+
+ expect(() => "foo"["10"]).toThrow();
+ expect(last_set_this_value).toBeNull();
+ last_set_this_value = null;
+ expect(() => "foo"["11"]).toThrow();
+ expect(last_set_this_value).toBeNull();
+ last_set_this_value = null;
+ expect(() => ("foo"["12"] = "wat")).toThrow();
+ expect(last_set_this_value).toBeNull();
+ last_set_this_value = null;
+ expect(() => ("foo"["13"] = "wat")).toThrow();
+ expect(last_set_this_value).toBeNull();
+ last_set_this_value = null;
+});
+
+test("primitive string: string property name access", () => {
+ expect("".length).toBe(0);
+ expect("foo".length).toBe(3);
+ expect("foo".bar).toBeUndefined();
+ expect("foo".foo).toBe("foo");
+ expect(typeof "foo".strict_this).toBe("string");
+ expect("foo".strict_this).toBe("foo");
+ expect(typeof "foo".non_strict_this).toBe("object");
+ expect("foo".non_strict_this !== "foo".non_strict_this).toBeTrue();
+ expect("foo".non_strict_this !== String.prototype).toBeTrue();
+ expect("foo".non_strict_this instanceof String).toBeTrue();
+ let str = new String("foo");
+ str.set_strict_this = "test";
+ expect(typeof last_set_this_value).toBe("object");
+ expect(last_set_this_value).toBe(str);
+ last_set_this_value = null;
+ str.set_non_strict_this = "test";
+ expect(typeof last_set_this_value).toBe("object");
+ expect(last_set_this_value instanceof String).toBeTrue();
+ expect(last_set_this_value).toBe(str);
+ let old_this_value = last_set_this_value;
+ last_set_this_value = null;
+ str.set_non_strict_this = "test";
+ expect(last_set_this_value === old_this_value).toBeTrue();
+ last_set_this_value = null;
+
+ expect(() => "foo".strict_get_private).toThrow();
+ expect(last_set_this_value).toBeNull();
+ last_set_this_value = null;
+ expect(() => "foo".non_strict_get_private).toThrow();
+ expect(last_set_this_value).toBeNull();
+ last_set_this_value = null;
+ expect(() => ("foo".strict_set_private = "wat")).toThrow();
+ expect(last_set_this_value).toBeNull();
+ last_set_this_value = null;
+ expect(() => ("foo".non_strict_set_private = "wat")).toThrow();
+ expect(last_set_this_value).toBeNull();
+ last_set_this_value = null;
+});
+
+test("string object: string numeric indexing", () => {
+ expect(new String("")[0]).toBeUndefined();
+ expect(new String("foo")[0]).toBe("f");
+ expect(new String("foo")[2]).toBe("o");
+ expect(new String("foo")[3]).toBeUndefined();
+ expect(new String("foo")[-1]).toBeUndefined();
+ expect(new String("foo")[1.5]).toBeUndefined();
+ expect(new String("foo")[5]).toBe("five");
+ expect(typeof new String("foo")[6]).toBe("object");
+ let str = new String("foo");
+ expect(str[7]).toBe(str);
+ expect(typeof "foo"[7]).toBe("object");
+ expect(str[7] instanceof String).toBeTrue();
+ expect(str[7]).toBe(str);
+ expect(str[7] !== String.prototope).toBeTrue();
+ str["8"] = "test";
+ expect(typeof last_set_this_value).toBe("object");
+ expect(last_set_this_value).toBe(str);
+ last_set_this_value = null;
+ str["9"] = "test";
+ expect(typeof last_set_this_value).toBe("object");
+ expect(last_set_this_value instanceof String).toBeTrue();
+ expect(last_set_this_value).toBe(str);
+ let old_this_value = last_set_this_value;
+ last_set_this_value = null;
+ str["9"] = "test";
+ expect(last_set_this_value === old_this_value).toBeTrue();
+ last_set_this_value = null;
+});
+test("string object: string property indexing", () => {
+ expect(new String("")["0"]).toBeUndefined();
+ expect(new String("foo")["0"]).toBe("f");
+ expect(new String("foo")["2"]).toBe("o");
+ expect(new String("foo")["3"]).toBeUndefined();
+ expect(new String("foo")["-1"]).toBeUndefined();
+ expect(new String("foo")["1.5"]).toBeUndefined();
+ expect(new String("foo")["5"]).toBe("five");
+ expect(typeof new String("foo")["6"]).toBe("object");
+ let str = new String("foo");
+ expect(str["7"]).toBe(str);
+ expect(typeof "foo"["7"]).toBe("object");
+ expect(str["7"] instanceof String).toBeTrue();
+ expect(str["7"]).toBe(str);
+ expect(str["7"] !== String.prototope).toBeTrue();
+ str["set_strict_this"] = "test";
+ expect(typeof last_set_this_value).toBe("object");
+ expect(last_set_this_value).toBe(str);
+ last_set_this_value = null;
+ str["set_non_strict_this"] = "test";
+ expect(typeof last_set_this_value).toBe("object");
+ expect(last_set_this_value instanceof String).toBeTrue();
+ expect(last_set_this_value).toBe(str);
+ let old_this_value = last_set_this_value;
+ last_set_this_value = null;
+ str["set_non_strict_this"] = "test";
+ expect(last_set_this_value === old_this_value).toBeTrue();
+ last_set_this_value = null;
+
+ expect(() => str.strict_get_private).toThrow();
+ expect(last_set_this_value).toBeNull();
+ last_set_this_value = null;
+ expect(() => str.non_strict_get_private).toThrow();
+ expect(last_set_this_value).toBeNull();
+ last_set_this_value = null;
+ expect(() => (str.strict_set_private = "wat")).toThrow();
+ expect(last_set_this_value).toBeNull();
+ last_set_this_value = null;
+ expect(() => (str.non_strict_set_private = "wat")).toThrow();
+ expect(last_set_this_value).toBeNull();
+ last_set_this_value = null;
+});
+test("string object: string property name access", () => {
+ expect(new String("").length).toBe(0);
+ expect(new String("foo").length).toBe(3);
+ expect(new String("foo").bar).toBeUndefined();
+ expect(new String("foo").foo).toBe("foo");
+ expect(typeof new String("foo").strict_this).toBe("object");
+ let str = new String("foo");
+ expect(str.strict_this).toBe(str);
+ expect(typeof str.non_strict_this).toBe("object");
+ expect(str.non_strict_this === str.non_strict_this).toBeTrue();
+ expect(str.non_strict_this !== String.prototype).toBeTrue();
+ expect(str.non_strict_this instanceof String).toBeTrue();
+ expect(new String("foo").non_strict_this !== new String("foo").non_strict_this).toBeTrue();
+ expect(new String("foo").strict_this !== new String("foo").strict_this).toBeTrue();
+
+ last_set_this_value = null;
+ expect(() => str.strict_get_private).toThrow();
+ expect(last_set_this_value).toBeNull();
+ last_set_this_value = null;
+ expect(() => str.non_strict_get_private).toThrow();
+ expect(last_set_this_value).toBeNull();
+ last_set_this_value = null;
+ expect(() => (str.strict_set_private = "wat")).toThrow();
+ expect(last_set_this_value).toBeNull();
+ last_set_this_value = null;
+ expect(() => (str.non_strict_set_private = "wat")).toThrow();
+ expect(last_set_this_value).toBeNull();
+ last_set_this_value = null;
+});