11cb0ef41Sopenharmony_ci'use strict'; 21cb0ef41Sopenharmony_ciconst common = require('../common'); 31cb0ef41Sopenharmony_ci 41cb0ef41Sopenharmony_cicommon.skipIfInspectorDisabled(); 51cb0ef41Sopenharmony_ci 61cb0ef41Sopenharmony_ciconst assert = require('assert'); 71cb0ef41Sopenharmony_ciconst { Session } = require('inspector'); 81cb0ef41Sopenharmony_ciconst path = require('path'); 91cb0ef41Sopenharmony_ciconst { pathToFileURL } = require('url'); 101cb0ef41Sopenharmony_ciconst { isMainThread, parentPort, Worker, workerData } = 111cb0ef41Sopenharmony_ci require('worker_threads'); 121cb0ef41Sopenharmony_ci 131cb0ef41Sopenharmony_ciif (!workerData) { 141cb0ef41Sopenharmony_ci common.skipIfWorker(); 151cb0ef41Sopenharmony_ci} 161cb0ef41Sopenharmony_ci 171cb0ef41Sopenharmony_cifunction toDebug() { 181cb0ef41Sopenharmony_ci let a = 1; 191cb0ef41Sopenharmony_ci a = a + 1; 201cb0ef41Sopenharmony_ci return a * 200; 211cb0ef41Sopenharmony_ci} 221cb0ef41Sopenharmony_ci 231cb0ef41Sopenharmony_ciconst messagesSent = []; 241cb0ef41Sopenharmony_ci 251cb0ef41Sopenharmony_ciasync function post(session, method, params) { 261cb0ef41Sopenharmony_ci return new Promise((resolve, reject) => { 271cb0ef41Sopenharmony_ci session.post(method, params, (error, success) => { 281cb0ef41Sopenharmony_ci messagesSent.push(method); 291cb0ef41Sopenharmony_ci if (error) { 301cb0ef41Sopenharmony_ci process._rawDebug(`Message ${method} produced an error`); 311cb0ef41Sopenharmony_ci reject(error); 321cb0ef41Sopenharmony_ci } else { 331cb0ef41Sopenharmony_ci process._rawDebug(`Message ${method} was sent`); 341cb0ef41Sopenharmony_ci resolve(success); 351cb0ef41Sopenharmony_ci } 361cb0ef41Sopenharmony_ci }); 371cb0ef41Sopenharmony_ci }); 381cb0ef41Sopenharmony_ci} 391cb0ef41Sopenharmony_ci 401cb0ef41Sopenharmony_ciasync function waitForNotification(session, notification) { 411cb0ef41Sopenharmony_ci return new Promise((resolve) => session.once(notification, resolve)); 421cb0ef41Sopenharmony_ci} 431cb0ef41Sopenharmony_ci 441cb0ef41Sopenharmony_cifunction startWorker(skipChild, sharedBuffer) { 451cb0ef41Sopenharmony_ci return new Promise((resolve) => { 461cb0ef41Sopenharmony_ci const worker = new Worker(__filename, { 471cb0ef41Sopenharmony_ci workerData: { skipChild, sharedBuffer } 481cb0ef41Sopenharmony_ci }); 491cb0ef41Sopenharmony_ci worker.on('error', (e) => { 501cb0ef41Sopenharmony_ci console.error(e); 511cb0ef41Sopenharmony_ci throw e; 521cb0ef41Sopenharmony_ci }); 531cb0ef41Sopenharmony_ci // Add 2 promises to the worker, one resolved when a message with a 541cb0ef41Sopenharmony_ci // .doConsoleLog property is received and one resolved when a .messagesSent 551cb0ef41Sopenharmony_ci // property is received. 561cb0ef41Sopenharmony_ci let resolveConsoleRequest; 571cb0ef41Sopenharmony_ci let resolveMessagesSent; 581cb0ef41Sopenharmony_ci worker.onConsoleRequest = 591cb0ef41Sopenharmony_ci new Promise((resolve) => resolveConsoleRequest = resolve); 601cb0ef41Sopenharmony_ci worker.onMessagesSent = 611cb0ef41Sopenharmony_ci new Promise((resolve) => resolveMessagesSent = resolve); 621cb0ef41Sopenharmony_ci worker.on('message', (m) => { 631cb0ef41Sopenharmony_ci resolve(worker); 641cb0ef41Sopenharmony_ci if (m.doConsoleLog) resolveConsoleRequest(); 651cb0ef41Sopenharmony_ci if (m.messagesSent) resolveMessagesSent(m.messagesSent); 661cb0ef41Sopenharmony_ci }); 671cb0ef41Sopenharmony_ci }); 681cb0ef41Sopenharmony_ci} 691cb0ef41Sopenharmony_ci 701cb0ef41Sopenharmony_cifunction doConsoleLog(arrayBuffer) { 711cb0ef41Sopenharmony_ci console.log('Message for a test'); 721cb0ef41Sopenharmony_ci arrayBuffer[0] = 128; 731cb0ef41Sopenharmony_ci} 741cb0ef41Sopenharmony_ci 751cb0ef41Sopenharmony_ci// This tests that inspector callbacks are called in a microtask 761cb0ef41Sopenharmony_ci// and do not interrupt the main code. Interrupting the code flow 771cb0ef41Sopenharmony_ci// can lead to unexpected behaviors. 781cb0ef41Sopenharmony_ciasync function ensureListenerDoesNotInterrupt(session) { 791cb0ef41Sopenharmony_ci // Make sure that the following code is not affected by the fact that it may 801cb0ef41Sopenharmony_ci // run inside an inspector message callback, during which other inspector 811cb0ef41Sopenharmony_ci // message callbacks (such as the one triggered by doConsoleLog()) would 821cb0ef41Sopenharmony_ci // not be processed. 831cb0ef41Sopenharmony_ci await new Promise(setImmediate); 841cb0ef41Sopenharmony_ci 851cb0ef41Sopenharmony_ci const currentTime = Date.now(); 861cb0ef41Sopenharmony_ci let consoleLogHappened = false; 871cb0ef41Sopenharmony_ci session.once('Runtime.consoleAPICalled', 881cb0ef41Sopenharmony_ci () => { consoleLogHappened = true; }); 891cb0ef41Sopenharmony_ci const buf = new Uint8Array(workerData.sharedBuffer); 901cb0ef41Sopenharmony_ci parentPort.postMessage({ doConsoleLog: true }); 911cb0ef41Sopenharmony_ci while (buf[0] === 1) { 921cb0ef41Sopenharmony_ci // Making sure the console.log was executed 931cb0ef41Sopenharmony_ci } 941cb0ef41Sopenharmony_ci while ((Date.now() - currentTime) < 50) { 951cb0ef41Sopenharmony_ci // Spin wait for 50ms, assume that was enough to get inspector message 961cb0ef41Sopenharmony_ci } 971cb0ef41Sopenharmony_ci assert.strictEqual(consoleLogHappened, false); 981cb0ef41Sopenharmony_ci await new Promise(queueMicrotask); 991cb0ef41Sopenharmony_ci assert.strictEqual(consoleLogHappened, true); 1001cb0ef41Sopenharmony_ci} 1011cb0ef41Sopenharmony_ci 1021cb0ef41Sopenharmony_ciasync function main() { 1031cb0ef41Sopenharmony_ci assert.throws( 1041cb0ef41Sopenharmony_ci () => { 1051cb0ef41Sopenharmony_ci const session = new Session(); 1061cb0ef41Sopenharmony_ci session.connectToMainThread(); 1071cb0ef41Sopenharmony_ci }, 1081cb0ef41Sopenharmony_ci { 1091cb0ef41Sopenharmony_ci code: 'ERR_INSPECTOR_NOT_WORKER', 1101cb0ef41Sopenharmony_ci name: 'Error', 1111cb0ef41Sopenharmony_ci message: 'Current thread is not a worker' 1121cb0ef41Sopenharmony_ci } 1131cb0ef41Sopenharmony_ci ); 1141cb0ef41Sopenharmony_ci const sharedBuffer = new SharedArrayBuffer(1); 1151cb0ef41Sopenharmony_ci const arrayBuffer = new Uint8Array(sharedBuffer); 1161cb0ef41Sopenharmony_ci arrayBuffer[0] = 1; 1171cb0ef41Sopenharmony_ci const worker = await startWorker(false, sharedBuffer); 1181cb0ef41Sopenharmony_ci worker.onConsoleRequest.then(doConsoleLog.bind(null, arrayBuffer)); 1191cb0ef41Sopenharmony_ci assert.strictEqual(toDebug(), 400); 1201cb0ef41Sopenharmony_ci assert.deepStrictEqual(await worker.onMessagesSent, [ 1211cb0ef41Sopenharmony_ci 'Debugger.enable', 1221cb0ef41Sopenharmony_ci 'Runtime.enable', 1231cb0ef41Sopenharmony_ci 'Debugger.setBreakpointByUrl', 1241cb0ef41Sopenharmony_ci 'Debugger.evaluateOnCallFrame', 1251cb0ef41Sopenharmony_ci 'Debugger.resume', 1261cb0ef41Sopenharmony_ci ]); 1271cb0ef41Sopenharmony_ci} 1281cb0ef41Sopenharmony_ci 1291cb0ef41Sopenharmony_ciasync function childMain() { 1301cb0ef41Sopenharmony_ci // Ensures the worker does not terminate too soon 1311cb0ef41Sopenharmony_ci parentPort.on('message', () => { }); 1321cb0ef41Sopenharmony_ci await (await startWorker(true)).onMessagesSent; 1331cb0ef41Sopenharmony_ci const session = new Session(); 1341cb0ef41Sopenharmony_ci session.connectToMainThread(); 1351cb0ef41Sopenharmony_ci assert.throws( 1361cb0ef41Sopenharmony_ci () => { 1371cb0ef41Sopenharmony_ci session.connectToMainThread(); 1381cb0ef41Sopenharmony_ci }, 1391cb0ef41Sopenharmony_ci { 1401cb0ef41Sopenharmony_ci code: 'ERR_INSPECTOR_ALREADY_CONNECTED', 1411cb0ef41Sopenharmony_ci name: 'Error', 1421cb0ef41Sopenharmony_ci message: 'The inspector session is already connected' 1431cb0ef41Sopenharmony_ci } 1441cb0ef41Sopenharmony_ci ); 1451cb0ef41Sopenharmony_ci await post(session, 'Debugger.enable'); 1461cb0ef41Sopenharmony_ci await post(session, 'Runtime.enable'); 1471cb0ef41Sopenharmony_ci await post(session, 'Debugger.setBreakpointByUrl', { 1481cb0ef41Sopenharmony_ci 'lineNumber': 18, 1491cb0ef41Sopenharmony_ci 'url': pathToFileURL(path.resolve(__dirname, __filename)).toString(), 1501cb0ef41Sopenharmony_ci 'columnNumber': 0, 1511cb0ef41Sopenharmony_ci 'condition': '' 1521cb0ef41Sopenharmony_ci }); 1531cb0ef41Sopenharmony_ci const pausedPromise = waitForNotification(session, 'Debugger.paused'); 1541cb0ef41Sopenharmony_ci parentPort.postMessage('Ready'); 1551cb0ef41Sopenharmony_ci const callFrameId = (await pausedPromise).params.callFrames[0].callFrameId; 1561cb0ef41Sopenharmony_ci 1571cb0ef41Sopenharmony_ci // Delay to ensure main thread is truly suspended 1581cb0ef41Sopenharmony_ci await new Promise((resolve) => setTimeout(resolve, 50)); 1591cb0ef41Sopenharmony_ci 1601cb0ef41Sopenharmony_ci const { result: { value } } = 1611cb0ef41Sopenharmony_ci await post(session, 1621cb0ef41Sopenharmony_ci 'Debugger.evaluateOnCallFrame', 1631cb0ef41Sopenharmony_ci { callFrameId, expression: 'a * 100' }); 1641cb0ef41Sopenharmony_ci assert.strictEqual(value, 100); 1651cb0ef41Sopenharmony_ci await post(session, 'Debugger.resume'); 1661cb0ef41Sopenharmony_ci await ensureListenerDoesNotInterrupt(session); 1671cb0ef41Sopenharmony_ci parentPort.postMessage({ messagesSent }); 1681cb0ef41Sopenharmony_ci parentPort.close(); 1691cb0ef41Sopenharmony_ci console.log('Worker is done'); 1701cb0ef41Sopenharmony_ci} 1711cb0ef41Sopenharmony_ci 1721cb0ef41Sopenharmony_ciasync function skipChildMain() { 1731cb0ef41Sopenharmony_ci // Ensures the worker does not terminate too soon 1741cb0ef41Sopenharmony_ci parentPort.on('message', () => { }); 1751cb0ef41Sopenharmony_ci 1761cb0ef41Sopenharmony_ci const session = new Session(); 1771cb0ef41Sopenharmony_ci session.connectToMainThread(); 1781cb0ef41Sopenharmony_ci const notifications = []; 1791cb0ef41Sopenharmony_ci session.on('NodeWorker.attachedToWorker', (n) => notifications.push(n)); 1801cb0ef41Sopenharmony_ci await post(session, 'NodeWorker.enable', { waitForDebuggerOnStart: false }); 1811cb0ef41Sopenharmony_ci // 2 notifications mean there are 2 workers so we are connected to a main 1821cb0ef41Sopenharmony_ci // thread 1831cb0ef41Sopenharmony_ci assert.strictEqual(notifications.length, 2); 1841cb0ef41Sopenharmony_ci parentPort.postMessage('Ready'); 1851cb0ef41Sopenharmony_ci parentPort.postMessage({ messagesSent }); 1861cb0ef41Sopenharmony_ci parentPort.close(); 1871cb0ef41Sopenharmony_ci console.log('Skip child is done'); 1881cb0ef41Sopenharmony_ci} 1891cb0ef41Sopenharmony_ci 1901cb0ef41Sopenharmony_ciif (isMainThread) { 1911cb0ef41Sopenharmony_ci main().then(common.mustCall()); 1921cb0ef41Sopenharmony_ci} else if (workerData.skipChild) { 1931cb0ef41Sopenharmony_ci skipChildMain().then(common.mustCall()); 1941cb0ef41Sopenharmony_ci} else { 1951cb0ef41Sopenharmony_ci childMain().then(common.mustCall()); 1961cb0ef41Sopenharmony_ci} 197