test262-host.js (9261B)
1 // This Source Code Form is subject to the terms of the Mozilla Public 2 // License, v. 2.0. If a copy of the MPL was not distributed with this 3 // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 5 // https://github.com/tc39/test262/blob/main/INTERPRETING.md#host-defined-functions 6 ;(function createHostObject(global) { 7 "use strict"; 8 9 // Save built-in functions and constructors. 10 var FunctionToString = global.Function.prototype.toString; 11 var ReflectApply = global.Reflect.apply; 12 var Atomics = global.Atomics; 13 var Error = global.Error; 14 var SharedArrayBuffer = global.SharedArrayBuffer; 15 var Int32Array = global.Int32Array; 16 17 // Save built-in shell functions. 18 var NewGlobal = global.newGlobal; 19 var setSharedArrayBuffer = global.setSharedArrayBuffer; 20 var getSharedArrayBuffer = global.getSharedArrayBuffer; 21 var evalInWorker = global.evalInWorker; 22 var monotonicNow = global.monotonicNow; 23 var gc = global.gc; 24 var clearKeptObjects = global.clearKeptObjects; 25 26 var hasCreateIsHTMLDDA = "createIsHTMLDDA" in global; 27 var hasThreads = ("helperThreadCount" in global ? global.helperThreadCount() > 0 : true); 28 var hasMailbox = typeof setSharedArrayBuffer === "function" && typeof getSharedArrayBuffer === "function"; 29 var hasEvalInWorker = typeof evalInWorker === "function"; 30 31 if (!hasCreateIsHTMLDDA && !("document" in global && "all" in global.document)) 32 throw new Error("no [[IsHTMLDDA]] object available for testing"); 33 34 var IsHTMLDDA = hasCreateIsHTMLDDA 35 ? global.createIsHTMLDDA() 36 : global.document.all; 37 38 // The $262.agent framework is not appropriate for browsers yet, and some 39 // test cases can't work in browsers (they block the main thread). 40 41 var shellCode = hasMailbox && hasEvalInWorker; 42 var sabTestable = Atomics && SharedArrayBuffer && hasThreads && shellCode; 43 44 global.$262 = { 45 __proto__: null, 46 createRealm() { 47 var newGlobalObject = NewGlobal(); 48 var createHostObjectFn = ReflectApply(FunctionToString, createHostObject, []); 49 newGlobalObject.Function(`${createHostObjectFn} createHostObject(this);`)(); 50 return newGlobalObject.$262; 51 }, 52 detachArrayBuffer: global.detachArrayBuffer, 53 evalScript: global.evaluateScript || global.evaluate, 54 global, 55 IsHTMLDDA, 56 gc() { 57 gc(); 58 }, 59 clearKeptObjects() { 60 clearKeptObjects(); 61 }, 62 agent: (function () { 63 64 // SpiderMonkey complication: With run-time argument --no-threads 65 // our test runner will not properly filter test cases that can't be 66 // run because agents can't be started, and so we do a little 67 // filtering here: We will quietly succeed and exit if an agent test 68 // should not have been run because threads cannot be started. 69 // 70 // Firefox complication: The test cases that use $262.agent can't 71 // currently work in the browser, so for now we rely on them not 72 // being run at all. 73 74 if (!sabTestable) { 75 let {reportCompare, quit} = global; 76 77 function notAvailable() { 78 // See comment above. 79 if (!hasThreads && shellCode) { 80 reportCompare(0, 0); 81 quit(0); 82 } 83 throw new Error("Agents not available"); 84 } 85 86 return { 87 start(script) { notAvailable() }, 88 broadcast(sab, id) { notAvailable() }, 89 getReport() { notAvailable() }, 90 sleep(s) { notAvailable() }, 91 monotonicNow, 92 } 93 } 94 95 // The SpiderMonkey implementation uses a designated shared buffer _ia 96 // for coordination, and spinlocks for everything except sleeping. 97 98 var _MSG_LOC = 0; // Low bit set: broadcast available; High bits: seq # 99 var _ID_LOC = 1; // ID sent with broadcast 100 var _ACK_LOC = 2; // Worker increments this to ack that broadcast was received 101 var _RDY_LOC = 3; // Worker increments this to ack that worker is up and running 102 var _LOCKTXT_LOC = 4; // Writer lock for the text buffer: 0=open, 1=closed 103 var _NUMTXT_LOC = 5; // Count of messages in text buffer 104 var _NEXT_LOC = 6; // First free location in the buffer 105 var _SLEEP_LOC = 7; // Used for sleeping 106 107 var _FIRST = 10; // First location of first message 108 109 var _ia = new Int32Array(new SharedArrayBuffer(65536)); 110 _ia[_NEXT_LOC] = _FIRST; 111 112 var _worker_prefix = 113 // BEGIN WORKER PREFIX 114 `if (typeof $262 === 'undefined') 115 $262 = {}; 116 $262.agent = (function (global) { 117 var ReflectApply = global.Reflect.apply; 118 var StringCharCodeAt = global.String.prototype.charCodeAt; 119 var { 120 add: Atomics_add, 121 compareExchange: Atomics_compareExchange, 122 load: Atomics_load, 123 store: Atomics_store, 124 wait: Atomics_wait, 125 } = global.Atomics; 126 127 var {getSharedArrayBuffer} = global; 128 129 var _finished = { done: false }; 130 131 var _ia = new Int32Array(getSharedArrayBuffer()); 132 var agent = { 133 receiveBroadcast(receiver) { 134 var k; 135 while (((k = Atomics_load(_ia, ${_MSG_LOC})) & 1) === 0) 136 ; 137 var received_sab = getSharedArrayBuffer(); 138 var received_id = Atomics_load(_ia, ${_ID_LOC}); 139 Atomics_add(_ia, ${_ACK_LOC}, 1); 140 while (Atomics_load(_ia, ${_MSG_LOC}) === k) 141 ; 142 receiver(received_sab, received_id); 143 waitForDone(_finished); 144 }, 145 146 report(msg) { 147 while (Atomics_compareExchange(_ia, ${_LOCKTXT_LOC}, 0, 1) === 1) 148 ; 149 msg = "" + msg; 150 var i = _ia[${_NEXT_LOC}]; 151 _ia[i++] = msg.length; 152 for ( let j=0 ; j < msg.length ; j++ ) 153 _ia[i++] = ReflectApply(StringCharCodeAt, msg, [j]); 154 _ia[${_NEXT_LOC}] = i; 155 Atomics_add(_ia, ${_NUMTXT_LOC}, 1); 156 Atomics_store(_ia, ${_LOCKTXT_LOC}, 0); 157 }, 158 159 sleep(s) { 160 Atomics_wait(_ia, ${_SLEEP_LOC}, 0, s); 161 }, 162 163 leaving() { 164 _finished.done = true; 165 }, 166 167 monotonicNow: global.monotonicNow, 168 }; 169 Atomics_add(_ia, ${_RDY_LOC}, 1); 170 return agent; 171 })(this);`; 172 // END WORKER PREFIX 173 174 var _numWorkers = 0; 175 var _numReports = 0; 176 var _reportPtr = _FIRST; 177 var { 178 add: Atomics_add, 179 load: Atomics_load, 180 store: Atomics_store, 181 wait: Atomics_wait, 182 } = Atomics; 183 var StringFromCharCode = global.String.fromCharCode; 184 185 return { 186 start(script) { 187 setSharedArrayBuffer(_ia.buffer); 188 var oldrdy = Atomics_load(_ia, _RDY_LOC); 189 evalInWorker(_worker_prefix + script); 190 while (Atomics_load(_ia, _RDY_LOC) === oldrdy) 191 ; 192 _numWorkers++; 193 }, 194 195 broadcast(sab, id) { 196 setSharedArrayBuffer(sab); 197 Atomics_store(_ia, _ID_LOC, id); 198 Atomics_store(_ia, _ACK_LOC, 0); 199 Atomics_add(_ia, _MSG_LOC, 1); 200 while (Atomics_load(_ia, _ACK_LOC) < _numWorkers) 201 ; 202 Atomics_add(_ia, _MSG_LOC, 1); 203 }, 204 205 getReport() { 206 if (_numReports === Atomics_load(_ia, _NUMTXT_LOC)) 207 return null; 208 var s = ""; 209 var i = _reportPtr; 210 var len = _ia[i++]; 211 for ( let j=0 ; j < len ; j++ ) 212 s += StringFromCharCode(_ia[i++]); 213 _reportPtr = i; 214 _numReports++; 215 return s; 216 }, 217 218 sleep(s) { 219 Atomics_wait(_ia, _SLEEP_LOC, 0, s); 220 }, 221 222 monotonicNow, 223 }; 224 })() 225 }; 226 })(this); 227 228 var $mozAsyncTestDone = false; 229 function $DONE(failure) { 230 // This function is generally called from within a Promise handler, so any 231 // exception thrown by this method will be swallowed and most likely 232 // ignored by the Promise machinery. 233 if ($mozAsyncTestDone) { 234 reportFailure("$DONE() already called"); 235 return; 236 } 237 $mozAsyncTestDone = true; 238 239 if (failure) 240 reportFailure(failure); 241 else 242 reportCompare(0, 0); 243 244 if (typeof jsTestDriverEnd === "function") { 245 gDelayTestDriverEnd = false; 246 jsTestDriverEnd(); 247 } 248 } 249 250 // Some tests in test262 leave promise rejections unhandled. 251 if ("ignoreUnhandledRejections" in this) { 252 ignoreUnhandledRejections(); 253 }