Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit e607055

Browse filesBrowse files
guybedfordtargos
authored andcommitted
bootstrap: --frozen-intrinsics override problem workaround
PR-URL: #28254 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 64f8530 commit e607055
Copy full SHA for e607055

File tree

Expand file treeCollapse file tree

3 files changed

+225
-73
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

3 files changed

+225
-73
lines changed
Open diff view settings
Collapse file

‎doc/api/cli.md‎

Copy file name to clipboardExpand all lines: doc/api/cli.md
+1-38Lines changed: 1 addition & 38 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -209,44 +209,7 @@ Enable experimental frozen intrinsics like `Array` and `Object`.
209209

210210
Support is currently only provided for the root context and no guarantees are
211211
currently provided that `global.Array` is indeed the default intrinsic
212-
reference.
213-
214-
**Code breakage is highly likely with this flag**, since redefining any
215-
builtin properties on a subclass will throw in strict mode due to the ECMA-262
216-
issue https://github.com/tc39/ecma262/pull/1307. This flag may still change
217-
or be removed in the future.
218-
219-
To avoid these cases, any builtin function overrides should be defined upfront:
220-
221-
```js
222-
const o = {};
223-
// THROWS: Cannot assign read only property 'toString' of object
224-
o.toString = () => 'string';
225-
226-
class X {
227-
constructor() {
228-
this.toString = () => 'string';
229-
}
230-
}
231-
// THROWS: Cannot assign read only property 'toString' of object
232-
new X();
233-
```
234-
235-
```js
236-
// OK
237-
const o = { toString: () => 'string' };
238-
239-
class X {
240-
toString = undefined;
241-
constructor() {
242-
this.toString = () => 'string';
243-
}
244-
}
245-
// OK
246-
new X();
247-
```
248-
249-
212+
reference. Code may break under this flag.
250213

251214
### `--heapsnapshot-signal=signal`
252215
<!-- YAML
Collapse file

‎lib/internal/freeze_intrinsics.js‎

Copy file name to clipboardExpand all lines: lib/internal/freeze_intrinsics.js
+203-35Lines changed: 203 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,29 @@
1212
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1313
// See the License for the specific language governing permissions and
1414
// limitations under the License.
15-
// SPDX-License-Identifier: MIT
15+
// SPDX-License-Identifier: Apache-2.0
1616

1717
// Based upon:
1818
// https://github.com/google/caja/blob/master/src/com/google/caja/ses/startSES.js
1919
// https://github.com/google/caja/blob/master/src/com/google/caja/ses/repairES5.js
20-
// https://github.com/tc39/proposal-frozen-realms/blob/91ac390e3451da92b5c27e354b39e52b7636a437/shim/src/deep-freeze.js
20+
// https://github.com/tc39/proposal-ses/blob/e5271cc42a257a05dcae2fd94713ed2f46c08620/shim/src/freeze.js
2121

22-
/* global WebAssembly, SharedArrayBuffer */
22+
/* global WebAssembly, SharedArrayBuffer, console */
2323
/* eslint-disable no-restricted-globals */
2424
'use strict';
2525

