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