Serenity Operating System
1test("deleting object properties", () => {
2 const o = {};
3 o.x = 1;
4 o.y = 2;
5 o.z = 3;
6 expect(Object.getOwnPropertyNames(o)).toHaveLength(3);
7
8 expect(delete o.x).toBeTrue();
9 expect(o.hasOwnProperty("x")).toBeFalse();
10 expect(o.hasOwnProperty("y")).toBeTrue();
11 expect(o.hasOwnProperty("z")).toBeTrue();
12 expect(Object.getOwnPropertyNames(o)).toHaveLength(2);
13
14 expect(delete o.y).toBeTrue();
15 expect(o.hasOwnProperty("x")).toBeFalse();
16 expect(o.hasOwnProperty("y")).toBeFalse();
17 expect(o.hasOwnProperty("z")).toBeTrue();
18 expect(Object.getOwnPropertyNames(o)).toHaveLength(1);
19
20 expect(delete o.z).toBeTrue();
21 expect(o.hasOwnProperty("x")).toBeFalse();
22 expect(o.hasOwnProperty("y")).toBeFalse();
23 expect(o.hasOwnProperty("z")).toBeFalse();
24 expect(Object.getOwnPropertyNames(o)).toHaveLength(0);
25});
26
27test("deleting array indices", () => {
28 const a = [3, 5, 7];
29
30 expect(Object.getOwnPropertyNames(a)).toHaveLength(4);
31
32 expect(delete a[0]).toBeTrue();
33 expect(a.hasOwnProperty(0)).toBeFalse();
34 expect(a.hasOwnProperty(1)).toBeTrue();
35 expect(a.hasOwnProperty(2)).toBeTrue();
36 expect(Object.getOwnPropertyNames(a)).toHaveLength(3);
37
38 expect(delete a[1]).toBeTrue();
39 expect(a.hasOwnProperty(0)).toBeFalse();
40 expect(a.hasOwnProperty(1)).toBeFalse();
41 expect(a.hasOwnProperty(2)).toBeTrue();
42 expect(Object.getOwnPropertyNames(a)).toHaveLength(2);
43
44 expect(delete a[2]).toBeTrue();
45 expect(a.hasOwnProperty(0)).toBeFalse();
46 expect(a.hasOwnProperty(1)).toBeFalse();
47 expect(a.hasOwnProperty(2)).toBeFalse();
48 expect(Object.getOwnPropertyNames(a)).toHaveLength(1);
49
50 expect(delete a["42"]).toBeTrue();
51 expect(Object.getOwnPropertyNames(a)).toHaveLength(1);
52});
53
54test("deleting non-configurable property", () => {
55 const q = {};
56 Object.defineProperty(q, "foo", { value: 1, writable: false, enumerable: false });
57 expect(q.foo).toBe(1);
58
59 expect(delete q.foo).toBeFalse();
60 expect(q.hasOwnProperty("foo")).toBeTrue();
61});
62
63test("deleting non-configurable property throws in strict mode", () => {
64 "use strict";
65 const q = {};
66 Object.defineProperty(q, "foo", { value: 1, writable: false, enumerable: false });
67 expect(q.foo).toBe(1);
68
69 expect(() => {
70 delete q.foo;
71 }).toThrowWithMessage(TypeError, "Cannot delete property 'foo' of [object Object]");
72 expect(q.hasOwnProperty("foo")).toBeTrue();
73});
74
75test("deleting super property", () => {
76 class A {
77 foo() {}
78 }
79
80 class B extends A {
81 bar() {
82 delete super.foo;
83 }
84
85 baz() {
86 delete super["foo"];
87 }
88 }
89
90 const obj = new B();
91 expect(() => {
92 obj.bar();
93 }).toThrowWithMessage(ReferenceError, "Can't delete a property on 'super'");
94
95 expect(() => {
96 obj.baz();
97 }).toThrowWithMessage(ReferenceError, "Can't delete a property on 'super'");
98});
99
100test("deleting an object computed property coerces the object to a property key", () => {
101 let called = false;
102 const obj = { prop1: 1, 2: 2 };
103
104 function createToPrimitiveFunction(object, valueToReturn) {
105 return function (hint) {
106 called = true;
107 console.log(this, object);
108 expect(this).toBe(object);
109 expect(hint).toBe("string");
110 return valueToReturn;
111 };
112 }
113
114 const a = {
115 [Symbol.toPrimitive]: function (hint) {
116 called = true;
117 expect(this).toBe(a);
118 expect(hint).toBe("string");
119 return "prop1";
120 },
121 };
122
123 const b = {
124 [Symbol.toPrimitive]: function (hint) {
125 called = true;
126 expect(this).toBe(b);
127 expect(hint).toBe("string");
128 return 2;
129 },
130 };
131
132 const c = {
133 [Symbol.toPrimitive]: function (hint) {
134 called = true;
135 expect(this).toBe(c);
136 expect(hint).toBe("string");
137 return {};
138 },
139 };
140
141 expect(Object.hasOwn(obj, "prop1")).toBeTrue();
142 expect(Object.hasOwn(obj, 2)).toBeTrue();
143
144 expect(delete obj[a]).toBeTrue();
145 expect(called).toBeTrue();
146 expect(Object.hasOwn(obj, "prop1")).toBeFalse();
147 expect(Object.hasOwn(obj, 2)).toBeTrue();
148 expect(obj.prop1).toBeUndefined();
149 expect(obj[2]).toBe(2);
150
151 called = false;
152 expect(delete obj[b]).toBeTrue();
153 expect(called).toBeTrue();
154 expect(Object.hasOwn(obj, "prop1")).toBeFalse();
155 expect(Object.hasOwn(obj, 2)).toBeFalse();
156 expect(obj.prop1).toBeUndefined();
157 expect(obj[2]).toBeUndefined();
158
159 called = false;
160 expect(() => {
161 delete obj[c];
162 }).toThrowWithMessage(
163 TypeError,
164 `Can't convert [object Object] to primitive with hint "string", its @@toPrimitive method returned an object`
165 );
166 expect(called).toBeTrue();
167});
168
169test("deleting a symbol returned by @@toPrimitive", () => {
170 let called = false;
171 const obj = { [Symbol.toStringTag]: "hello world" };
172
173 const a = {
174 [Symbol.toPrimitive]: function (hint) {
175 called = true;
176 expect(this).toBe(a);
177 expect(hint).toBe("string");
178 return Symbol.toStringTag;
179 },
180 };
181
182 expect(Object.hasOwn(obj, Symbol.toStringTag)).toBeTrue();
183 expect(delete obj[a]).toBeTrue();
184 expect(called).toBeTrue();
185 expect(Object.hasOwn(obj, Symbol.toStringTag)).toBeFalse();
186 expect(obj[Symbol.toStringTag]).toBeUndefined();
187});
188
189// FIXME: This currently does not work with the AST interpreter, but works with Bytecode.
190test.skip("delete always evaluates the lhs", () => {
191 const obj = { prop: 1 };
192 let called = false;
193 function a() {
194 called = true;
195 return obj;
196 }
197 expect(delete a()).toBeTrue();
198 expect(called).toBeTrue();
199 expect(obj).toBeDefined();
200 expect(Object.hasOwn(obj, "prop")).toBeTrue();
201 expect(obj.prop).toBe(1);
202
203 called = false;
204 expect(delete a().prop).toBeTrue();
205 expect(called).toBeTrue();
206 expect(obj).toBeDefined();
207 expect(Object.hasOwn(obj, "prop")).toBeFalse();
208 expect(obj.prop).toBeUndefined();
209
210 let b = 1;
211 expect(delete ++b).toBeTrue();
212 expect(b).toBe(2);
213
214 expect(delete b++).toBeTrue();
215 expect(b).toBe(3);
216
217 let c = { d: 1 };
218 expect(delete (b = c)).toBeTrue();
219 expect(b).toBeDefined();
220 expect(c).toBeDefined();
221 expect(b).toBe(c);
222
223 function d() {
224 throw new Error("called");
225 }
226
227 expect(() => {
228 delete d();
229 }).toThrowWithMessage(Error, "called");
230
231 expect(() => {
232 delete d().stack;
233 }).toThrowWithMessage(Error, "called");
234
235 expect(() => {
236 delete ~d();
237 }).toThrowWithMessage(Error, "called");
238
239 expect(() => {
240 delete new d();
241 }).toThrowWithMessage(Error, "called");
242});