2626
module.exports = function() {
27+
const {
28+
defineProperty,
29+
freeze,
30+
getOwnPropertyDescriptor,
31+
getOwnPropertyDescriptors,
32+
getOwnPropertyNames,
33+
getOwnPropertySymbols,
34+
getPrototypeOf
35+
} = Object;
36+
const objectHasOwnProperty = Object.prototype.hasOwnProperty;
37+
const { ownKeys } = Reflect;
2738
const {
2839
clearImmediate,
2940
clearInterval,
@@ -33,30 +44,112 @@ module.exports = function() {
3344
setTimeout
3445
} = require('timers');
3546

47+
const intrinsicPrototypes = [
48+
// Anonymous Intrinsics
49+
// IteratorPrototype
50+
getPrototypeOf(
51+
getPrototypeOf(new Array()[Symbol.iterator]())
52+
),
53+
// ArrayIteratorPrototype
54+
getPrototypeOf(new Array()[Symbol.iterator]()),
55+
// StringIteratorPrototype
56+
getPrototypeOf(new String()[Symbol.iterator]()),
57+
// MapIteratorPrototype
58+
getPrototypeOf(new Map()[Symbol.iterator]()),
59+
// SetIteratorPrototype
60+
getPrototypeOf(new Set()[Symbol.iterator]()),
61+
// GeneratorFunction
62+
getPrototypeOf(function* () {}),
63+
// AsyncFunction
64+
getPrototypeOf(async function() {}),
65+
// AsyncGeneratorFunction
66+
getPrototypeOf(async function* () {}),
67+
// TypedArray
68+
getPrototypeOf(Uint8Array),
69+
70+
// 19 Fundamental Objects
71+
Object.prototype, // 19.1
72+
Function.prototype, // 19.2
73+
Boolean.prototype, // 19.3
74+
75+
Error.prototype, // 19.5
76+
EvalError.prototype,
77+
RangeError.prototype,
78+
ReferenceError.prototype,
79+
SyntaxError.prototype,
80+
TypeError.prototype,
81+
URIError.prototype,
82+
83+
// 20 Numbers and Dates
84+
Number.prototype, // 20.1
85+
Date.prototype, // 20.3
86+
87+
// 21 Text Processing
88+
String.prototype, // 21.1
89+
RegExp.prototype, // 21.2
90+
91+
// 22 Indexed Collections
92+
Array.prototype, // 22.1
93+
94+
Int8Array.prototype,
95+
Uint8Array.prototype,
96+
Uint8ClampedArray.prototype,
97+
Int16Array.prototype,
98+
Uint16Array.prototype,
99+
Int32Array.prototype,
100+
Uint32Array.prototype,
101+
Float32Array.prototype,
102+
Float64Array.prototype,
103+
BigInt64Array.prototype,
104+
BigUint64Array.prototype,
105+
106+
// 23 Keyed Collections
107+
Map.prototype, // 23.1
108+
Set.prototype, // 23.2
109+
WeakMap.prototype, // 23.3
110+
WeakSet.prototype, // 23.4
111+
112+
// 24 Structured Data
113+
ArrayBuffer.prototype, // 24.1
114+
DataView.prototype, // 24.3
115+
Promise.prototype, // 25.4
116+
117+
// Other APIs / Web Compatibility
118+
console.Console.prototype,
119+
BigInt.prototype,
120+
WebAssembly.Module.prototype,
121+
WebAssembly.Instance.prototype,
122+
WebAssembly.Table.prototype,
123+
WebAssembly.Memory.prototype,
124+
WebAssembly.CompileError.prototype,
125+
WebAssembly.LinkError.prototype,
126+
WebAssembly.RuntimeError.prototype,
127+
SharedArrayBuffer.prototype
128+
];
36129
const intrinsics = [
37130
// Anonymous Intrinsics
38131
// ThrowTypeError
39-
Object.getOwnPropertyDescriptor(Function.prototype, 'caller').get,
132+
getOwnPropertyDescriptor(Function.prototype, 'caller').get,
40133
// IteratorPrototype
41-
Object.getPrototypeOf(
42-
Object.getPrototypeOf(new Array()[Symbol.iterator]())
134+
getPrototypeOf(
135+
getPrototypeOf(new Array()[Symbol.iterator]())
43136
),
44137
// ArrayIteratorPrototype
45-
Object.getPrototypeOf(new Array()[Symbol.iterator]()),
138+
getPrototypeOf(new Array()[Symbol.iterator]()),
46139
// StringIteratorPrototype
47-
Object.getPrototypeOf(new String()[Symbol.iterator]()),
140+
getPrototypeOf(new String()[Symbol.iterator]()),
48141
// MapIteratorPrototype
49-
Object.getPrototypeOf(new Map()[Symbol.iterator]()),
142+
getPrototypeOf(new Map()[Symbol.iterator]()),
50143
// SetIteratorPrototype
51-
Object.getPrototypeOf(new Set()[Symbol.iterator]()),
144+
getPrototypeOf(new Set()[Symbol.iterator]()),
52145
// GeneratorFunction
53-
Object.getPrototypeOf(function* () {}),
146+
getPrototypeOf(function* () {}),
54147
// AsyncFunction
55-
Object.getPrototypeOf(async function() {}),
148+
getPrototypeOf(async function() {}),
56149
// AsyncGeneratorFunction
57-
Object.getPrototypeOf(async function* () {}),
150+
getPrototypeOf(async function* () {}),
58151
// TypedArray
59-
Object.getPrototypeOf(Uint8Array),
152+
getPrototypeOf(Uint8Array),
60153

61154
// 18 The Global Object
62155
eval,
@@ -75,14 +168,13 @@ module.exports = function() {
75168
Boolean, // 19.3
76169
Symbol, // 19.4
77170

78-
// Disabled pending stack trace mutation handling
79-
// Error, // 19.5
80-
// EvalError,
81-
// RangeError,
82-
// ReferenceError,
83-
// SyntaxError,
84-
// TypeError,
85-
// URIError,
171+
Error, // 19.5
172+
EvalError,
173+
RangeError,
174+
ReferenceError,
175+
SyntaxError,
176+
TypeError,
177+
URIError,
86178

87179
// 20 Numbers and Dates
88180
Number, // 20.1
@@ -128,36 +220,37 @@ module.exports = function() {
128220
escape,
129221
unescape,
130222

131-
// Web compatibility
223+
// Other APIs / Web Compatibility
132224
clearImmediate,
133225
clearInterval,
134226
clearTimeout,
135227
setImmediate,
136228
setInterval,
137229
setTimeout,
138-
139-
// Other APIs
230+
console,
140231
BigInt,
141232
Atomics,
142233
WebAssembly,
143234
SharedArrayBuffer
144235
];
145236

146-
if (typeof Intl !== 'undefined')
237+
if (typeof Intl !== 'undefined') {
238+
intrinsicPrototypes.push(Intl.Collator.prototype);
239+
intrinsicPrototypes.push(Intl.DateTimeFormat.prototype);
240+
intrinsicPrototypes.push(Intl.ListFormat.prototype);
241+
intrinsicPrototypes.push(Intl.NumberFormat.prototype);
242+
intrinsicPrototypes.push(Intl.PluralRules.prototype);
243+
intrinsicPrototypes.push(Intl.RelativeTimeFormat.prototype);
147244
intrinsics.push(Intl);
245+
}
246+
247+
intrinsicPrototypes.forEach(enableDerivedOverrides);
148248

249+
const frozenSet = new WeakSet();
149250
intrinsics.forEach(deepFreeze);
150251

252+
// Objects that are deeply frozen.
151253
function deepFreeze(root) {
152-
153-
const { freeze, getOwnPropertyDescriptors, getPrototypeOf } = Object;
154-
const { ownKeys } = Reflect;
155-
156-
// Objects that are deeply frozen.
157-
// It turns out that Error is reachable from WebAssembly so it is
158-
// explicitly added here to ensure it is not frozen
159-
const frozenSet = new WeakSet([Error, Error.prototype]);
160-
161254
/**
162255
* "innerDeepFreeze()" acts like "Object.freeze()", except that:
163256
*
@@ -246,4 +339,79 @@ module.exports = function() {
246339
innerDeepFreeze(root);
247340
return root;
248341
}
342+
343+
/**
344+
* For a special set of properties (defined below), it ensures that the
345+
* effect of freezing does not suppress the ability to override these
346+
* properties on derived objects by simple assignment.
347+
*
348+
* Because of lack of sufficient foresight at the time, ES5 unfortunately
349+
* specified that a simple assignment to a non-existent property must fail if
350+
* it would override a non-writable data property of the same name. (In
351+
* retrospect, this was a mistake, but it is now too late and we must live
352+
* with the consequences.) As a result, simply freezing an object to make it
353+
* tamper proof has the unfortunate side effect of breaking previously correct
354+
* code that is considered to have followed JS best practices, if this
355+
* previous code used assignment to override.
356+
*
357+
* To work around this mistake, deepFreeze(), prior to freezing, replaces
358+
* selected configurable own data properties with accessor properties which
359+
* simulate what we should have specified -- that assignments to derived
360+
* objects succeed if otherwise possible.
361+
*/
362+
function enableDerivedOverride(obj, prop, desc) {
363+
if ('value' in desc && desc.configurable) {
364+
const value = desc.value;
365+
366+
function getter() {
367+
return value;
368+
}
369+
370+
// Re-attach the data property on the object so
371+
// it can be found by the deep-freeze traversal process.
372+
getter.value = value;
373+
374+
function setter(newValue) {
375+
if (obj === this) {
376+
// eslint-disable-next-line no-restricted-syntax
377+
throw new TypeError(
378+
`Cannot assign to read only property '${prop}' of object '${obj}'`
379+
);
380+
}
381+
if (objectHasOwnProperty.call(this, prop)) {
382+
this[prop] = newValue;
383+
} else {
384+
defineProperty(this, prop, {
385+
value: newValue,
386+
writable: true,
387+
enumerable: desc.enumerable,
388+
configurable: desc.configurable
389+
});
390+
}
391+
}
392+
393+
defineProperty(obj, prop, {
394+
get: getter,
395+
set: setter,
396+
enumerable: desc.enumerable,
397+
configurable: desc.configurable
398+
});
399+
}
400+
}
401+
402+
function enableDerivedOverrides(obj) {
403+
if (!obj) {
404+
return;
405+
}
406+
const descs = getOwnPropertyDescriptors(obj);
407+
if (!descs) {
408+
return;
409+
}
410+
getOwnPropertyNames(obj).forEach((prop) => {
411+
return enableDerivedOverride(obj, prop, descs[prop]);
412+
});
413+
getOwnPropertySymbols(obj).forEach((prop) => {
414+
return enableDerivedOverride(obj, prop, descs[prop]);
415+
});
416+
}
249417
};
Collapse file

‎test/parallel/test-freeze-intrinsics.js‎

Copy file name to clipboardExpand all lines: test/parallel/test-freeze-intrinsics.js
+21Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,24 @@ assert.throws(
77
() => Object.defineProperty = 'asdf',
88
TypeError
99
);
10+
11+
// Ensure we can extend Console
12+
{
13+
class ExtendedConsole extends console.Console {}
14+
15+
const s = new ExtendedConsole(process.stdout);
16+
const logs = [];
17+
s.log = (msg) => logs.push(msg);
18+
s.log('custom');
19+
s.log = undefined;
20+
assert.strictEqual(s.log, undefined);
21+
assert.strictEqual(logs.length, 1);
22+
assert.strictEqual(logs[0], 'custom');
23+
}
24+
25+
// Ensure we can write override Object prototype properties on instances
26+
{
27+
const o = {};
28+
o.toString = () => 'Custom toString';
29+
assert.strictEqual(o + 'asdf', 'Custom toStringasdf');
30+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.