Skip to content

Commit 9ef5b63

Browse files
author
Irina Yatsenko
committed
[MERGE #5652 @irinayat-MS] Sealed typed arrays should report themselves frozen only if empty
Merge pull request #5652 from irinayat-MS:sealTypedArray Fixes #5624 Rewritten and expanded tests for seal/freeze. Fixed the bug.
2 parents 3dae2d3 + 66ddb87 commit 9ef5b63

File tree

8 files changed

+301
-102
lines changed

8 files changed

+301
-102
lines changed

lib/Runtime/Library/TypedArray.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1114,6 +1114,15 @@ namespace Js
11141114
return SetItem(index, value);
11151115
}
11161116

1117+
BOOL TypedArrayBase::IsObjectArrayFrozen()
1118+
{
1119+
if (GetLength() > 0)
1120+
{
1121+
return false; // the backing buffer is always modifiable
1122+
}
1123+
return IsSealed();
1124+
}
1125+
11171126
BOOL TypedArrayBase::Is(Var aValue)
11181127
{
11191128
TypeId typeId = JavascriptOperators::GetTypeId(aValue);

lib/Runtime/Library/TypedArray.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ namespace Js
179179

180180
// objectArray support
181181
virtual BOOL SetItemWithAttributes(uint32 index, Var value, PropertyAttributes attributes) override;
182+
virtual BOOL IsObjectArrayFrozen() override;
182183

183184
Var FindMinOrMax(Js::ScriptContext * scriptContext, TypeId typeId, bool findMax);
184185
template<typename T, bool checkNaNAndNegZero> Var FindMinOrMax(Js::ScriptContext * scriptContext, bool findMax);

lib/Runtime/Types/SimpleDictionaryTypeHandler.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2288,6 +2288,15 @@ namespace Js
22882288
return false;
22892289
}
22902290

2291+
if (DynamicObject::IsAnyTypedArray(instance))
2292+
{
2293+
auto typedArray = static_cast<TypedArrayBase*>(instance);
2294+
if (!typedArray->IsObjectArrayFrozen())
2295+
{
2296+
return false;
2297+
}
2298+
}
2299+
22912300
// Since we've determined that the object was frozen, set the flag to avoid further checks into all properties
22922301
// (once frozen there is no way to go back to un-frozen).
22932302
this->SetFlags(IsSealedOnceFlag | IsFrozenOnceFlag);

test/es5/freeze.baseline

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

test/es5/freeze.js

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

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

