Skip to content

Commit c07fef6

Browse files
author
Irina Yatsenko
committed
[1.10>master] [MERGE #5530 @irinayat-MS] 'length' property of arrays should have same delete semantics as own non-indexed non-configurable props
Merge pull request #5530 from irinayat-MS:DeleteArrayLength Fixes OS#16382392 The assert has caught non-conforming behaviour of delete on 'length' property of Arrays. In strict mode delete should throw because 'length' is own and non-configurable.
2 parents be0a038 + 6f03ee0 commit c07fef6

File tree

5 files changed

+120
-30
lines changed

5 files changed

+120
-30
lines changed

lib/Runtime/Library/JavascriptArray.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12237,6 +12237,9 @@ using namespace Js;
1223712237
{
1223812238
if (propertyId == PropertyIds::length)
1223912239
{
12240+
JavascriptError::ThrowCantDeleteIfStrictModeOrNonconfigurable(
12241+
flags, GetScriptContext(), BuiltInPropertyRecords::length.buffer);
12242+
1224012243
return false;
1224112244
}
1224212245
return DynamicObject::DeleteProperty(propertyId, flags);
@@ -12246,6 +12249,9 @@ using namespace Js;
1224612249
{
1224712250
if (BuiltInPropertyRecords::length.Equals(propertyNameString))
1224812251
{
12252+
JavascriptError::ThrowCantDeleteIfStrictModeOrNonconfigurable(
12253+
flags, GetScriptContext(), BuiltInPropertyRecords::length.buffer);
12254+
1224912255
return false;
1225012256
}
1225112257
return DynamicObject::DeleteProperty(propertyNameString, flags);

test/Array/delete.baseline

Lines changed: 0 additions & 8 deletions
This file was deleted.

test/Array/delete.js

Lines changed: 91 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,100 @@
33
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
44
//-------------------------------------------------------------------------------------------------------
55

6-
function write(v) { WScript.Echo(v + ""); }
6+
if (this.WScript && this.WScript.LoadScriptFile) { // Check for running in ch
7+
this.WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
8+
}
79

8-
Object.prototype[5] = "obj.proto5";
9-
Object.prototype[7] = "obj.proto7";
10+
var tests = [
11+
{
12+
name: "Deleting of configurable non-indexed properties on Arrays",
13+
body: function () {
14+
var arr = [1,4,9,16];
1015

11-
Array.prototype[1] = "arr.proto.1";
12-
Array.prototype[2] = "arr.proto.2";
13-
Array.prototype[3] = "arr.proto.3";
14-
Array.prototype[6] = "arr.proto.6";
16+
arr.non_indexed = 'whatever';
17+
Object.defineProperty(arr, 'with_getter', { get: function() { return 'with getter'; }, configurable: true });
1518

16-
var n=8;
17-
var i=0;
19+
assert.areEqual('whatever', arr.non_indexed, "arr.non_indexed is set to 'whatever'");
20+
assert.areEqual('with getter', arr.with_getter, "arr.with_getter is set to 'with getter'");
1821

19-
var arr = new Array(n);
22+
var res = delete arr.non_indexed;
23+
assert.areEqual(true, res, "Deleting own property should succeed");
24+
assert.areEqual(undefined, arr.non_indexed, "arr.non_indexed has been deleted");
2025

21-
for (i=3;i<n;i++) { arr[i] = i * i + 1; }
26+
var res = delete arr.with_getter;
27+
assert.areEqual(true, res, "Deleting own property with a getter should succeed");
28+
assert.areEqual(undefined, arr.with_getter, "arr.with_getter has been deleted");
29+
}
30+
},
31+
{
32+
name: "Deleting of non-configurable non-indexed properties on Arrays",
33+
body: function () {
34+
var arr = [1,4,9,16];
35+
var id = 'id';
36+
Object.defineProperty(arr, id, { value: 17, configurable: false });
2237

23-
write(delete arr[1]); write("T1:" + arr.length + " : " + arr);
24-
write(delete arr[3]); write("T2:" + arr.length + " : " + arr);
25-
write(delete arr[n-1]); write("T3:" + arr.length + " : " + arr);
26-
write(delete arr[n+1]); write("T4:" + arr.length + " : " + arr);
38+
var res = delete arr[id];
39+
assert.areEqual(false, res);
40+
assert.areEqual(17, arr[id], "arr['id'] value after failed delete");
41+
42+
assert.throws(function () { 'use strict'; delete arr[id]; }, TypeError,
43+
"Should throw on delete of non-indexed property in array",
44+
"Calling delete on 'id' is not allowed in strict mode");
45+
}
46+
},
47+
{
48+
name: "Deleting of the 'length' property on Arrays",
49+
body: function () {
50+
var arr = [1,4,9,16];
51+
52+
var res = delete arr.length;
53+
assert.areEqual(false, res, "delete of arr.length should fail (as noop)");
54+
assert.areEqual(4, arr.length, "arr.length after attempting to delete it");
55+
56+
assert.throws(function () { 'use strict'; delete arr.length; }, TypeError,
57+
"Should throw on delete of 'length' property in array",
58+
"Calling delete on 'length' is not allowed in strict mode");
59+
assert.areEqual(4, arr.length, "arr.length after attempting to delete it in strict mode");
60+
}
61+
},
62+
{
63+
name: "Deleting of indexed properties on Arrays",
64+
body: function () {
65+
var arr = [1,4,9,16];
66+
67+
var res = delete arr[1];
68+
assert.areEqual(true, res, "delete of arr[1] should succeed");
69+
assert.areEqual(undefined, arr[1], "arr[1] value after delete should be undefined");
70+
assert.areEqual(4, arr.length, "the array's lenght should not change");
71+
72+
res = delete arr[42];
73+
assert.areEqual(true, res, "delete of arr[42] (beyond the array bounds) should succeed");
74+
assert.areEqual(4, arr.length, "the array's length is unchanged");
75+
76+
const last = arr.length - 1;
77+
res = delete arr[last];
78+
assert.areEqual(true, res, "delete of last element should succeed");
79+
assert.areEqual(undefined, arr[last], "arr[last] value after delete should be undefined");
80+
assert.areEqual(4, arr.length, "the array's lenght should not change");
81+
}
82+
},
83+
{
84+
name: "Deleting of indexed properties on Arrays that are also set on prototypes",
85+
body: function () {
86+
Object.prototype[4] = "obj.proto";
87+
Array.prototype[1] = "arr.proto";
88+
var arr = [1,4,9,16,25];
89+
90+
var res = delete arr[1];
91+
assert.areEqual(true, res, "delete of arr[1] should succeed");
92+
assert.areEqual("arr.proto", arr[1], "arr[1] after deleting should be picked up from the Array prototype");
93+
94+
var res = delete arr[4];
95+
assert.areEqual(true, res, "delete of arr[4] should succeed");
96+
assert.areEqual("obj.proto", arr[4], "arr[4] after deleting should be picked up from the Object prototype");
97+
assert.areEqual(5, arr.length, "arr.length after deleting of the last element");
98+
}
99+
},
100+
];
101+
102+
testRunner.runTests(tests, { verbose: false /*so no need to provide baseline*/ });

test/Array/rlexe.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,6 @@
146146
<test>
147147
<default>
148148
<files>delete.js</files>
149-
<baseline>delete.baseline</baseline>
150149
</default>
151150
</test>
152151
<test>

test/typedarray/delete.js

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,34 +17,51 @@ var tests = [
1717
ta.non_indexed = 'whatever';
1818
assert.areEqual('whatever', ta.non_indexed, "ta.non_indexed is set to 'whatever'");
1919

20-
delete ta.non_indexed;
20+
var res = delete ta.non_indexed;
21+
assert.areEqual(true, res, "delete of configurable non-indexed property should succeed");
2122
assert.areEqual(undefined, ta.non_indexed, "ta.non_indexed has been deleted");
2223
}
2324
},
2425
{
25-
name: "delete of nonconfigurable non-indexed properties on Typed arrays",
26+
name: "Deleting of non-configurable non-indexed properties on Typed arrays",
2627
body: function () {
2728
const ta = Int8Array.of(42);
2829
var id = 'id';
2930
Object.defineProperty(ta, id, { value: 17, configurable: false });
3031

31-
delete ta[id];
32+
var res = delete ta[id];
33+
assert.areEqual(false, res, "delete of non-configurable property should fail");
3234
assert.areEqual(17, ta[id], "ta['id'] value after failed delete");
3335

34-
assert.throws(function () { 'use strict'; delete ta[id]; }, TypeError, "Should throw on delete of indexed property in typed array", "Calling delete on 'id' is not allowed in strict mode");
36+
assert.throws(function () { 'use strict'; delete ta[id]; }, TypeError, "Should throw on delete of non-indexed property in typed array", "Calling delete on 'id' is not allowed in strict mode");
3537
}
3638
},
3739
{
3840
name: "Typed arrays don't support delete of indexed properties",
3941
body: function () {
4042
const ta = Int8Array.of(42);
4143

42-
delete ta[0];
44+
var res = delete ta[0];
45+
assert.areEqual(false, res, "delete of ta[0] should fail");
4346
assert.areEqual(42, ta[0], "ta[0] value after failed delete");
4447

4548
assert.throws(function () { 'use strict'; delete ta[0]; }, TypeError, "Should throw on delete of indexed property in typed array", "Calling delete on '0' is not allowed in strict mode");
4649
}
47-
}
50+
},
51+
{
52+
name: "Typed arrays ignore delete of the inherited 'length' property",
53+
body: function () {
54+
const ta = Int8Array.of(42);
55+
56+
var res = delete ta.length;
57+
assert.areEqual(true, res, "delete of ta.length should succeed (as noop)");
58+
assert.areEqual(1, ta.length, "ta.length after attempting to delete it");
59+
60+
res = (function () { 'use strict'; return delete ta.length; })();
61+
assert.areEqual(true, res, "delete of ta.length should succeed (as noop) in strict mode");
62+
assert.areEqual(1, ta.length, "ta.length after attempting to delete it in strict mode");
63+
}
64+
},
4865
];
4966

5067
testRunner.runTests(tests, { verbose: false /*so no need to provide baseline*/ });

0 commit comments

Comments
 (0)