mirror of
https://github.com/nodejs/node.git
synced 2025-05-01 17:03:34 +00:00

PR-URL: https://github.com/nodejs/node/pull/26849 Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Vse Mozhet Byt <vsemozhetbyt@gmail.com>
245 lines
6.9 KiB
JavaScript
245 lines
6.9 KiB
JavaScript
// Adapted from SES/Caja - Copyright (C) 2011 Google Inc.
|
|
// Copyright (C) 2018 Agoric
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
// Based upon:
|
|
// https://github.com/google/caja/blob/master/src/com/google/caja/ses/startSES.js
|
|
// https://github.com/google/caja/blob/master/src/com/google/caja/ses/repairES5.js
|
|
// https://github.com/tc39/proposal-frozen-realms/blob/91ac390e3451da92b5c27e354b39e52b7636a437/shim/src/deep-freeze.js
|
|
|
|
/* global WebAssembly, SharedArrayBuffer, console */
|
|
'use strict';
|
|
module.exports = function() {
|
|
|
|
const intrinsics = [
|
|
// Anonymous Intrinsics
|
|
// ThrowTypeError
|
|
Object.getOwnPropertyDescriptor(Function.prototype, 'caller').get,
|
|
// IteratorPrototype
|
|
Object.getPrototypeOf(
|
|
Object.getPrototypeOf(new Array()[Symbol.iterator]())
|
|
),
|
|
// ArrayIteratorPrototype
|
|
Object.getPrototypeOf(new Array()[Symbol.iterator]()),
|
|
// StringIteratorPrototype
|
|
Object.getPrototypeOf(new String()[Symbol.iterator]()),
|
|
// MapIteratorPrototype
|
|
Object.getPrototypeOf(new Map()[Symbol.iterator]()),
|
|
// SetIteratorPrototype
|
|
Object.getPrototypeOf(new Set()[Symbol.iterator]()),
|
|
// GeneratorFunction
|
|
Object.getPrototypeOf(function* () {}),
|
|
// AsyncFunction
|
|
Object.getPrototypeOf(async function() {}),
|
|
// AsyncGeneratorFunction
|
|
Object.getPrototypeOf(async function* () {}),
|
|
// TypedArray
|
|
Object.getPrototypeOf(Uint8Array),
|
|
|
|
// 18 The Global Object
|
|
eval,
|
|
isFinite,
|
|
isNaN,
|
|
parseFloat,
|
|
parseInt,
|
|
decodeURI,
|
|
decodeURIComponent,
|
|
encodeURI,
|
|
encodeURIComponent,
|
|
|
|
// 19 Fundamental Objects
|
|
Object, // 19.1
|
|
Function, // 19.2
|
|
Boolean, // 19.3
|
|
Symbol, // 19.4
|
|
|
|
// Disabled pending stack trace mutation handling
|
|
// Error, // 19.5
|
|
// EvalError,
|
|
// RangeError,
|
|
// ReferenceError,
|
|
// SyntaxError,
|
|
// TypeError,
|
|
// URIError,
|
|
|
|
// 20 Numbers and Dates
|
|
Number, // 20.1
|
|
Math, // 20.2
|
|
Date, // 20.3
|
|
|
|
// 21 Text Processing
|
|
String, // 21.1
|
|
RegExp, // 21.2
|
|
|
|
// 22 Indexed Collections
|
|
Array, // 22.1
|
|
|
|
Int8Array,
|
|
Uint8Array,
|
|
Uint8ClampedArray,
|
|
Int16Array,
|
|
Uint16Array,
|
|
Int32Array,
|
|
Uint32Array,
|
|
Float32Array,
|
|
Float64Array,
|
|
BigInt64Array,
|
|
BigUint64Array,
|
|
|
|
// 23 Keyed Collections
|
|
Map, // 23.1
|
|
Set, // 23.2
|
|
WeakMap, // 23.3
|
|
WeakSet, // 23.4
|
|
|
|
// 24 Structured Data
|
|
ArrayBuffer, // 24.1
|
|
DataView, // 24.3
|
|
JSON, // 24.5
|
|
Promise, // 25.4
|
|
|
|
// 26 Reflection
|
|
Reflect, // 26.1
|
|
Proxy, // 26.2
|
|
|
|
// B.2.1
|
|
escape,
|
|
unescape,
|
|
|
|
// Web compatibility
|
|
clearImmediate,
|
|
clearInterval,
|
|
clearTimeout,
|
|
decodeURI,
|
|
decodeURIComponent,
|
|
encodeURI,
|
|
encodeURIComponent,
|
|
setImmediate,
|
|
setInterval,
|
|
setTimeout,
|
|
|
|
// Other APIs
|
|
console,
|
|
BigInt,
|
|
Atomics,
|
|
WebAssembly,
|
|
SharedArrayBuffer
|
|
];
|
|
|
|
if (typeof Intl !== 'undefined')
|
|
intrinsics.push(Intl);
|
|
|
|
intrinsics.forEach(deepFreeze);
|
|
|
|
function deepFreeze(root) {
|
|
|
|
const { freeze, getOwnPropertyDescriptors, getPrototypeOf } = Object;
|
|
const { ownKeys } = Reflect;
|
|
|
|
// Objects that are deeply frozen.
|
|
// It turns out that Error is reachable from WebAssembly so it is
|
|
// explicitly added here to ensure it is not frozen
|
|
const frozenSet = new WeakSet([Error, Error.prototype]);
|
|
|
|
/**
|
|
* "innerDeepFreeze()" acts like "Object.freeze()", except that:
|
|
*
|
|
* To deepFreeze an object is to freeze it and all objects transitively
|
|
* reachable from it via transitive reflective property and prototype
|
|
* traversal.
|
|
*/
|
|
function innerDeepFreeze(node) {
|
|
// Objects that we have frozen in this round.
|
|
const freezingSet = new Set();
|
|
|
|
// If val is something we should be freezing but aren't yet,
|
|
// add it to freezingSet.
|
|
function enqueue(val) {
|
|
if (Object(val) !== val) {
|
|
// ignore primitives
|
|
return;
|
|
}
|
|
const type = typeof val;
|
|
if (type !== 'object' && type !== 'function') {
|
|
// NB: handle for any new cases in future
|
|
}
|
|
if (frozenSet.has(val) || freezingSet.has(val)) {
|
|
// TODO: Use uncurried form
|
|
// Ignore if already frozen or freezing
|
|
return;
|
|
}
|
|
freezingSet.add(val); // TODO: Use uncurried form
|
|
}
|
|
|
|
function doFreeze(obj) {
|
|
// Immediately freeze the object to ensure reactive
|
|
// objects such as proxies won't add properties
|
|
// during traversal, before they get frozen.
|
|
|
|
// Object are verified before being enqueued,
|
|
// therefore this is a valid candidate.
|
|
// Throws if this fails (strict mode).
|
|
freeze(obj);
|
|
|
|
// We rely upon certain commitments of Object.freeze and proxies here
|
|
|
|
// Get stable/immutable outbound links before a Proxy has a chance to do
|
|
// something sneaky.
|
|
const proto = getPrototypeOf(obj);
|
|
const descs = getOwnPropertyDescriptors(obj);
|
|
enqueue(proto);
|
|
ownKeys(descs).forEach((name) => {
|
|
// TODO: Uncurried form
|
|
// TODO: getOwnPropertyDescriptors is guaranteed to return well-formed
|
|
// descriptors, but they still inherit from Object.prototype. If
|
|
// someone has poisoned Object.prototype to add 'value' or 'get'
|
|
// properties, then a simple 'if ("value" in desc)' or 'desc.value'
|
|
// test could be confused. We use hasOwnProperty to be sure about
|
|
// whether 'value' is present or not, which tells us for sure that
|
|
// this is a data property.
|
|
const desc = descs[name];
|
|
if ('value' in desc) {
|
|
// todo uncurried form
|
|
enqueue(desc.value);
|
|
} else {
|
|
enqueue(desc.get);
|
|
enqueue(desc.set);
|
|
}
|
|
});
|
|
}
|
|
|
|
function dequeue() {
|
|
// New values added before forEach() has finished will be visited.
|
|
freezingSet.forEach(doFreeze); // TODO: Curried forEach
|
|
}
|
|
|
|
function commit() {
|
|
// TODO: Curried forEach
|
|
// We capture the real WeakSet.prototype.add above, in case someone
|
|
// changes it. The two-argument form of forEach passes the second
|
|
// argument as the 'this' binding, so we add to the correct set.
|
|
freezingSet.forEach(frozenSet.add, frozenSet);
|
|
}
|
|
|
|
enqueue(node);
|
|
dequeue();
|
|
commit();
|
|
}
|
|
|
|
innerDeepFreeze(root);
|
|
return root;
|
|
}
|
|
};
|