DbgController.js 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. var TRACE_NONE = 0x0000;
  6. var TRACE_COMMANDS = 0x001;
  7. var TRACE_DIAG_OUTPUT = 0x002;
  8. var TRACE_INTERNAL_FUNCTIONS = 0x004;
  9. var TRACE_DEBUG_EVENTS = 0x008;
  10. var TRACE_ALL = TRACE_COMMANDS | TRACE_DIAG_OUTPUT | TRACE_INTERNAL_FUNCTIONS | TRACE_DEBUG_EVENTS;
  11. // Have all JsDiag* functions installed on it by Debugger.cpp
  12. var hostDebugObject = {};
  13. var controllerObj = (function () {
  14. var _commandList = [];
  15. var _commandCompletions = [];
  16. var _wasResumed = false;
  17. var _currentStackFrameIndex = 0;
  18. var _trace = TRACE_NONE;
  19. var _eventLog = [];
  20. var _baseline = undefined;
  21. var _exceptionCommands = undefined;
  22. var _onasyncbreakCommands = undefined;
  23. var _inspectMaxStringLength = 16;
  24. function internalPrint(str) {
  25. WScript.Echo(str);
  26. }
  27. function isTracing(traceFlag) {
  28. return _trace & traceFlag;
  29. }
  30. function internalTrace(traceFlag, ...varArgs) {
  31. if (isTracing(traceFlag)) {
  32. var str = "";
  33. varArgs.map(function (element) {
  34. str += (typeof element != "string") ? JSON.stringify(element, undefined, " ") : element;
  35. });
  36. internalPrint(str);
  37. }
  38. }
  39. function printError(str) {
  40. internalPrint("Error: " + str);
  41. }
  42. function callHostFunction(fn) {
  43. var result = fn.apply(undefined, [].slice.call(arguments, 1));
  44. var obj = {};
  45. obj[fn.name] = result;
  46. internalTrace(TRACE_DIAG_OUTPUT, obj);
  47. return result;
  48. }
  49. filterLog = (function () {
  50. var parentFilter = { "this": 1, "locals": 1, "globals": 1 };
  51. var filter = {};
  52. // Discard all known globals to reduce baseline noise.
  53. [
  54. "#__proto__",
  55. "Array",
  56. "ArrayBuffer",
  57. "Atomics",
  58. "Boolean",
  59. "chWriteTraceEvent",
  60. "CollectGarbage",
  61. "console",
  62. "DataView",
  63. "Date",
  64. "decodeURI",
  65. "decodeURIComponent",
  66. "encodeURI",
  67. "encodeURIComponent",
  68. "Error",
  69. "escape",
  70. "eval",
  71. "EvalError",
  72. "Float32Array",
  73. "Float64Array",
  74. "Function",
  75. "Infinity",
  76. "Int16Array",
  77. "Int32Array",
  78. "Int8Array",
  79. "Intl",
  80. "isFinite",
  81. "isNaN",
  82. "JSON",
  83. "Map",
  84. "Math",
  85. "NaN",
  86. "Number",
  87. "Object",
  88. "parseFloat",
  89. "parseInt",
  90. "print",
  91. "Promise",
  92. "Proxy",
  93. "RangeError",
  94. "read",
  95. "readbuffer",
  96. "readline",
  97. "ReferenceError",
  98. "Reflect",
  99. "RegExp",
  100. "Set",
  101. "SharedArrayBuffer",
  102. "String",
  103. "Symbol",
  104. "SyntaxError",
  105. "TypeError",
  106. "Uint16Array",
  107. "Uint32Array",
  108. "Uint8Array",
  109. "Uint8ClampedArray",
  110. "undefined",
  111. "unescape",
  112. "URIError",
  113. "WeakMap",
  114. "WeakSet",
  115. "WebAssembly",
  116. "WScript",
  117. ].forEach(function (name) {
  118. filter[name] = 1;
  119. });
  120. function filterInternal(parentName, obj, depth) {
  121. for (var p in obj) {
  122. if (parentFilter[parentName] == 1 && filter[p] == 1) {
  123. delete obj[p];
  124. } else if (typeof obj[p] == "object") {
  125. filterInternal(p.trim(), obj[p], depth + 1);
  126. }
  127. }
  128. }
  129. return function (obj) {
  130. try {
  131. filterInternal("this"/*filter root*/, obj, 0);
  132. } catch (ex) {
  133. printError("exception during filter: " + ex);
  134. }
  135. };
  136. })();
  137. function recordEvent(json) {
  138. filterLog(json);
  139. _eventLog.push(json);
  140. }
  141. // remove path from file name and just have the filename with extension
  142. function filterFileName(fileName) {
  143. try {
  144. var index = fileName.lastIndexOf("\\");
  145. if (index >= 0) {
  146. return fileName.substring(index + 1);
  147. }
  148. } catch (ex) { }
  149. return "";
  150. }
  151. var bpManager = (function () {
  152. var _bpMap = [];
  153. var _locBpId = -1;
  154. function getBpFromName(name) {
  155. for (var i in _bpMap) {
  156. if (_bpMap[i].name === name) {
  157. return _bpMap[i];
  158. }
  159. }
  160. printError("Breakpoint named '" + name + "' was not found");
  161. }
  162. function internalSetBp(name, scriptId, line, column, execStr) {
  163. var bpObject = callHostFunction(hostDebugObject.JsDiagSetBreakpoint, scriptId, line, column);
  164. return {
  165. id: bpObject.breakpointId,
  166. scriptId: scriptId,
  167. name: name,
  168. line: bpObject.line,
  169. column: bpObject.column,
  170. execStr: execStr,
  171. enabled: true
  172. };
  173. }
  174. function addBpObject(bpObj) {
  175. internalTrace(TRACE_INTERNAL_FUNCTIONS, "addBpObject: ", bpObj);
  176. _bpMap[bpObj.id] = bpObj;
  177. }
  178. return {
  179. setBreakpoint: function (name, scriptId, line, column, execStr) {
  180. var bpObj = internalSetBp(name, scriptId, line, column, execStr);
  181. addBpObject(bpObj);
  182. },
  183. setLocationBreakpoint: function (name, scriptId, line, column, execStr) {
  184. var bpObj = {
  185. id: _locBpId--,
  186. scriptId: scriptId,
  187. name: name,
  188. line: line,
  189. column: column,
  190. execStr: execStr,
  191. enabled: false
  192. }
  193. addBpObject(bpObj);
  194. },
  195. enableBreakpoint: function (name) {
  196. var bpObj = getBpFromName(name);
  197. if (bpObj) {
  198. delete _bpMap[bpObj.id];
  199. internalTrace(TRACE_INTERNAL_FUNCTIONS, "enableBreakpoint: ", name, " bpObj: ", bpObj);
  200. bpObj = internalSetBp(bpObj.name, bpObj.scriptId, bpObj.line, bpObj.column, bpObj.execStr);
  201. addBpObject(bpObj);
  202. }
  203. },
  204. deleteBreakpoint: function (name) {
  205. var bpObj = getBpFromName(name);
  206. if (bpObj && bpObj.enabled) {
  207. internalTrace(TRACE_INTERNAL_FUNCTIONS, "deleteBreakpoint: ", name, " bpObj: ", bpObj);
  208. callHostFunction(hostDebugObject.JsDiagRemoveBreakpoint, bpObj.id);
  209. delete _bpMap[bpObj.id];
  210. }
  211. },
  212. disableBreakpoint: function (name) {
  213. var bpObj = getBpFromName(name);
  214. if (bpObj && bpObj.enabled) {
  215. internalTrace(TRACE_INTERNAL_FUNCTIONS, "disableBreakpoint: ", name, " bpObj: ", bpObj);
  216. callHostFunction(hostDebugObject.JsDiagRemoveBreakpoint, bpObj.id);
  217. _bpMap[bpObj.id].enabled = false;
  218. }
  219. },
  220. getExecStr: function (id) {
  221. for (var i in _bpMap) {
  222. if (_bpMap[i].id === id) {
  223. return _bpMap[i].execStr;
  224. }
  225. }
  226. printError("Breakpoint '" + id + "' was not found");
  227. },
  228. setExecStr: function (id, newExecStr) {
  229. for (var i in _bpMap) {
  230. if (_bpMap[i].id === id) {
  231. _bpMap[i].execStr = newExecStr;
  232. }
  233. }
  234. },
  235. clearAllBreakpoints: function () {
  236. _bpMap = [];
  237. _locBpId = -1;
  238. }
  239. }
  240. })();
  241. function addSourceFile(text, srcId) {
  242. try {
  243. // Split the text into lines. Note this doesn't take into account block comments, but that's probably okay.
  244. var lines = text.split(/\n/);
  245. // /**bp <-- a breakpoint
  246. // /**loc <-- a named source location used for enabling bp at later stage
  247. // /**exception <-- set exception handling, catch all or only uncaught exception
  248. // /**onasyncbreak <-- set what happens on async break
  249. var bpStartToken = "/**";
  250. var bpStartStrings = ["bp", "loc", "exception", "onasyncbreak"];
  251. var bpEnd = "**/";
  252. // Iterate through each source line, setting any breakpoints.
  253. for (var i = 0; i < lines.length; ++i) {
  254. var line = lines[i];
  255. for (var startString in bpStartStrings) {
  256. var bpStart = bpStartToken + bpStartStrings[startString];
  257. var isLocationBreakpoint = (bpStart.indexOf("loc") != -1);
  258. var isExceptionBreakpoint = (bpStart.indexOf("exception") != -1);
  259. var isOnAsyncBreak = (bpStart.indexOf("onasyncbreak") != -1);
  260. var startIdx = -1;
  261. while ((startIdx = line.indexOf(bpStart, startIdx + 1)) != -1) {
  262. var endIdx;
  263. var currLine = i;
  264. var bpLine = i;
  265. var currBpLineString = "";
  266. // Gather up any lines within the breakpoint comment.
  267. do {
  268. var currentStartIdx = 0;
  269. if (currLine == i) {
  270. currentStartIdx = startIdx;
  271. }
  272. currBpLineString += lines[currLine++];
  273. endIdx = currBpLineString.indexOf(bpEnd, currentStartIdx);
  274. } while (endIdx == -1 && currLine < lines.length && lines[currLine].indexOf(bpStartToken) == -1);
  275. // Move the line cursor forward, allowing the current line to be re-checked
  276. i = currLine - 1;
  277. // Do some checking
  278. if (endIdx == -1) {
  279. printError("Unterminated breakpoint expression");
  280. return;
  281. }
  282. var bpStrStartIdx = startIdx + bpStart.length;
  283. var bpStr = currBpLineString.substring(bpStrStartIdx, endIdx);
  284. var bpFullStr = currBpLineString.substring(startIdx, endIdx);
  285. // Quick check to make sure the breakpoint is not within a
  286. // quoted string (such as an eval). If it is within an eval, the
  287. // eval will cause a separate call to have its breakpoints parsed.
  288. // This check can be defeated, but it should cover the useful scenarios.
  289. var quoteCount = 0;
  290. var escapeCount = 0;
  291. for (var j = 0; j < bpStrStartIdx; ++j) {
  292. switch (currBpLineString[j]) {
  293. case '\\':
  294. escapeCount++;
  295. continue;
  296. case '"':
  297. case '\'':
  298. if (escapeCount % 2 == 0)
  299. quoteCount++;
  300. /* fall through */
  301. default:
  302. escapeCount = 0;
  303. }
  304. }
  305. if (quoteCount % 2 == 1) {
  306. // The breakpoint was in a quoted string.
  307. continue;
  308. }
  309. // Only support strings like:
  310. // /**bp**/
  311. // /**bp(name)**/
  312. // /**bp(columnoffset)**/ takes an integer
  313. // /**bp:locals();stack()**/
  314. // /**bp(name):locals();stack()**/
  315. // /**loc(name)**/
  316. // /**loc(name):locals();stack()**/
  317. //
  318. // Parse the breakpoint name (if it exists)
  319. var bpName = undefined;
  320. var bpColumnOffset = 0;
  321. var bpExecStr = undefined;
  322. var parseIdx = 0;
  323. if (bpStr[parseIdx] == '(') {
  324. // The name and offset is overloaded with the same parameter.
  325. // if that is int (determined by parseInt), then it is column offset otherwise left as name.
  326. bpName = bpStr.match(/\(([\w,]+?)\)/)[1];
  327. parseIdx = bpName.length + 2;
  328. bpColumnOffset = parseInt(bpName);
  329. if (isNaN(bpColumnOffset)) {
  330. bpColumnOffset = 0;
  331. } else {
  332. bpName = undefined;
  333. if (bpColumnOffset > line.length) {
  334. bpColumnOffset = line.length - 1;
  335. } else if (bpColumnOffset < 0) {
  336. bpColumnOffset = 0;
  337. }
  338. }
  339. } else if (isLocationBreakpoint) {
  340. printError("'loc' sites require a label, for example /**loc(myFunc)**/");
  341. return;
  342. }
  343. // Process the exception label:
  344. // exception(none)
  345. // exception(uncaught)
  346. // exception(all)
  347. if (isExceptionBreakpoint) {
  348. var exceptionAttributes = -1;
  349. if (bpName !== undefined) {
  350. if (bpName == "none") {
  351. exceptionAttributes = 0; // JsDiagBreakOnExceptionAttributeNone
  352. } else if (bpName == "uncaught") {
  353. exceptionAttributes = 0x1; // JsDiagBreakOnExceptionAttributeUncaught
  354. } else if (bpName == "firstchance") {
  355. exceptionAttributes = 0x2; // JsDiagBreakOnExceptionAttributeFirstChance
  356. } else if (bpName == "all") {
  357. exceptionAttributes = 0x1 | 0x2; // JsDiagBreakOnExceptionAttributeUncaught | JsDiagBreakOnExceptionAttributeFirstChance
  358. }
  359. } else {
  360. // throw "No exception type specified";
  361. }
  362. callHostFunction(hostDebugObject.JsDiagSetBreakOnException, exceptionAttributes);
  363. }
  364. // Parse the breakpoint execution string
  365. if (bpStr[parseIdx] == ':') {
  366. bpExecStr = bpStr.substring(parseIdx + 1);
  367. } else if (parseIdx != bpStr.length) {
  368. printError("Invalid breakpoint string: " + bpStr);
  369. return;
  370. }
  371. // Insert the breakpoint.
  372. if (isExceptionBreakpoint) {
  373. if (_exceptionCommands != undefined) {
  374. printError("More than one 'exception' annotation found");
  375. return;
  376. }
  377. _exceptionCommands = bpExecStr;
  378. }
  379. if (isOnAsyncBreak) {
  380. if (_onasyncbreakCommands != undefined) {
  381. printError("More than one 'onasyncbreak' annotation found");
  382. return;
  383. }
  384. _onasyncbreakCommands = bpExecStr;
  385. }
  386. if (!isExceptionBreakpoint && !isOnAsyncBreak) {
  387. if (!isLocationBreakpoint) {
  388. bpManager.setBreakpoint(bpName, srcId, bpLine, bpColumnOffset, bpExecStr);
  389. } else {
  390. bpManager.setLocationBreakpoint(bpName, srcId, bpLine, bpColumnOffset, bpExecStr);
  391. }
  392. }
  393. }
  394. }
  395. }
  396. } catch (ex) {
  397. printError(ex);
  398. }
  399. }
  400. function handleBreakpoint(id) {
  401. internalTrace(TRACE_INTERNAL_FUNCTIONS, "handleBreakpoint id: ", id)
  402. _wasResumed = false;
  403. if (id != -1) {
  404. try {
  405. var execStr = "";
  406. if (id === "exception") {
  407. execStr = _exceptionCommands;
  408. if (execStr && execStr.toString().search("removeExpr()") != -1) {
  409. _exceptionCommands = undefined;
  410. }
  411. } else if (id === "asyncbreak") {
  412. execStr = _onasyncbreakCommands;
  413. if (execStr && execStr.toString().search("removeExpr()") != -1) {
  414. _onasyncbreakCommands = undefined;
  415. }
  416. } else if (id === "debuggerStatement") {
  417. execStr = "dumpBreak();locals();stack();"
  418. } else {
  419. // Retrieve this breakpoint's execution string
  420. execStr = bpManager.getExecStr(id);
  421. if (execStr.toString().search("removeExpr()") != -1) {
  422. // A hack to remove entire expression, so that it will not run again.
  423. bpManager.setExecStr(id, null);
  424. }
  425. }
  426. internalTrace(TRACE_INTERNAL_FUNCTIONS, "handleBreakpoint execStr: ", execStr)
  427. if (execStr != null) {
  428. eval(execStr);
  429. }
  430. } catch (ex) {
  431. printError(ex);
  432. }
  433. }
  434. internalTrace(TRACE_INTERNAL_FUNCTIONS, "_commandList length: ", _commandList.length, " _wasResumed: ", _wasResumed);
  435. // Continue processing the command list.
  436. while (_commandList.length > 0 && !_wasResumed) {
  437. var cmd = _commandList.shift();
  438. internalTrace(TRACE_INTERNAL_FUNCTIONS, "cmd: ", cmd);
  439. var completion = cmd.fn.apply(this, cmd.args);
  440. if (typeof completion === "function") {
  441. _commandCompletions.push(completion);
  442. }
  443. }
  444. while (_commandCompletions.length > 0) {
  445. var completion = _commandCompletions.shift();
  446. completion();
  447. }
  448. if (!_wasResumed) {
  449. _currentStackFrameIndex = 0;
  450. _wasResumed = true;
  451. }
  452. }
  453. function GetObjectDisplay(obj) {
  454. var objectDisplay = ("className" in obj) ? obj["className"] : obj["type"];
  455. var value = ("value" in obj) ? obj["value"] : obj["display"];
  456. if (value && value.length > _inspectMaxStringLength) {
  457. objectDisplay += " <large string>";
  458. } else {
  459. objectDisplay += " " + value;
  460. }
  461. return objectDisplay;
  462. }
  463. var stringToArrayBuffer = function stringToArrayBuffer(str) {
  464. var arr = [];
  465. for (var i = 0, len = str.length; i < len; i++) {
  466. arr[i] = str.charCodeAt(i) & 0xFF;
  467. }
  468. return new Uint8Array(arr).buffer;
  469. }
  470. function GetChild(obj, level) {
  471. function GetChildrens(obj, level) {
  472. var retArray = {};
  473. for (var i = 0; i < obj.length; ++i) {
  474. var propName = (obj[i].name == "__proto__") ? "#__proto__" : obj[i].name;
  475. retArray[propName] = GetChild(obj[i], level);
  476. }
  477. return retArray;
  478. }
  479. var retValue = {};
  480. if ("handle" in obj) {
  481. if (level >= 0) {
  482. var childProps = callHostFunction(hostDebugObject.JsDiagGetProperties, obj["handle"], 0, 1000);
  483. var properties = childProps["properties"];
  484. var debuggerOnlyProperties = childProps["debuggerOnlyProperties"];
  485. Array.prototype.push.apply(properties, debuggerOnlyProperties);
  486. if (properties.length > 0) {
  487. retValue = GetChildrens(properties, level - 1);
  488. } else {
  489. retValue = GetObjectDisplay(obj);
  490. }
  491. } else {
  492. retValue = GetObjectDisplay(obj);
  493. }
  494. delete obj["handle"];
  495. }
  496. if ("propertyAttributes" in obj) {
  497. delete obj["propertyAttributes"];
  498. }
  499. return retValue;
  500. }
  501. function compareWithBaseline() {
  502. function compareObjects(a, b, obj_namespace) {
  503. var objectsEqual = true;
  504. // This is a basic object comparison function, primarily to be used for JSON data.
  505. // It doesn't handle cyclical objects.
  506. function fail(step, message) {
  507. if (message == undefined) {
  508. message = "diff baselines for details";
  509. }
  510. printError("Step " + step + "; on: " + obj_namespace + ": " + message);
  511. objectsEqual = false;
  512. }
  513. function failNonObj(step, a, b, message) {
  514. if (message == undefined) {
  515. message = "";
  516. }
  517. printError("Step " + step + "; Local Diff on: " + obj_namespace + ": " + message);
  518. printError("Value 1:" + JSON.stringify(a));
  519. printError("Value 2:" + JSON.stringify(b));
  520. print("");
  521. objectsEqual = false;
  522. }
  523. // (1) Check strict equality.
  524. if (a === b)
  525. return true;
  526. // (2) non-Objects must have passed the strict equality comparison in (1)
  527. if ((typeof a != "object") || (typeof b != "object")) {
  528. failNonObj(2, a, b);
  529. return false;
  530. }
  531. // (3) check all properties
  532. for (var p in a) {
  533. // (4) check the property
  534. if (a[p] === b[p])
  535. continue;
  536. // (5) non-Objects must have passed the strict equality comparison in (4)
  537. if (typeof (a[p]) != "object") {
  538. failNonObj(5, a[p], b[p], "Property " + p);
  539. continue;
  540. }
  541. // (6) recursively check objects or arrays
  542. if (!compareObjects(a[p], b[p], obj_namespace + "." + p)) {
  543. // Don't need to report error message as it'll be reported inside nested call
  544. objectsEqual = false;
  545. continue;
  546. }
  547. }
  548. // (7) check any properties not in the previous enumeration
  549. var hasOwnProperty = Object.prototype.hasOwnProperty;
  550. for (var p in b) {
  551. if (hasOwnProperty.call(b, p) && !hasOwnProperty.call(a, p)) {
  552. fail(7, "Property missing: " + p + ", value: " + JSON.stringify(b[p]));
  553. continue;
  554. }
  555. }
  556. return objectsEqual;
  557. }
  558. var PASSED = 0;
  559. var FAILED = 1;
  560. if (_baseline == undefined) {
  561. return PASSED;
  562. }
  563. try {
  564. if (compareObjects(_baseline, _eventLog, "baseline")) {
  565. return PASSED;
  566. }
  567. }
  568. catch (ex) {
  569. printError("EXCEPTION: " + ex);
  570. }
  571. printError("TEST FAILED");
  572. return FAILED;
  573. }
  574. return {
  575. pushCommand: function pushCommand(fn, args) {
  576. _commandList.push({
  577. fn: fn,
  578. args: args
  579. });
  580. },
  581. debuggerCommands: {
  582. log: function (str) {
  583. internalPrint("LOG: " + str);
  584. },
  585. logJson: function (str) {
  586. recordEvent({ log: str });
  587. },
  588. resume: function (kind) {
  589. if (_wasResumed) {
  590. printError("Breakpoint resumed twice");
  591. } else {
  592. var stepType = -1;
  593. if (kind == "step_into") {
  594. stepType = 0;
  595. } else if (kind == "step_out") {
  596. stepType = 1;
  597. } else if (kind == "step_over") {
  598. stepType = 2;
  599. } else if (kind == "step_document") {
  600. stepType = 0;
  601. }
  602. if (stepType != -1) {
  603. callHostFunction(hostDebugObject.JsDiagSetStepType, stepType);
  604. } else if (kind != "continue") {
  605. throw new Error("Unhandled step type - " + kind);
  606. }
  607. _wasResumed = true;
  608. _currentStackFrameIndex = 0;
  609. }
  610. },
  611. locals: function (expandLevel, flags) {
  612. if (expandLevel == undefined || expandLevel < 0) {
  613. expandLevel = 0;
  614. }
  615. var stackProperties = callHostFunction(hostDebugObject.JsDiagGetStackProperties, _currentStackFrameIndex);
  616. if (expandLevel >= 0) {
  617. var localsJSON = {};
  618. ["thisObject", "exception", "arguments", "returnValue"].forEach(function (name) {
  619. if (name in stackProperties) {
  620. var stackObject = stackProperties[name];
  621. localsJSON[stackObject.name] = GetChild(stackObject, expandLevel - 1);
  622. }
  623. });
  624. ["functionCallsReturn", "locals"].forEach(function (name) {
  625. if (name in stackProperties) {
  626. var stackObject = stackProperties[name];
  627. if (stackObject.length > 0) {
  628. localsJSON[name] = {};
  629. for (var i = 0; i < stackObject.length; ++i) {
  630. localsJSON[name][stackObject[i].name] = GetChild(stackObject[i], expandLevel - 1);
  631. }
  632. }
  633. }
  634. });
  635. if ("scopes" in stackProperties) {
  636. var scopesArray = stackProperties["scopes"];
  637. for (var i = 0; i < scopesArray.length; ++i) {
  638. localsJSON["scopes" + i] = GetChild(scopesArray[i], expandLevel - 1);
  639. }
  640. }
  641. if ("globals" in stackProperties && expandLevel > 0) {
  642. localsJSON["globals"] = GetChild(stackProperties["globals"], expandLevel - 1);
  643. }
  644. recordEvent(localsJSON);
  645. }
  646. },
  647. stack: function (flags) {
  648. if (flags === undefined) {
  649. flags = 0;
  650. }
  651. var stackTrace = callHostFunction(hostDebugObject.JsDiagGetStackTrace);
  652. var stackTraceArray = [];
  653. for (var i = 0; i < stackTrace.length; ++i) {
  654. var stack = {};
  655. stack["line"] = stackTrace[i].line;
  656. stack["column"] = stackTrace[i].column;
  657. stack["sourceText"] = stackTrace[i].sourceText;
  658. var functionObject = callHostFunction(hostDebugObject.JsDiagGetObjectFromHandle, stackTrace[i].functionHandle);
  659. stack["function"] = functionObject.name;
  660. stackTraceArray.push(stack);
  661. }
  662. recordEvent({
  663. 'callStack': stackTraceArray
  664. });
  665. },
  666. evaluate: function (expression, expandLevel, flags) {
  667. if (expression != undefined) {
  668. if (typeof expandLevel != "number" || expandLevel <= 0) {
  669. expandLevel = 0;
  670. }
  671. if (WScript && typeof expression == 'string' && WScript.forceDebugArrayBuffer)
  672. expression = stringToArrayBuffer(expression);
  673. var evalResult = callHostFunction(hostDebugObject.JsDiagEvaluate, _currentStackFrameIndex, expression);
  674. var evaluateOutput = {};
  675. evaluateOutput[evalResult.name] = GetChild(evalResult, expandLevel - 1);
  676. recordEvent({
  677. 'evaluate': evaluateOutput
  678. });
  679. }
  680. },
  681. enableBp: function (name) {
  682. bpManager.enableBreakpoint(name);
  683. },
  684. disableBp: function (name) {
  685. bpManager.disableBreakpoint(name);
  686. },
  687. deleteBp: function (name) {
  688. bpManager.deleteBreakpoint(name);
  689. },
  690. setFrame: function (depth) {
  691. var stackTrace = callHostFunction(hostDebugObject.JsDiagGetStackTrace);
  692. for (var i = 0; i < stackTrace.length; ++i) {
  693. if (stackTrace[i].index == depth) {
  694. _currentStackFrameIndex = depth;
  695. break;
  696. }
  697. }
  698. },
  699. dumpBreak: function () {
  700. var breakpoints = callHostFunction(hostDebugObject.JsDiagGetBreakpoints);
  701. recordEvent({
  702. 'breakpoints': breakpoints
  703. });
  704. },
  705. dumpSourceList: function () {
  706. var sources = callHostFunction(hostDebugObject.JsDiagGetScripts);
  707. sources.forEach(function (source) {
  708. if ("fileName" in source) {
  709. source["fileName"] = filterFileName(source["fileName"]);
  710. }
  711. });
  712. recordEvent({
  713. 'sources': sources
  714. });
  715. },
  716. dumpFunctionProperties: function (frameIdOrArrayOfIds = [0], expandLevel = 0) {
  717. if (typeof frameIdOrArrayOfIds != "number" && !(frameIdOrArrayOfIds instanceof Array)) {
  718. frameIdOrArrayOfIds = [0];
  719. }
  720. if (typeof expandLevel != "number" || expandLevel < 0) {
  721. expandLevel = 0;
  722. }
  723. let stackTrace = callHostFunction(hostDebugObject.JsDiagGetStackTrace);
  724. let functionHandles = [];
  725. let requestedFrameIndexes = [];
  726. if (typeof frameIdOrArrayOfIds === "number") {
  727. requestedFrameIndexes.push(frameIdOrArrayOfIds);
  728. } else if (frameIdOrArrayOfIds instanceof Array) {
  729. frameIdOrArrayOfIds.forEach((s) => {
  730. if (typeof s === "number") {
  731. requestedFrameIndexes.push(s);
  732. }
  733. });
  734. }
  735. if (requestedFrameIndexes.length == 0) {
  736. requestedFrameIndexes.push(0);
  737. }
  738. stackTrace.forEach((stackFrame) => {
  739. let stackFrameIndex = stackFrame.index;
  740. if (requestedFrameIndexes.includes(stackFrameIndex) && !functionHandles.includes(stackFrame.functionHandle)) {
  741. functionHandles.push(stackFrame.functionHandle);
  742. }
  743. });
  744. let functionProperties = [];
  745. functionHandles.forEach((handle) => {
  746. functionProperties.push(GetChild({ handle: handle }, expandLevel));
  747. });
  748. recordEvent({
  749. 'functionProperties': functionProperties
  750. });
  751. },
  752. trace: function (traceFlag) {
  753. _trace |= traceFlag;
  754. }
  755. },
  756. setBaseline: function (baseline) {
  757. try {
  758. _baseline = JSON.parse(baseline);
  759. } catch (ex) {
  760. printError("Invalid JSON passed to setBaseline: " + ex);
  761. internalPrint(baseline);
  762. }
  763. },
  764. dumpFunctionPosition: function (functionPosition) {
  765. if (!functionPosition) {
  766. functionPosition = {};
  767. }
  768. else {
  769. functionPosition["fileName"] = filterFileName(functionPosition["fileName"]);
  770. }
  771. recordEvent({
  772. 'functionPosition': functionPosition
  773. });
  774. },
  775. setInspectMaxStringLength: function (value) {
  776. _inspectMaxStringLength = value;
  777. },
  778. getOutputJson: function () {
  779. return JSON.stringify(_eventLog, undefined, " ");
  780. },
  781. verify: function () {
  782. return compareWithBaseline();
  783. },
  784. handleDebugEvent: function (debugEvent, eventData) {
  785. function debugEventToString(debugEvent) {
  786. switch (debugEvent) {
  787. case 0:
  788. return "JsDiagDebugEventSourceCompile";
  789. case 1:
  790. return "JsDiagDebugEventCompileError";
  791. case 2:
  792. return "JsDiagDebugEventBreakpoint";
  793. case 3:
  794. return "JsDiagDebugEventStepComplete";
  795. case 4:
  796. return "JsDiagDebugEventDebuggerStatement";
  797. case 5:
  798. return "JsDiagDebugEventAsyncBreak";
  799. case 6:
  800. return "JsDiagDebugEventRuntimeException";
  801. default:
  802. return "UnKnown";
  803. }
  804. }
  805. internalTrace(TRACE_DEBUG_EVENTS, {
  806. 'debugEvent': debugEventToString(debugEvent),
  807. 'eventData': eventData
  808. });
  809. switch (debugEvent) {
  810. case 0:
  811. /*JsDiagDebugEventSourceCompile*/
  812. var source = callHostFunction(hostDebugObject.JsDiagGetSource, eventData.scriptId);
  813. addSourceFile(source.source, source.scriptId);
  814. break;
  815. case 1:
  816. /*JsDiagDebugEventCompileError*/
  817. var stackTrace = callHostFunction(hostDebugObject.JsDiagGetScripts);
  818. break;
  819. case 2:
  820. /*JsDiagDebugEventBreakpoint*/
  821. case 3:
  822. /*JsDiagDebugEventStepComplete*/
  823. handleBreakpoint(("breakpointId" in eventData) ? eventData.breakpointId : -1);
  824. break;
  825. case 4:
  826. /*JsDiagDebugEventDebuggerStatement*/
  827. handleBreakpoint("debuggerStatement");
  828. break;
  829. case 5:
  830. /*JsDiagDebugEventAsyncBreak*/
  831. handleBreakpoint("asyncbreak");
  832. break;
  833. case 6:
  834. /*JsDiagDebugEventRuntimeException*/
  835. handleBreakpoint("exception");
  836. break;
  837. default:
  838. throw "Unhandled JsDiagDebugEvent value " + debugEvent;
  839. break;
  840. }
  841. },
  842. handleSourceRunDown: function (sources) {
  843. bpManager.clearAllBreakpoints();
  844. for (var len = 0; len < sources.length; ++len) {
  845. var source = callHostFunction(hostDebugObject.JsDiagGetSource, sources[len].scriptId);
  846. addSourceFile(source.source, source.scriptId);
  847. };
  848. },
  849. }
  850. })();
  851. // Commands for use from the breakpoint execution string in test files
  852. function log(str) {
  853. // Prints given string as 'LOG: <given string>' on console
  854. controllerObj.pushCommand(controllerObj.debuggerCommands.log, arguments);
  855. }
  856. function logJson(str) {
  857. // Prints given string on console
  858. controllerObj.pushCommand(controllerObj.debuggerCommands.logJson, arguments);
  859. }
  860. function resume(kind) {
  861. // Resume exceution after a break, kinds - step_into, step_out, step_over
  862. controllerObj.pushCommand(controllerObj.debuggerCommands.resume, arguments);
  863. }
  864. function locals(expandLevel, flags) {
  865. // Dumps locals tree, expand till expandLevel with given flags
  866. controllerObj.pushCommand(controllerObj.debuggerCommands.locals, arguments);
  867. }
  868. function stack() {
  869. controllerObj.pushCommand(controllerObj.debuggerCommands.stack, arguments);
  870. }
  871. function removeExpr(bpId) {
  872. // A workaround to remove the current expression
  873. }
  874. function evaluate(expression, expandLevel, flags) {
  875. controllerObj.pushCommand(controllerObj.debuggerCommands.evaluate, arguments);
  876. }
  877. function enableBp(name) {
  878. controllerObj.pushCommand(controllerObj.debuggerCommands.enableBp, arguments);
  879. }
  880. function disableBp(name) {
  881. controllerObj.pushCommand(controllerObj.debuggerCommands.disableBp, arguments);
  882. }
  883. function deleteBp(name) {
  884. controllerObj.pushCommand(controllerObj.debuggerCommands.deleteBp, arguments);
  885. }
  886. function setFrame(name) {
  887. controllerObj.pushCommand(controllerObj.debuggerCommands.setFrame, arguments);
  888. }
  889. function dumpBreak() {
  890. controllerObj.pushCommand(controllerObj.debuggerCommands.dumpBreak, arguments);
  891. }
  892. function dumpSourceList() {
  893. controllerObj.pushCommand(controllerObj.debuggerCommands.dumpSourceList, arguments);
  894. }
  895. function dumpFunctionProperties() {
  896. controllerObj.pushCommand(controllerObj.debuggerCommands.dumpFunctionProperties, arguments);
  897. }
  898. // Start internal tracing. E.g.: /**bp:trace(TRACE_COMMANDS)**/
  899. function trace() {
  900. controllerObj.pushCommand(controllerObj.debuggerCommands.trace, arguments);
  901. }
  902. // Non Supported - TO BE REMOVED
  903. function setExceptionResume() { }
  904. function setnext() { }
  905. function evaluateAsync() { }
  906. function trackProjectionCall() { }
  907. function mbp() { }
  908. function deleteMbp() { }
  909. // APIs exposed to Debugger.cpp
  910. function GetOutputJson() {
  911. return controllerObj.getOutputJson.apply(controllerObj, arguments);
  912. }
  913. function Verify() {
  914. return controllerObj.verify.apply(controllerObj, arguments);
  915. }
  916. function SetBaseline() {
  917. return controllerObj.setBaseline.apply(controllerObj, arguments);
  918. }
  919. function SetInspectMaxStringLength() {
  920. return controllerObj.setInspectMaxStringLength.apply(controllerObj, arguments);
  921. }
  922. function DumpFunctionPosition() {
  923. return controllerObj.dumpFunctionPosition.apply(controllerObj, arguments);
  924. }
  925. // Called from Debugger.cpp to handle JsDiagDebugEvent
  926. function HandleDebugEvent() {
  927. return controllerObj.handleDebugEvent.apply(controllerObj, arguments);
  928. }
  929. // Called from Debugger.cpp when debugger is attached using WScript.Attach
  930. function HandleSourceRunDown() {
  931. return controllerObj.handleSourceRunDown.apply(controllerObj, arguments);
  932. }