DbgController.js 36 KB

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