DbgController.js 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945
  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 {
  407. // Retrieve this breakpoint's execution string
  408. execStr = bpManager.getExecStr(id);
  409. if (execStr.toString().search("removeExpr()") != -1) {
  410. // A hack to remove entire expression, so that it will not run again.
  411. bpManager.setExecStr(id, null);
  412. }
  413. }
  414. internalTrace(TRACE_INTERNAL_FUNCTIONS, "handleBreakpoint execStr: ", execStr)
  415. if (execStr != null) {
  416. eval(execStr);
  417. }
  418. } catch (ex) {
  419. printError(ex);
  420. }
  421. }
  422. internalTrace(TRACE_INTERNAL_FUNCTIONS, "_commandList length: ", _commandList.length, " _wasResumed: ", _wasResumed);
  423. // Continue processing the command list.
  424. while (_commandList.length > 0 && !_wasResumed) {
  425. var cmd = _commandList.shift();
  426. internalTrace(TRACE_INTERNAL_FUNCTIONS, "cmd: ", cmd);
  427. var completion = cmd.fn.apply(this, cmd.args);
  428. if (typeof completion === "function") {
  429. _commandCompletions.push(completion);
  430. }
  431. }
  432. while (_commandCompletions.length > 0) {
  433. var completion = _commandCompletions.shift();
  434. completion();
  435. }
  436. if (!_wasResumed) {
  437. _currentStackFrameIndex = 0;
  438. _wasResumed = true;
  439. }
  440. }
  441. function GetObjectDisplay(obj) {
  442. var objectDisplay = ("className" in obj) ? obj["className"] : obj["type"];
  443. var value = ("value" in obj) ? obj["value"] : obj["display"];
  444. if (value && value.length > _inspectMaxStringLength) {
  445. objectDisplay += " <large string>";
  446. } else {
  447. objectDisplay += " " + value;
  448. }
  449. return objectDisplay;
  450. }
  451. function GetChild(obj, level) {
  452. function GetChildrens(obj, level) {
  453. var retArray = {};
  454. for (var i = 0; i < obj.length; ++i) {
  455. var propName = (obj[i].name == "__proto__") ? "#__proto__" : obj[i].name;
  456. retArray[propName] = GetChild(obj[i], level);
  457. }
  458. return retArray;
  459. }
  460. var retValue = {};
  461. if ("handle" in obj) {
  462. if (level >= 0) {
  463. var childProps = callHostFunction(hostDebugObject.JsDiagGetProperties, obj["handle"], 0, 1000);
  464. var properties = childProps["properties"];
  465. var debuggerOnlyProperties = childProps["debuggerOnlyProperties"];
  466. Array.prototype.push.apply(properties, debuggerOnlyProperties);
  467. if (properties.length > 0) {
  468. retValue = GetChildrens(properties, level - 1);
  469. } else {
  470. retValue = GetObjectDisplay(obj);
  471. }
  472. } else {
  473. retValue = GetObjectDisplay(obj);
  474. }
  475. delete obj["handle"];
  476. }
  477. if ("propertyAttributes" in obj) {
  478. delete obj["propertyAttributes"];
  479. }
  480. return retValue;
  481. }
  482. function compareWithBaseline() {
  483. function compareObjects(a, b, obj_namespace) {
  484. var objectsEqual = true;
  485. // This is a basic object comparison function, primarily to be used for JSON data.
  486. // It doesn't handle cyclical objects.
  487. function fail(step, message) {
  488. if (message == undefined) {
  489. message = "diff baselines for details";
  490. }
  491. printError("Step " + step + "; on: " + obj_namespace + ": " + message);
  492. objectsEqual = false;
  493. }
  494. function failNonObj(step, a, b, message) {
  495. if (message == undefined) {
  496. message = "";
  497. }
  498. printError("Step " + step + "; Local Diff on: " + obj_namespace + ": " + message);
  499. printError("Value 1:" + JSON.stringify(a));
  500. printError("Value 2:" + JSON.stringify(b));
  501. print("");
  502. objectsEqual = false;
  503. }
  504. // (1) Check strict equality.
  505. if (a === b)
  506. return true;
  507. // (2) non-Objects must have passed the strict equality comparison in (1)
  508. if ((typeof a != "object") || (typeof b != "object")) {
  509. failNonObj(2, a, b);
  510. return false;
  511. }
  512. // (3) check all properties
  513. for (var p in a) {
  514. // (4) check the property
  515. if (a[p] === b[p])
  516. continue;
  517. // (5) non-Objects must have passed the strict equality comparison in (4)
  518. if (typeof (a[p]) != "object") {
  519. failNonObj(5, a[p], b[p], "Property " + p);
  520. continue;
  521. }
  522. // (6) recursively check objects or arrays
  523. if (!compareObjects(a[p], b[p], obj_namespace + "." + p)) {
  524. // Don't need to report error message as it'll be reported inside nested call
  525. objectsEqual = false;
  526. continue;
  527. }
  528. }
  529. // (7) check any properties not in the previous enumeration
  530. var hasOwnProperty = Object.prototype.hasOwnProperty;
  531. for (var p in b) {
  532. if (hasOwnProperty.call(b, p) && !hasOwnProperty.call(a, p)) {
  533. fail(7, "Property missing: " + p + ", value: " + JSON.stringify(b[p]));
  534. continue;
  535. }
  536. }
  537. return objectsEqual;
  538. }
  539. var PASSED = 0;
  540. var FAILED = 1;
  541. if (_baseline == undefined) {
  542. return PASSED;
  543. }
  544. try {
  545. if (compareObjects(_baseline, _eventLog, "baseline")) {
  546. return PASSED;
  547. }
  548. }
  549. catch (ex) {
  550. printError("EXCEPTION: " + ex);
  551. }
  552. printError("TEST FAILED");
  553. return FAILED;
  554. }
  555. return {
  556. pushCommand: function pushCommand(fn, args) {
  557. _commandList.push({
  558. fn: fn,
  559. args: args
  560. });
  561. },
  562. debuggerCommands: {
  563. log: function (str) {
  564. internalPrint("LOG: " + str);
  565. },
  566. logJson: function (str) {
  567. internalPrint(str);
  568. },
  569. resume: function (kind) {
  570. if (_wasResumed) {
  571. printError("Breakpoint resumed twice");
  572. } else {
  573. var stepType = -1;
  574. if (kind == "step_into") {
  575. stepType = 0;
  576. } else if (kind == "step_out") {
  577. stepType = 1;
  578. } else if (kind == "step_over") {
  579. stepType = 2;
  580. } else if (kind == "step_document") {
  581. stepType = 0;
  582. }
  583. if (stepType != -1) {
  584. callHostFunction(hostDebugObject.JsDiagSetStepType, stepType);
  585. } else if (kind != "continue") {
  586. throw new Error("Unhandled step type - " + kind);
  587. }
  588. _wasResumed = true;
  589. _currentStackFrameIndex = 0;
  590. }
  591. },
  592. locals: function (expandLevel, flags) {
  593. if (expandLevel == undefined || expandLevel < 0) {
  594. expandLevel = 0;
  595. }
  596. var stackProperties = callHostFunction(hostDebugObject.JsDiagGetStackProperties, _currentStackFrameIndex);
  597. if (expandLevel >= 0) {
  598. var localsJSON = {};
  599. ["thisObject", "exception", "arguments", "returnValue"].forEach(function (name) {
  600. if (name in stackProperties) {
  601. var stackObject = stackProperties[name];
  602. localsJSON[stackObject.name] = GetChild(stackObject, expandLevel - 1);
  603. }
  604. });
  605. ["functionCallsReturn", "locals"].forEach(function (name) {
  606. if (name in stackProperties) {
  607. var stackObject = stackProperties[name];
  608. if (stackObject.length > 0) {
  609. localsJSON[name] = {};
  610. for (var i = 0; i < stackObject.length; ++i) {
  611. localsJSON[name][stackObject[i].name] = GetChild(stackObject[i], expandLevel - 1);
  612. }
  613. }
  614. }
  615. });
  616. if ("scopes" in stackProperties) {
  617. var scopesArray = stackProperties["scopes"];
  618. for (var i = 0; i < scopesArray.length; ++i) {
  619. localsJSON["scopes" + i] = GetChild(scopesArray[i], expandLevel - 1);
  620. }
  621. }
  622. if ("globals" in stackProperties && expandLevel > 0) {
  623. localsJSON["globals"] = GetChild(stackProperties["globals"], expandLevel - 1);
  624. }
  625. recordEvent(localsJSON);
  626. }
  627. },
  628. stack: function (flags) {
  629. if (flags === undefined) {
  630. flags = 0;
  631. }
  632. var stackTrace = callHostFunction(hostDebugObject.JsDiagGetStackTrace);
  633. var stackTraceArray = [];
  634. for (var i = 0; i < stackTrace.length; ++i) {
  635. var stack = {};
  636. stack["line"] = stackTrace[i].line;
  637. stack["column"] = stackTrace[i].column;
  638. stack["sourceText"] = stackTrace[i].sourceText;
  639. var functionObject = callHostFunction(hostDebugObject.JsDiagGetObjectFromHandle, stackTrace[i].functionHandle);
  640. stack["function"] = functionObject.name;
  641. stackTraceArray.push(stack);
  642. }
  643. recordEvent({
  644. 'callStack': stackTraceArray
  645. });
  646. },
  647. evaluate: function (expression, expandLevel, flags) {
  648. if (expression != undefined) {
  649. if (typeof expandLevel != "number" || expandLevel <= 0) {
  650. expandLevel = 0;
  651. }
  652. var evalResult = callHostFunction(hostDebugObject.JsDiagEvaluate, _currentStackFrameIndex, expression);
  653. var evaluateOutput = {};
  654. evaluateOutput[evalResult.name] = GetChild(evalResult, expandLevel - 1);
  655. recordEvent({
  656. 'evaluate': evaluateOutput
  657. });
  658. }
  659. },
  660. enableBp: function (name) {
  661. bpManager.enableBreakpoint(name);
  662. },
  663. disableBp: function (name) {
  664. bpManager.disableBreakpoint(name);
  665. },
  666. deleteBp: function (name) {
  667. bpManager.deleteBreakpoint(name);
  668. },
  669. setFrame: function (depth) {
  670. var stackTrace = callHostFunction(hostDebugObject.JsDiagGetStackTrace);
  671. for (var i = 0; i < stackTrace.length; ++i) {
  672. if (stackTrace[i].index == depth) {
  673. _currentStackFrameIndex = depth;
  674. break;
  675. }
  676. }
  677. },
  678. dumpBreak: function () {
  679. var breakpoints = callHostFunction(hostDebugObject.JsDiagGetBreakpoints);
  680. recordEvent({
  681. 'breakpoints': breakpoints
  682. });
  683. },
  684. dumpSourceList: function () {
  685. var sources = callHostFunction(hostDebugObject.JsDiagGetScripts);
  686. sources.forEach(function (source) {
  687. if ("fileName" in source) {
  688. source["fileName"] = filterFileName(source["fileName"]);
  689. }
  690. });
  691. recordEvent({
  692. 'sources': sources
  693. });
  694. },
  695. trace: function (traceFlag) {
  696. _trace |= traceFlag;
  697. }
  698. },
  699. setBaseline: function (baseline) {
  700. try {
  701. _baseline = JSON.parse(baseline);
  702. } catch (ex) {
  703. printError("Invalid JSON passed to setBaseline: " + ex);
  704. internalPrint(baseline);
  705. }
  706. },
  707. dumpFunctionPosition: function (functionPosition) {
  708. if (!functionPosition) {
  709. functionPosition = {};
  710. }
  711. else {
  712. functionPosition["fileName"] = filterFileName(functionPosition["fileName"]);
  713. }
  714. recordEvent({
  715. 'functionPosition': functionPosition
  716. });
  717. },
  718. setInspectMaxStringLength: function (value) {
  719. _inspectMaxStringLength = value;
  720. },
  721. getOutputJson: function () {
  722. return JSON.stringify(_eventLog, undefined, " ");
  723. },
  724. verify: function () {
  725. return compareWithBaseline();
  726. },
  727. handleDebugEvent: function (debugEvent, eventData) {
  728. function debugEventToString(debugEvent) {
  729. switch (debugEvent) {
  730. case 0:
  731. return "JsDiagDebugEventSourceCompile";
  732. case 1:
  733. return "JsDiagDebugEventCompileError";
  734. case 2:
  735. return "JsDiagDebugEventBreakpoint";
  736. case 3:
  737. return "JsDiagDebugEventStepComplete";
  738. case 4:
  739. return "JsDiagDebugEventDebuggerStatement";
  740. case 5:
  741. return "JsDiagDebugEventAsyncBreak";
  742. case 6:
  743. return "JsDiagDebugEventRuntimeException";
  744. default:
  745. return "UnKnown";
  746. }
  747. }
  748. internalTrace(TRACE_DEBUG_EVENTS, {
  749. 'debugEvent': debugEventToString(debugEvent),
  750. 'eventData': eventData
  751. });
  752. switch (debugEvent) {
  753. case 0:
  754. /*JsDiagDebugEventSourceCompile*/
  755. var source = callHostFunction(hostDebugObject.JsDiagGetSource, eventData.scriptId);
  756. addSourceFile(source.source, source.scriptId);
  757. break;
  758. case 1:
  759. /*JsDiagDebugEventCompileError*/
  760. var stackTrace = callHostFunction(hostDebugObject.JsDiagGetScripts);
  761. break;
  762. case 2:
  763. /*JsDiagDebugEventBreakpoint*/
  764. case 3:
  765. /*JsDiagDebugEventStepComplete*/
  766. case 4:
  767. /*JsDiagDebugEventDebuggerStatement*/
  768. handleBreakpoint(("breakpointId" in eventData) ? eventData.breakpointId : -1);
  769. break;
  770. case 5:
  771. /*JsDiagDebugEventAsyncBreak*/
  772. handleBreakpoint("asyncbreak");
  773. break;
  774. case 6:
  775. /*JsDiagDebugEventRuntimeException*/
  776. handleBreakpoint("exception");
  777. break;
  778. default:
  779. throw "Unhandled JsDiagDebugEvent value " + debugEvent;
  780. break;
  781. }
  782. },
  783. handleSourceRunDown: function (sources) {
  784. bpManager.clearAllBreakpoints();
  785. for (var len = 0; len < sources.length; ++len) {
  786. var source = callHostFunction(hostDebugObject.JsDiagGetSource, sources[len].scriptId);
  787. addSourceFile(source.source, source.scriptId);
  788. };
  789. },
  790. }
  791. })();
  792. // Commands for use from the breakpoint execution string in test files
  793. function log(str) {
  794. // Prints given string as 'LOG: <given string>' on console
  795. controllerObj.pushCommand(controllerObj.debuggerCommands.log, arguments);
  796. }
  797. function logJson(str) {
  798. // Prints given string on console
  799. controllerObj.pushCommand(controllerObj.debuggerCommands.logJson, arguments);
  800. }
  801. function resume(kind) {
  802. // Resume exceution after a break, kinds - step_into, step_out, step_over
  803. controllerObj.pushCommand(controllerObj.debuggerCommands.resume, arguments);
  804. }
  805. function locals(expandLevel, flags) {
  806. // Dumps locals tree, expand till expandLevel with given flags
  807. controllerObj.pushCommand(controllerObj.debuggerCommands.locals, arguments);
  808. }
  809. function stack() {
  810. controllerObj.pushCommand(controllerObj.debuggerCommands.stack, arguments);
  811. }
  812. function removeExpr(bpId) {
  813. // A workaround to remove the current expression
  814. }
  815. function evaluate(expression, expandLevel, flags) {
  816. controllerObj.pushCommand(controllerObj.debuggerCommands.evaluate, arguments);
  817. }
  818. function enableBp(name) {
  819. controllerObj.pushCommand(controllerObj.debuggerCommands.enableBp, arguments);
  820. }
  821. function disableBp(name) {
  822. controllerObj.pushCommand(controllerObj.debuggerCommands.disableBp, arguments);
  823. }
  824. function deleteBp(name) {
  825. controllerObj.pushCommand(controllerObj.debuggerCommands.deleteBp, arguments);
  826. }
  827. function setFrame(name) {
  828. controllerObj.pushCommand(controllerObj.debuggerCommands.setFrame, arguments);
  829. }
  830. function dumpBreak() {
  831. controllerObj.pushCommand(controllerObj.debuggerCommands.dumpBreak, arguments);
  832. }
  833. function dumpSourceList() {
  834. controllerObj.pushCommand(controllerObj.debuggerCommands.dumpSourceList, arguments);
  835. }
  836. // Start internal tracing. E.g.: /**bp:trace(TRACE_COMMANDS)**/
  837. function trace() {
  838. controllerObj.pushCommand(controllerObj.debuggerCommands.trace, arguments);
  839. }
  840. // Non Supported - TO BE REMOVED
  841. function setExceptionResume() { }
  842. function setnext() { }
  843. function evaluateAsync() { }
  844. function trackProjectionCall() { }
  845. function mbp() { }
  846. function deleteMbp() { }
  847. // APIs exposed to Debugger.cpp
  848. function GetOutputJson() {
  849. return controllerObj.getOutputJson.apply(controllerObj, arguments);
  850. }
  851. function Verify() {
  852. return controllerObj.verify.apply(controllerObj, arguments);
  853. }
  854. function SetBaseline() {
  855. return controllerObj.setBaseline.apply(controllerObj, arguments);
  856. }
  857. function SetInspectMaxStringLength() {
  858. return controllerObj.setInspectMaxStringLength.apply(controllerObj, arguments);
  859. }
  860. function DumpFunctionPosition() {
  861. return controllerObj.dumpFunctionPosition.apply(controllerObj, arguments);
  862. }
  863. // Called from Debugger.cpp to handle JsDiagDebugEvent
  864. function HandleDebugEvent() {
  865. return controllerObj.handleDebugEvent.apply(controllerObj, arguments);
  866. }
  867. // Called from Debugger.cpp when debugger is attached using WScript.Attach
  868. function HandleSourceRunDown() {
  869. return controllerObj.handleSourceRunDown.apply(controllerObj, arguments);
  870. }