11-
write("TestCase1");
12-
write(Object.freeze.length);
13-
write(Object.isFrozen({}));
14-
15-
write("TestCase2 - freeze & add a property");
16-
var a = {x:20, y:30};
17-
Object.freeze(a);
18-
SafeCall(function() { a.z = 50; });
19-
write(Object.getOwnPropertyNames(a));
20-
write(Object.isFrozen(a));
21-
22-
write("TestCase3 - freeze & delete a property");
23-
var a = {x:20, y:30};
24-
Object.freeze(a);
25-
SafeCall(function() { delete a.x; });
26-
write(Object.getOwnPropertyNames(a));
27-
write(Object.isFrozen(a));
28-
write(a.x);
29-
30-
write("TestCase4 - freeze & modify a property");
31-
var a = {x:20, y:30};
32-
Object.freeze(a);
33-
SafeCall(function() { a.x = 40; });
34-
SafeCall(function() { a.y = 60; });
35-
write(Object.getOwnPropertyNames(a));
36-
write(Object.isFrozen(a));
37-
write(a.x);
38-
39-
function SafeCall(f)
40-
{
41-
try
10+
var tests = [
4211
{
43-
f();
44-
}
45-
catch (e)
12+
name: "Add, delete, modify properties after freezing",
13+
body: function () {
14+
let a = {x: 42};
15+
16+
Object.freeze(a);
17+
assert.isFalse(Object.isExtensible(a));
18+
assert.isTrue(Object.isSealed(a));
19+
assert.isTrue(Object.isFrozen(a));
20+
21+
// cannot add new properties
22+
a.y = 17;
23+
assert.isFalse(a.hasOwnProperty('y'));
24+
assert.throws(function () { 'use strict'; a.y = 17; }, TypeError,
25+
"Should throw on creating a new property in frozen object in strict mode",
26+
"Cannot create property for a non-extensible object");
27+
28+
// cannot delete properties
29+
assert.isFalse(delete a.x);
30+
assert.isTrue(a.hasOwnProperty('x'));
31+
assert.throws(function () { 'use strict'; delete a.x; }, TypeError,
32+
"Should throw on creating a new property in frozen object in strict mode",
33+
"Calling delete on 'x' is not allowed in strict mode");
34+
35+
// cannot change prototype
36+
let b = {};
37+
assert.throws(function () { 'use strict'; Object.setPrototypeOf(a, b); }, TypeError,
38+
"Should throw on creating a new property in sealed object in strict mode",
39+
"Cannot create property for a non-extensible object");
40+
41+
// existing properties should be set to non-writable and non-configurable
42+
let descr = Object.getOwnPropertyDescriptor(a, 'x');
43+
assert.isFalse(descr.configurable);
44+
assert.isFalse(descr.writable);
45+
}
46+
},
47+
{
48+
name: "Add, delete, modify indexed elements of an array after freezing",
49+
body: function () {
50+
let a = [42];
51+
a[2] = 43;
52+
53+
Object.freeze(a);
54+
assert.isFalse(Object.isExtensible(a));
55+
assert.isTrue(Object.isSealed(a));
56+
assert.isTrue(Object.isFrozen(a));
57+
58+
// the array cannot be extended
59+
a[3] = 17;
60+
assert.areEqual(3, a.length);
61+
assert.isFalse(a.hasOwnProperty('3'))
62+
assert.throws(function () { 'use strict'; a[3] = 17; }, TypeError,
63+
"Should throw on creating a new property in frozen object in strict mode",
64+
"Cannot create property for a non-extensible object");
65+
66+
// a hole cannot be filled
67+
a[1] = 17;
68+
assert.areEqual(3, a.length);
69+
assert.isFalse(a.hasOwnProperty('1'))
70+
assert.throws(function () { 'use strict'; a[1] = 17; }, TypeError,
71+
"Should throw on creating a new property in frozen object in strict mode",
72+
"Cannot create property for a non-extensible object");
73+
74+
// existing elements cannot be deleted
75+
assert.isFalse(delete a[0]);
76+
assert.isTrue(a.hasOwnProperty('0'));
77+
assert.throws(function () { 'use strict'; delete a[0]; }, TypeError,
78+
"Should throw on creating a new property in frozen object in strict mode",
79+
"Calling delete on '0' is not allowed in strict mode");
80+
81+
// existing elements cannot be modified
82+
let descr = Object.getOwnPropertyDescriptor(a, '0');
83+
assert.isFalse(descr.configurable);
84+
assert.isFalse(descr.writable);
85+
a[0] = 17;
86+
assert.areEqual(42, a[0]);
87+
assert.throws(function () { 'use strict'; a[0] = 17; }, TypeError,
88+
"Should throw on creating a new property in frozen object in strict mode",
89+
"Assignment to read-only properties is not allowed in strict mode");
90+
91+
// the special 'length' property also cannot be modified
92+
let descr_len = Object.getOwnPropertyDescriptor(a, 'length');
93+
assert.isFalse(descr_len.configurable);
94+
assert.isFalse(descr_len.writable);
95+
}
96+
},
4697
{
47-
write("Exception: " + e.name);
48-
}
49-
}
98+
// what is the spec???
99+
// v8 doesn't allow freezing typed arrays at all
100+
name: "Add, delete, modify indexed elements of a typed array after freezing",
101+
body: function () {
102+
let a = new Int8Array(1);
103+
a[0] = 42;
104+
105+
Object.freeze(a); // should it throw?
106+
107+
assert.isFalse(Object.isExtensible(a));
108+
assert.isTrue(Object.isSealed(a));
109+
assert.isTrue(Object.isFrozen(a)); // should it return false?
110+
111+
// current behavior:
112+
// even though the array is frozen it's ok to modify existing elements
113+
a[0] = 17;
114+
assert.areEqual(17, a[0]);
115+
}
116+
},
117+
];
118+
119+
testRunner.runTests(tests, { verbose: false /*so no need to provide baseline*/ });

test/es5/rlexe.xml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,13 +105,11 @@
105105
<test>
106106
<default>
107107
<files>seal.js</files>
108-
<baseline>seal.baseline</baseline>
109108
</default>
110109
</test>
111110
<test>
112111
<default>
113112
<files>freeze.js</files>
114-
<baseline>freeze.baseline</baseline>
115113
</default>
116114
</test>
117115
<test>

test/es5/seal.baseline

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

0 commit comments

Comments
 (0)