DbgController.js 37 KB

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