|
| 1 | +// Adapted from SES/Caja - Copyright (C) 2011 Google Inc. |
| 2 | +// Copyright (C) 2018 Agoric |
| 3 | + |
| 4 | +// Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | +// you may not use this file except in compliance with the License. |
| 6 | +// You may obtain a copy of the License at |
| 7 | +// |
| 8 | +// http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | +// |
| 10 | +// Unless required by applicable law or agreed to in writing, software |
| 11 | +// distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | +// See the License for the specific language governing permissions and |
| 14 | +// limitations under the License. |
| 15 | +// SPDX-License-Identifier: MIT |
| 16 | + |
| 17 | +// based upon: |
| 18 | +// https://github.com/google/caja/blob/master/src/com/google/caja/ses/startSES.js |
| 19 | +// 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 |
| 21 | + |
| 22 | +/* global WebAssembly, SharedArrayBuffer, console */ |
| 23 | +'use strict'; |
| 24 | +module.exports=function(){ |
| 25 | + |
| 26 | +constintrinsics=[ |
| 27 | +// Anonymous Intrinsics |
| 28 | +// ThrowTypeError |
| 29 | +Object.getOwnPropertyDescriptor(Function.prototype,'caller').get, |
| 30 | +// IteratorPrototype |
| 31 | +Object.getPrototypeOf( |
| 32 | +Object.getPrototypeOf(newArray()[Symbol.iterator]()) |
| 33 | +), |
| 34 | +// ArrayIteratorPrototype |
| 35 | +Object.getPrototypeOf(newArray()[Symbol.iterator]()), |
| 36 | +// StringIteratorPrototype |
| 37 | +Object.getPrototypeOf(newString()[Symbol.iterator]()), |
| 38 | +// MapIteratorPrototype |
| 39 | +Object.getPrototypeOf(newMap()[Symbol.iterator]()), |
| 40 | +// SetIteratorPrototype |
| 41 | +Object.getPrototypeOf(newSet()[Symbol.iterator]()), |
| 42 | +// GeneratorFunction |
| 43 | +Object.getPrototypeOf(function*(){}), |
| 44 | +// AsyncFunction |
| 45 | +Object.getPrototypeOf(asyncfunction(){}), |
| 46 | +// AsyncGeneratorFunction |
| 47 | +Object.getPrototypeOf(asyncfunction*(){}), |
| 48 | +// TypedArray |
| 49 | +Object.getPrototypeOf(Uint8Array), |
| 50 | + |
| 51 | +// 18 The Global Object |
| 52 | +eval, |
| 53 | +isFinite, |
| 54 | +isNaN, |
| 55 | +parseFloat, |
| 56 | +parseInt, |
| 57 | +decodeURI, |
| 58 | +decodeURIComponent, |
| 59 | +encodeURI, |
| 60 | +encodeURIComponent, |
| 61 | + |
| 62 | +// 19 Fundamental Objects |
| 63 | +Object,// 19.1 |
| 64 | +Function,// 19.2 |
| 65 | +Boolean,// 19.3 |
| 66 | +Symbol,// 19.4 |
| 67 | + |
| 68 | +// Disabled pending stack trace mutation handling |
| 69 | +// Error, // 19.5 |
| 70 | +// EvalError, |
| 71 | +// RangeError, |
| 72 | +// ReferenceError, |
| 73 | +// SyntaxError, |
| 74 | +// TypeError, |
| 75 | +// URIError, |
| 76 | + |
| 77 | +// 20 Numbers and Dates |
| 78 | +Number,// 20.1 |
| 79 | +Math,// 20.2 |
| 80 | +Date,// 20.3 |
| 81 | + |
| 82 | +// 21 Text Processing |
| 83 | +String,// 21.1 |
| 84 | +RegExp,// 21.2 |
| 85 | + |
| 86 | +// 22 Indexed Collections |
| 87 | +Array,// 22.1 |
| 88 | + |
| 89 | +Int8Array, |
| 90 | +Uint8Array, |
| 91 | +Uint8ClampedArray, |
| 92 | +Int16Array, |
| 93 | +Uint16Array, |
| 94 | +Int32Array, |
| 95 | +Uint32Array, |
| 96 | +Float32Array, |
| 97 | +Float64Array, |
| 98 | +BigInt64Array, |
| 99 | +BigUint64Array, |
| 100 | + |
| 101 | +// 23 Keyed Collections |
| 102 | +Map,// 23.1 |
| 103 | +Set,// 23.2 |
| 104 | +WeakMap,// 23.3 |
| 105 | +WeakSet,// 23.4 |
| 106 | + |
| 107 | +// 24 Structured Data |
| 108 | +ArrayBuffer,// 24.1 |
| 109 | +DataView,// 24.3 |
| 110 | +JSON,// 24.5 |
| 111 | +Promise,// 25.4 |
| 112 | + |
| 113 | +// 26 Reflection |
| 114 | +Reflect,// 26.1 |
| 115 | +Proxy,// 26.2 |
| 116 | + |
| 117 | +// B.2.1 |
| 118 | +escape, |
| 119 | +unescape, |
| 120 | + |
| 121 | +// Web compatibility |
| 122 | +clearImmediate, |
| 123 | +clearInterval, |
| 124 | +clearTimeout, |
| 125 | +decodeURI, |
| 126 | +decodeURIComponent, |
| 127 | +encodeURI, |
| 128 | +encodeURIComponent, |
| 129 | +setImmediate, |
| 130 | +setInterval, |
| 131 | +setTimeout, |
| 132 | + |
| 133 | +// Other APIs |
| 134 | +console, |
| 135 | +BigInt, |
| 136 | +Atomics, |
| 137 | +WebAssembly, |
| 138 | +SharedArrayBuffer |
| 139 | +]; |
| 140 | + |
| 141 | +if(typeofIntl!=='undefined') |
| 142 | +intrinsics.push(Intl); |
| 143 | + |
| 144 | +intrinsics.forEach(deepFreeze); |
| 145 | + |
| 146 | +functiondeepFreeze(root){ |
| 147 | + |
| 148 | +const{ freeze, getOwnPropertyDescriptors, getPrototypeOf }=Object; |
| 149 | +const{ ownKeys }=Reflect; |
| 150 | + |
| 151 | +// Objects that are deeply frozen. |
| 152 | +// It turns out that Error is reachable from WebAssembly so it is |
| 153 | +// explicitly added here to ensure it is not frozen |
| 154 | +constfrozenSet=newWeakSet([Error,Error.prototype]); |
| 155 | + |
| 156 | +/** |
| 157 | + * "innerDeepFreeze()" acts like "Object.freeze()", except that: |
| 158 | + * |
| 159 | + * To deepFreeze an object is to freeze it and all objects transitively |
| 160 | + * reachable from it via transitive reflective property and prototype |
| 161 | + * traversal. |
| 162 | + */ |
| 163 | +functioninnerDeepFreeze(node){ |
| 164 | +// Objects that we have frozen in this round. |
| 165 | +constfreezingSet=newSet(); |
| 166 | + |
| 167 | +// If val is something we should be freezing but aren't yet, |
| 168 | +// add it to freezingSet. |
| 169 | +functionenqueue(val){ |
| 170 | +if(Object(val)!==val){ |
| 171 | +// ignore primitives |
| 172 | +return; |
| 173 | +} |
| 174 | +consttype=typeofval; |
| 175 | +if(type!=='object'&&type!=='function'){ |
| 176 | +// NB: handle for any new cases in future |
| 177 | +} |
| 178 | +if(frozenSet.has(val)||freezingSet.has(val)){ |
| 179 | +// todo use uncurried form |
| 180 | +// Ignore if already frozen or freezing |
| 181 | +return; |
| 182 | +} |
| 183 | +freezingSet.add(val);// todo use uncurried form |
| 184 | +} |
| 185 | + |
| 186 | +functiondoFreeze(obj){ |
| 187 | +// Immediately freeze the object to ensure reactive |
| 188 | +// objects such as proxies won't add properties |
| 189 | +// during traversal, before they get frozen. |
| 190 | + |
| 191 | +// Object are verified before being enqueued, |
| 192 | +// therefore this is a valid candidate. |
| 193 | +// Throws if this fails (strict mode). |
| 194 | +freeze(obj); |
| 195 | + |
| 196 | +// We rely upon certain commitments of Object.freeze and proxies here |
| 197 | + |
| 198 | +// Get stable/immutable outbound links before a Proxy has a chance to do |
| 199 | +// something sneaky. |
| 200 | +constproto=getPrototypeOf(obj); |
| 201 | +constdescs=getOwnPropertyDescriptors(obj); |
| 202 | +enqueue(proto); |
| 203 | +ownKeys(descs).forEach((name)=>{ |
| 204 | +// todo uncurried form |
| 205 | +// todo: getOwnPropertyDescriptors is guaranteed to return well-formed |
| 206 | +// descriptors, but they still inherit from Object.prototype. If |
| 207 | +// someone has poisoned Object.prototype to add 'value' or 'get' |
| 208 | +// properties, then a simple 'if ("value" in desc)' or 'desc.value' |
| 209 | +// test could be confused. We use hasOwnProperty to be sure about |
| 210 | +// whether 'value' is present or not, which tells us for sure that |
| 211 | +// this is a data property. |
| 212 | +constdesc=descs[name]; |
| 213 | +if('value'indesc){ |
| 214 | +// todo uncurried form |
| 215 | +enqueue(desc.value); |
| 216 | +}else{ |
| 217 | +enqueue(desc.get); |
| 218 | +enqueue(desc.set); |
| 219 | +} |
| 220 | +}); |
| 221 | +} |
| 222 | + |
| 223 | +functiondequeue(){ |
| 224 | +// New values added before forEach() has finished will be visited. |
| 225 | +freezingSet.forEach(doFreeze);// todo curried forEach |
| 226 | +} |
| 227 | + |
| 228 | +functioncommit(){ |
| 229 | +// todo curried forEach |
| 230 | +// we capture the real WeakSet.prototype.add above, in case someone |
| 231 | +// changes it. The two-argument form of forEach passes the second |
| 232 | +// argument as the 'this' binding, so we add to the correct set. |
| 233 | +freezingSet.forEach(frozenSet.add,frozenSet); |
| 234 | +} |
| 235 | + |
| 236 | +enqueue(node); |
| 237 | +dequeue(); |
| 238 | +commit(); |
| 239 | +} |
| 240 | + |
| 241 | +innerDeepFreeze(root); |
| 242 | +returnroot; |
| 243 | +} |
| 244 | +}; |
0 commit comments