Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1101 lines
32 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 1995
  5. //
  6. // File: utils.js
  7. //
  8. // Contents: Utility file for use by all script modules
  9. //
  10. //----------------------------------------------------------------------------
  11. Include('sendmail.js');
  12. // ---------------------------------------------------------------------------
  13. //
  14. // Global Variables
  15. //
  16. // ---------------------------------------------------------------------------
  17. var g_aDebugOnce = new Array();
  18. // The following should be "true" in the main branch and "false" in the ntdrop
  19. // branch. Do not integrate this change from one side to the other.
  20. var g_fUseMTScriptAssert = false;
  21. var g_IgnoreAllAsserts = false;
  22. var g_fAssertOnDispatchException = true;
  23. var g_SuppressErrorDialogs = true;
  24. var MAX_MSGS_FILES = 10;
  25. var strUtilsVersion = /* $DROPVERSION: */ "V(########) F(!!!!!!!!!!!!!!)" /* $ */;
  26. var g_reBuildNum = new RegExp("V\\(([#0-9. ]+)\\)");
  27. //var g_reBuildFile = new RegExp("F\\(([!A-Za-z_. ]+)\\)");
  28. var g_aPublicBuildNum;
  29. initUneval();
  30. unevalInitialized = true;
  31. // ---------------------------------------------------------------------------
  32. //
  33. // String Prototype Additions
  34. //
  35. // ---------------------------------------------------------------------------
  36. function String_TrimTrailingWS()
  37. {
  38. var end = this.length - 1;
  39. while (this.charAt(end) == ' ')
  40. {
  41. end--;
  42. }
  43. return this.slice(0, end+1);
  44. }
  45. function String_IsEqualNoCase(str)
  46. {
  47. return this.toLowerCase() == str.toLowerCase();
  48. }
  49. /*function String_TrimLeadingWS()
  50. {
  51. var end = 0;
  52. while (this.charAt(end) == ' ')
  53. {
  54. ++end;
  55. }
  56. return this.slice(end);
  57. }
  58. */
  59. // String_SplitFileName
  60. // Return an array of 3 elements, path,filename,extension
  61. // [0] == "C:\path\"
  62. // [1] == "filename"
  63. // [2] == ".ext"
  64. //
  65. function String_SplitFileName()
  66. {
  67. var nDot = this.lastIndexOf('.');
  68. var nSlash = this.lastIndexOf('\\');
  69. var nColon = this.lastIndexOf(':');
  70. if (nDot >= 0 && nDot > nSlash && nDot > nColon)
  71. {
  72. return [this.slice(0, nSlash + 1), this.slice(nSlash + 1, nDot), this.slice(nDot)];
  73. }
  74. // We get here if the file had no extension
  75. if (nSlash >= 2) // do not slice the UNC double \ at the start of a filename.
  76. {
  77. return [this.slice(0, nSlash + 1), this.slice(nSlash + 1, nDot), ''];
  78. }
  79. return ['', this, ''];
  80. }
  81. function String_RemoveExtension()
  82. {
  83. var nDot = this.lastIndexOf('.');
  84. var nSlash = this.lastIndexOf('\\');
  85. var nColon = this.lastIndexOf(':');
  86. if (nDot >= 0 && nDot > nSlash && nDot > nColon)
  87. {
  88. return this.slice(0, nDot);
  89. }
  90. return this;
  91. }
  92. //+---------------------------------------------------------------------------
  93. //
  94. // Function: Substitute
  95. //
  96. // Synopsis: Given the replacement information in aStrMap, replace any
  97. // 'name' occurrances with the appropriate 'value'.
  98. //
  99. // Arguments: [aStrMap] -- Array of regular expressions and replacement text
  100. //
  101. // Returns: The string with any appropriate substitutions made.
  102. //
  103. // Notes: aStrMap is the array generated by InitStringMaps()
  104. //
  105. //----------------------------------------------------------------------------
  106. function String_Substitute(aStrMap)
  107. {
  108. var i;
  109. var text = this;
  110. for (i = 0; i < aStrMap.length; i++)
  111. {
  112. text = text.replace(aStrMap[i].re, aStrMap[i].newtext);
  113. }
  114. return text;
  115. }
  116. //+---------------------------------------------------------------------------
  117. //
  118. // Function: InitStringMaps
  119. //
  120. // Synopsis: Creates an array with regular expression objects that we can
  121. // use to do substitutions of strings.
  122. //
  123. // Arguments: [aStrMap] -- Array of strings of form "name=value"
  124. // [aNewStrMap] -- New array containing regular expressions and
  125. // replacement text. Used by String_Substitute().
  126. //
  127. //----------------------------------------------------------------------------
  128. function InitStringMaps(aStrMap, aNewStrMap)
  129. {
  130. var i;
  131. var index;
  132. for (i = 0; i < aStrMap.length; i++)
  133. {
  134. aNewStrMap[i] = new Object();
  135. index = aStrMap[i].indexOf('=');
  136. aNewStrMap[i].re = new RegExp(aStrMap[i].slice(0, index), 'gi');
  137. aNewStrMap[i].re.compile(aStrMap[i].slice(0, index), 'gi');
  138. aNewStrMap[i].newtext = aStrMap[i].slice(index+1);
  139. }
  140. }
  141. // PadDigits(n, cDigits)
  142. // Return a string representation of the given
  143. // number. Leading zeros are added to make the
  144. // string cDigits long.
  145. function PadDigits(n, cDigits)
  146. {
  147. var strDigits = '';
  148. var i;
  149. var strNumber = n.toString();
  150. for(i = 0; i < cDigits - strNumber.length; ++i)
  151. strDigits += '0';
  152. return strDigits + n;
  153. }
  154. // DateToString(dDate)
  155. // Format a date like this: "YYYY/MM/DD:HH:MM:SS"
  156. // this format does not change with locale, and is
  157. // lends itself to sorting.
  158. function Date_DateToSDString(fDateOnly)
  159. {
  160. var str =
  161. PadDigits(this.getFullYear(), 4) + "/" +
  162. PadDigits(this.getMonth() + 1, 2) + "/" +
  163. PadDigits(this.getDate(), 2);
  164. if (!fDateOnly)
  165. {
  166. str += ":" +
  167. PadDigits(this.getHours(), 2) + ":" +
  168. PadDigits(this.getMinutes(), 2) + ":" +
  169. PadDigits(this.getSeconds(), 2);
  170. }
  171. return str;
  172. }
  173. function Error_ToString()
  174. {
  175. var i;
  176. var str = 'Exception(';
  177. /*
  178. Only some error messages get filled in for "ex".
  179. Specifically the text for disk full never seems
  180. to get set by functions such as CreateTextFile().
  181. */
  182. if (this.number != null && this.description == "")
  183. {
  184. switch(this.number)
  185. {
  186. case -2147024784:
  187. this.description = "There is not enough space on the disk.";
  188. break;
  189. case -2147024894:
  190. this.description = "The system cannot find the file specified.";
  191. break;
  192. case -2147023585:
  193. this.description = "There are currently no logon servers available to service the logon request.";
  194. break;
  195. case -2147023170:
  196. this.description = "The remote procedure call failed.";
  197. break;
  198. case -2147024837:
  199. this.description = "An unexpected network error occurred.";
  200. break;
  201. case -2147024893:
  202. this.description = "The system cannot find the path specified.";
  203. break;
  204. case -2147024890:
  205. this.description = "The handle is invalid.";
  206. break;
  207. default:
  208. this.description = "Error text not set for (" + this.number + ")";
  209. break;
  210. }
  211. }
  212. for(i in this)
  213. {
  214. if (!this.__isPublicMember(i))
  215. continue;
  216. str += i + ": " + this[i] + " ";
  217. }
  218. return str + ")";
  219. }
  220. String.prototype.IsEqualNoCase = String_IsEqualNoCase;
  221. String.prototype.TrimTrailingWS = String_TrimTrailingWS;
  222. //String.prototype.TrimLeadingWS = String_TrimLeadingWS;
  223. String.prototype.RemoveExtension = String_RemoveExtension;
  224. String.prototype.SplitFileName = String_SplitFileName;
  225. String.prototype.Substitute = String_Substitute;
  226. Date.prototype.DateToSDString = Date_DateToSDString;
  227. Error.prototype.toString = Error_ToString;
  228. // ---------------------------------------------------------------------------
  229. //
  230. // Miscellaneous Stuff
  231. //
  232. // ---------------------------------------------------------------------------
  233. function CommonOnScriptError(strThread, strFile, nLine, nChar, strText, sCode, strSource, strDescription)
  234. {
  235. var dlg = new Dialog();
  236. dlg.fShowDialog = true;
  237. dlg.strTitle = "Script Error in " + strThread + ".js";
  238. dlg.strMessage = "File: " + strFile +
  239. "\nLine: " + nLine +
  240. "\nChar: " + nChar +
  241. "\nText: " + strText +
  242. "\nsCode: " + sCode +
  243. "\nSource: " + strSource +
  244. "\nDescription: " + strDescription +
  245. "\nThread:" + strThread;
  246. dlg.aBtnText[0] = "OK";
  247. LogMsg("On error!\n" + dlg.strMessage);
  248. CreateErrorDialog(dlg);
  249. if (g_SuppressErrorDialogs)
  250. return 1; // Enter the script debugger if present
  251. return 0; // display the error
  252. }
  253. function CommonVersionCheck(strLocalVersion)
  254. {
  255. var aLocal;
  256. if (g_aPublicBuildNum == null)
  257. {
  258. g_aPublicBuildNum = g_reBuildNum.exec(PublicData.strDataVersion);
  259. if (g_aPublicBuildNum) // First time around, lets check the version of utils.js too.
  260. {
  261. if (!CommonVersionCheck(strUtilsVersion))
  262. return false;
  263. }
  264. }
  265. aLocal = g_reBuildNum.exec(strLocalVersion);
  266. if (!aLocal || !g_aPublicBuildNum)
  267. {
  268. SimpleErrorDialog("Invalid version format: ", strLocalVersion + "," + PublicData.strDataVersion, false);
  269. return false;
  270. }
  271. if (aLocal[1] != g_aPublicBuildNum[1])
  272. {
  273. SimpleErrorDialog("Script Version mismatch", strLocalVersion + "," + PublicData.strDataVersion, false);
  274. return false;
  275. }
  276. return true;
  277. }
  278. /*
  279. CreateErrorDialog()
  280. If we are runnning standalone:
  281. 1: Send email, throttled by MAIL_RESEND_INTERVAL
  282. 2: Update PublicData.objDialog, throttled by PublicData.objDialog.fShowDialog
  283. If we are running distributed:
  284. 1: If the error is EMail only, throttle it by MAIL_RESEND_INTERVAL
  285. 2: Update PublicData.objDialog, throttled by PublicData.objDialog.fShowDialog
  286. 3: Notify BuildManager via 'ScriptError' notification.
  287. */
  288. function CreateErrorDialog(objDialog)
  289. {
  290. var fEMailSent = false;
  291. var fSendMail = false;
  292. var fUpdateDialog = false;
  293. if (PublicData.objDialog)
  294. objDialog.cDialogIndex = PublicData.objDialog.cDialogIndex;
  295. objDialog.cDialogIndex++;
  296. TakeThreadLock('OnTaskError');
  297. var curDate = new Date().getTime();
  298. if ((curDate - PrivateData.dateErrorMailSent) > MAIL_RESEND_INTERVAL)
  299. {
  300. PrivateData.dateErrorMailSent = curDate;
  301. fSendMail = true;
  302. }
  303. ReleaseThreadLock('OnTaskError');
  304. if (fSendMail)
  305. {
  306. if (PrivateData.fIsStandalone)
  307. {
  308. if (SendErrorMail(objDialog.strTitle, objDialog.strMessage))
  309. fEMailSent = true;
  310. else
  311. {
  312. // If EMail failed, then create a dialog instead.
  313. objDialog.fEMailOnly = false;
  314. }
  315. }
  316. }
  317. if (!fEMailSent)
  318. LogMsg("On error NOT EMAIL MESSAGE: " + objDialog.strTitle + ", " + objDialog.strMessage);
  319. if (fSendMail || !objDialog.fEMailOnly)
  320. {
  321. TakeThreadLock('Dialog');
  322. try
  323. {
  324. if (!PublicData.objDialog || PublicData.objDialog.fShowDialog == false)
  325. {
  326. PublicData.objDialog.strTitle = objDialog.strTitle;
  327. PublicData.objDialog.strMessage = objDialog.strMessage;
  328. PublicData.objDialog.aBtnText[0] = objDialog.aBtnText[0];
  329. PublicData.objDialog.cDialogIndex = objDialog.cDialogIndex;
  330. PublicData.objDialog.fShowDialog = objDialog.fShowDialog;
  331. PublicData.objDialog.fEMailOnly = objDialog.fEMailOnly;
  332. fUpdateDialog = true;
  333. }
  334. }
  335. catch(ex)
  336. {
  337. }
  338. ReleaseThreadLock('Dialog');
  339. if (!PrivateData.fIsStandalone && fUpdateDialog)
  340. { // If distributed build, notify our parent
  341. if (PrivateData.objUtil != null && PrivateData.objUtil.fnUneval != null)
  342. {
  343. NotifyScript('ScriptError', PrivateData.objUtil.fnUneval(objDialog));
  344. // At this point or soon thereafter, "slaveproxy" should exec the 'dialog' command
  345. // to clear fShowDialog.
  346. }
  347. }
  348. }
  349. }
  350. /*
  351. CreateFileOpenErrorDialog()
  352. Build a simple file error dialog and push it to the UI.
  353. Argument:
  354. strMsg: Supply a single word description of the failed operation (eg: "open", "copy")
  355. strFilename: The filename in question
  356. ex: The exception object created when the error occured.
  357. */
  358. function CreateFileOpenErrorDialog(strMsg, strFilename, ex)
  359. {
  360. SimpleErrorDialog(strMsg, strFilename + "\n" + ex, false);
  361. }
  362. /*
  363. SimpleErrorDialog()
  364. Easy way to create and set the error dialog - just
  365. pass in a title and a message. Set fEMailOnly
  366. to just email the message.
  367. */
  368. function SimpleErrorDialog(strTitle, strMsg, fEMailOnly)
  369. {
  370. var dlg = new Dialog();
  371. dlg.fShowDialog = true;
  372. dlg.strTitle = strTitle;
  373. dlg.strMessage = strMsg;
  374. dlg.aBtnText[0] = "OK";
  375. dlg.fEMailOnly = fEMailOnly;
  376. CreateErrorDialog(dlg);
  377. }
  378. /*
  379. LogOpenTextFile
  380. This is a simple wrapper for OpenTextFile().
  381. The only difference is that this function automatically
  382. handles error reporting - putting the error message in the log
  383. file and in the error dialog.
  384. */
  385. function LogOpenTextFile(fsobj, filename, iomode, create, format)
  386. {
  387. try
  388. {
  389. return fsobj.OpenTextFile(filename, iomode, create, format);
  390. }
  391. catch(ex)
  392. {
  393. LogMsg("Exception occurred calling OpenTextFile(" + [filename, iomode, create, format].toString() + ")" + ex);
  394. CreateFileOpenErrorDialog("An error occurred opening a file", "File: " + filename, ex);
  395. throw ex;
  396. }
  397. }
  398. /*
  399. LogCreateTextFile
  400. This is a simple wrapper for CreateTextFile().
  401. The only difference is that this function automatically
  402. handles error reporting - putting the error message in the log
  403. file and in the error dialog.
  404. This function is mainly used with UNC paths.
  405. Apparently, file handles opened with UNC paths can
  406. go stale sometimes (even when the UNC refers to the
  407. local machine).
  408. Therefore, I've created a simple wrapper for the TextStream object
  409. to handle this problem by attempting to reopen the file if
  410. we get an error writing to it.
  411. If we continue to not be able to write to the file, generate an error
  412. message.
  413. */
  414. function LogCreateTextFile(fsobj, filename, overwrite, unicode)
  415. {
  416. try
  417. {
  418. var obj = new Object;
  419. obj.hFile = fsobj.CreateTextFile(filename, overwrite, unicode);
  420. obj.strFileName = filename;
  421. obj.fFailed = false;
  422. obj.fsobj = fsobj;
  423. obj.WriteLine = CreateTextFile_WriteLine;
  424. obj.Close = CreateTextFile_Close;
  425. }
  426. catch(ex)
  427. {
  428. LogMsg("Exception occurred calling CreateTextFile(" + [filename, overwrite, unicode].toString() + ")" + ex);
  429. CreateFileOpenErrorDialog("An error occurred creating a file", "File: " + filename, ex);
  430. throw ex;
  431. }
  432. return obj;
  433. }
  434. function CreateTextFile_WriteLine(msg)
  435. {
  436. var n;
  437. for(n = 0 ; n < 3; ++n)
  438. {
  439. try
  440. {
  441. if (this.hFile == null)
  442. {
  443. this.hFile = this.fsobj.OpenTextFile(this.strFileName, 8); // 8==For append
  444. LogMsg("Reopened TextFile '" + this.strFileName + "'");
  445. this.fFailed = false;
  446. }
  447. this.hFile.WriteLine(msg);
  448. return;
  449. }
  450. catch(ex)
  451. {
  452. if (!this.fFailed)
  453. {
  454. LogMsg("Error During WriteLine on file '" + this.strFileName + "', " + ex);
  455. LogMsg("TRY Reopen TextFile '" + this.strFileName + "'");
  456. Sleep(250);
  457. }
  458. this.hFile = null;
  459. }
  460. }
  461. if (!this.fFailed)
  462. {
  463. LogMsg("Failed to reopen " + this.strFileName);
  464. CreateFileOpenErrorDialog("An error occurred reopening a file", "File: " + this.strFileName, ex);
  465. this.fFailed = true;
  466. }
  467. }
  468. function CreateTextFile_Close()
  469. {
  470. try
  471. {
  472. this.hFile.Close();
  473. }
  474. catch(ex)
  475. {
  476. LogMsg("Error During Close() on file '" + this.strFileName + "', " + ex);
  477. }
  478. }
  479. function EnsureArray(obj, name)
  480. {
  481. if (obj[name])
  482. {
  483. if (obj[name].length == null || typeof(obj[name]) != 'object')
  484. {
  485. var oldobj = obj[name];
  486. obj[name] = new Array();
  487. obj[name][0] = oldobj;
  488. }
  489. }
  490. else
  491. {
  492. obj[name] = new Array();
  493. }
  494. }
  495. function ThrowError(msg, detail)
  496. {
  497. var ex = new Error(-1, msg);
  498. ex.detail = detail;
  499. throw ex;
  500. }
  501. function GetCallerName(cIgnoreCaller)
  502. {
  503. var tokens;
  504. if (cIgnoreCaller == null)
  505. cIgnoreCaller = 0;
  506. ++cIgnoreCaller;
  507. var caller = GetCallerName.caller;
  508. while (caller != null && cIgnoreCaller)
  509. {
  510. caller = caller.caller;
  511. --cIgnoreCaller;
  512. }
  513. if (caller != null)
  514. {
  515. tokens = caller.toString().split(/ |\t|\)|,/);
  516. if (tokens.length > 1 && tokens[0] == "function")
  517. {
  518. return tokens[1] + ")";
  519. }
  520. }
  521. return "<undefined>";
  522. }
  523. function DebugOnce(id)
  524. {
  525. caller = GetCallerName();
  526. if (g_aDebugOnce[id + caller] == null)
  527. {
  528. g_aDebugOnce[id + caller] = 1;
  529. debugger;
  530. }
  531. }
  532. function JAssert(fSuccess, msg)
  533. {
  534. var caller;
  535. var i;
  536. if (!fSuccess)
  537. {
  538. if (msg == null)
  539. msg = '';
  540. caller = GetCallerName();
  541. LogMsg("ASSERTION FAILED :(" + caller + ") " + msg);
  542. if (!g_IgnoreAllAsserts)
  543. debugger;
  544. if (g_fUseMTScriptAssert)
  545. {
  546. if (arguments.length > 1)
  547. ASSERT(false, "JScript: " + msg);
  548. else
  549. ASSERT(false, "JScript Assertion");
  550. }
  551. }
  552. }
  553. function LogObject(strMsg, obj)
  554. {
  555. LogMsg("DUMP OBJECT " + strMsg);
  556. var i;
  557. for(i in obj)
  558. {
  559. if (!obj.__isPublicMember(i))
  560. continue;
  561. LogMsg("\tobj[" + i + "] = '" + obj[i] + "'");
  562. }
  563. }
  564. function GetPrivPub()
  565. {
  566. var obj = new Object;
  567. var strData = PrivateData.objUtil.fnUneval(PrivateData);
  568. obj.PrivateData = MyEval(strData);
  569. strData = PrivateData.objUtil.fnUneval(PublicData);
  570. obj.PublicData = MyEval(strData);
  571. return obj;
  572. }
  573. // Record MTScript message to a log file.
  574. // If the log file is not open, create a new one.
  575. // This function should be called eary during the intialization
  576. // of mtscript.js.
  577. function LogMsg(strMsg, cIgnoreCaller)
  578. {
  579. try
  580. {
  581. var fileLog;
  582. var d = (new Date()).DateToSDString();
  583. var strFormattedMsg = LocalMachine + "\t" + d + "\t" + GetCallerName(cIgnoreCaller) + "\t" + strMsg;
  584. if (PrivateData.objUtil.fnCreateNumberedFile == null)
  585. OUTPUTDEBUGSTRING("Utilthrd Initialize() has not been called yet");
  586. else if (PrivateData.fEnableLogging)
  587. {
  588. if (!PrivateData.fileLog)
  589. {
  590. // First check to see if the file has been created yet.
  591. // The grab a critical section.
  592. // Checking "fileLog" first makes the usual
  593. // case (the file is already open) quicker.
  594. if (PrivateData.fInLogMsg)
  595. {
  596. OUTPUTDEBUGSTRING(d.slice(11) + "\t" + GetCallerName(cIgnoreCaller) + "\t" + strMsg + "\tLOGMSG RECURSION!");
  597. return;
  598. }
  599. TakeThreadLock("PrivateData.fileLog");
  600. if (!PrivateData.fileLog && PrivateData.fEnableLogging)
  601. {
  602. PrivateData.fInLogMsg = true;
  603. JAssert(PrivateData.objUtil.fnCreateNumberedFile != null, "Utilthrd Initialize() has not been called yet");
  604. fileLog = PrivateData.objUtil.fnCreateNumberedFile("MTScript.log", MAX_MSGS_FILES);
  605. fileLog[0].WriteLine("Log of " + LocalMachine + " Created " + d);
  606. PrivateData.fileLog = fileLog;
  607. }
  608. ReleaseThreadLock("PrivateData.fileLog");
  609. PrivateData.fInLogMsg = false;
  610. }
  611. if (PrivateData.fEnableLogging)
  612. PrivateData.fileLog[0].WriteLine(strFormattedMsg);
  613. }
  614. OUTPUTDEBUGSTRING(d.slice(11) + "\t" + GetCallerName(cIgnoreCaller) + "\t" + strMsg);
  615. }
  616. catch(ex)
  617. {
  618. OUTPUTDEBUGSTRING("Error in AppendLog: " + ex);
  619. OUTPUTDEBUGSTRING(strMsg);
  620. PrivateData.fInLogMsg = false;
  621. }
  622. }
  623. /*
  624. Given parameters:
  625. testmachine
  626. D:\foo\bar\newnt
  627. \build_logs\sync_root.log
  628. change it to:
  629. \\testmachine\BC_D_foo_bar_newnt\build_logs\sync_root.log
  630. */
  631. function MakeUNCPath(strMachineName, strEnlistmentPath, strPath)
  632. {
  633. // var strPathInput = strPath;
  634. var aParts = strEnlistmentPath.split(/[\\:]/g);
  635. strPath = "\\\\" +
  636. strMachineName +
  637. "\\BC_" +
  638. aParts.join("_") +
  639. strPath;
  640. // LogMsg("Input: ('" + strEnlistmentPath + "', '" + strPathInput + "'), output='" + strPath + "' parts=(" + aParts.join(",") + ")");
  641. return strPath;
  642. }
  643. // MyEval(expr)
  644. // evaluating uneval'ed objects creates a bunch of junk local variables.
  645. // by putting the eval call in a little subroutine, we avoid keeping those
  646. // locals around.
  647. function MyEval(expr)
  648. {
  649. try
  650. {
  651. return eval(expr);
  652. }
  653. catch(ex)
  654. {
  655. LogMsg("caught an exception: " + ex);
  656. LogMsg("evaluating " + expr);
  657. throw ex;
  658. }
  659. }
  660. // ---------------------------------------------------------------------------
  661. //
  662. // Uneval implementation
  663. //
  664. // Calling 'uneval(object)' will return a string that when eval'd will result
  665. // in the same object. This is used to marshal things between threads and
  666. // across machines.
  667. //
  668. // This was stolen from the Scripting team's RemoteScripting implementation
  669. //
  670. // ---------------------------------------------------------------------------
  671. //*****************************************************************
  672. // function uneval(obj)
  673. // This function takes a given jscript object and creates a
  674. // string representing the object instance in its current state,
  675. // such that when the string is "evaluated" with the "eval"
  676. // function, the object will be recreated. This function is used
  677. // to to "marshall" jscript objects across the client/server
  678. // boundary.
  679. //
  680. //*****************************************************************
  681. var unevalInitialized;
  682. function unevalGetNextID()
  683. {
  684. return '_o' + PrivateData.objUtil.unevalNextID++;
  685. }
  686. function unevalDecl(obj)
  687. {
  688. var s = '';
  689. switch (typeof(obj))
  690. {
  691. case 'undefined':
  692. case 'boolean':
  693. case 'number':
  694. case 'string':
  695. break;
  696. default:
  697. try
  698. {
  699. if (null != obj && !obj.__mark && 'string' != typeof(obj.__decl))
  700. try
  701. {
  702. // if "obj" is an IDispatch, the call to
  703. // unevalDecl may throw.
  704. // In this case we just need to remove the __mark and move on.
  705. obj.__mark = true;
  706. obj.__decl = unevalGetNextID();
  707. s = obj.__unevalDecl();
  708. delete obj.__mark;
  709. }
  710. catch(ex)
  711. {
  712. LogMsg("uneval exception " + ex); // BUGBUG remove this message
  713. delete obj.__mark;
  714. }
  715. }
  716. catch(ex)
  717. {
  718. // if obj is inaccessible, we don't need to declare anything.
  719. LogMsg("uneval exception inaccessible" + ex); // BUGBUG remove this message
  720. }
  721. break;
  722. }
  723. return s;
  724. }
  725. function unevalInst(obj)
  726. {
  727. var s = '';
  728. switch (typeof(obj))
  729. {
  730. case 'undefined':
  731. s = 'undefined';
  732. break;
  733. case 'boolean':
  734. case 'number':
  735. s = String(obj);
  736. break;
  737. case 'string':
  738. s = UnevalString(obj);
  739. break;
  740. default:
  741. try
  742. {
  743. if (null == obj)
  744. s = 'null';
  745. else if (obj.__mark)
  746. s = '"ERROR: Cycle in uneval graph."';
  747. else
  748. try
  749. {
  750. // if "obj" is an IDispatch, the call to
  751. // unevalDecl may throw.
  752. // In this case we need to remove the
  753. // __mark and supply a value (null).
  754. obj.__mark = true;
  755. s = obj.__unevalInst();
  756. delete obj.__mark;
  757. }
  758. catch(ex)
  759. {
  760. s = 'null';
  761. delete obj.__mark;
  762. LogMsg("unevalInst exception " + ex); // BUGBUG remove this message
  763. }
  764. }
  765. catch(ex)
  766. {
  767. // if "obj" is inacessible, say so.
  768. s = UnevalString('inaccessible object');
  769. LogMsg("unevalInst exception inaccessible" + ex); // BUGBUG remove this message
  770. }
  771. break;
  772. }
  773. return s;
  774. }
  775. function unevalClear(obj)
  776. {
  777. switch (typeof(obj))
  778. {
  779. case 'undefined':
  780. case 'boolean':
  781. case 'number':
  782. case 'string':
  783. break;
  784. default:
  785. if (null != obj && !obj.__mark && 'string' == typeof(obj.__decl))
  786. {
  787. obj.__mark = true;
  788. try
  789. {
  790. // if "obj" is an IDispatch, the call to
  791. // unevalClear may throw.
  792. // In this case we need to remove the
  793. // __mark.
  794. obj.__unevalClear();
  795. }
  796. catch(ex)
  797. {
  798. LogMsg("unevalClear exception " + ex); // BUGBUG remove this message
  799. }
  800. delete obj.__mark;
  801. }
  802. break;
  803. }
  804. return ;
  805. }
  806. function unevalDoNothing()
  807. { }
  808. function unevalConvertToString(obj)
  809. { return String(obj); }
  810. /*
  811. Reimplemented in the host interface for performance.
  812. function unevalString(str)
  813. {
  814. //TODO does this function kill performance?
  815. var i;
  816. var newstr = '"';
  817. var c;
  818. for(i = 0; i < str.length; ++i)
  819. {
  820. c = str.charAt(i);
  821. switch(c)
  822. {
  823. case'\\':
  824. newstr += "\\\\";
  825. break;
  826. case '"':
  827. newstr += '\\"';
  828. break;
  829. case "'":
  830. newstr += "\\'";
  831. break;
  832. case "\n":
  833. newstr += "\\n";
  834. break;
  835. case "\r":
  836. newstr += "\\r";
  837. break;
  838. case "\t":
  839. newstr += "\\t";
  840. break;
  841. default:
  842. newstr += c;
  843. break;
  844. }
  845. }
  846. return newstr + '"';
  847. }
  848. */
  849. //*****************************************************************
  850. // function initUneval()
  851. //
  852. // This function sets up the prototype __uneval functions that
  853. // are used to support uneval function for all data types.
  854. //
  855. //*****************************************************************
  856. function initUneval()
  857. {
  858. // instrinsic objects
  859. Object.__unevalDecl = unevalDoNothing;
  860. Object.__unevalInst = function () { return 'Object'; }
  861. Object.__unevalClear = unevalDoNothing;
  862. Boolean.__unevalDecl = unevalDoNothing;
  863. Boolean.__unevalInst = function () { return 'Boolean'; }
  864. Boolean.__unevalClear = unevalDoNothing;
  865. Number.__unevalDecl = unevalDoNothing;
  866. Number.__unevalInst = function () { return 'Number'; }
  867. Number.__unevalClear = unevalDoNothing;
  868. String.__unevalDecl = unevalDoNothing;
  869. String.__unevalInst = function () { return 'String'; }
  870. String.__unevalClear = unevalDoNothing;
  871. Date.__unevalDecl = unevalDoNothing;
  872. Date.__unevalInst = function () { return 'Date'; }
  873. Date.__unevalClear = unevalDoNothing;
  874. Function.__unevalDecl = unevalDoNothing;
  875. Function.__unevalInst = function () { return 'Function'; }
  876. Function.__unevalClear = unevalDoNothing;
  877. Array.__unevalDecl = unevalDoNothing;
  878. Array.__unevalInst = function () { return 'Array'; }
  879. Array.__unevalClear = unevalDoNothing;
  880. // object members
  881. Object.prototype.__enumMembers = function(retval,func)
  882. {
  883. var isPublicMember = this.__isPublicMember;
  884. if ('object' == typeof(this.__unevalProperties))
  885. {
  886. var unevalProperties = this.__unevalProperties;
  887. var length = unevalProperties.length;
  888. for (var i = 0; i < length; i++)
  889. {
  890. try
  891. {
  892. if (isPublicMember(unevalProperties[i]))
  893. func(retval, this, unevalProperties[i]);
  894. }
  895. catch(ex)
  896. {
  897. // This happens if accessing "obj.foo" is inaccessible.
  898. // This can happen if "foo" is an object and the thread
  899. // which created "foo" has terminated.
  900. LogMsg("enumMembers(1) caught an exception on member " + i); // BUGBUG remove this message
  901. }
  902. }
  903. }
  904. else
  905. {
  906. for (var i in this)
  907. {
  908. try
  909. {
  910. if (isPublicMember(i))
  911. func(retval, this, i);
  912. }
  913. catch(ex)
  914. {
  915. // This happens if accessing "obj.foo" is inaccessible.
  916. // This can happen if "foo" is an object and the thread
  917. // which created "foo" has terminated.
  918. LogMsg("enumMembers(2) caught an exception on member " + i); // BUGBUG remove this message
  919. }
  920. }
  921. }
  922. }
  923. Object.prototype.__unevalDeclMember = function (retval, obj, member)
  924. {
  925. retval.otherDecl += unevalDecl(obj[member]);
  926. retval.myDecl += obj.__decl + '[' + UnevalString(member) + ']=' + unevalInst(obj[member]) + ';\n';
  927. }
  928. Object.prototype.__unevalDecl = function()
  929. {
  930. var retval = { otherDecl:'', myDecl:'' };
  931. this.__enumMembers(retval, this.__unevalDeclMember);
  932. return retval.otherDecl + 'var ' + this.__decl + '=' + this.__unevalConstructor() + ';\n' + retval.myDecl;
  933. }
  934. Object.prototype.__unevalInst = function ()
  935. { return this.__decl; }
  936. Object.prototype.__unevalClearMember = function(retval, obj, member)
  937. { unevalClear(obj[member]); }
  938. Object.prototype.__unevalClear = function()
  939. {
  940. delete this.__decl;
  941. this.__enumMembers(null, this.__unevalClearMember);
  942. }
  943. Object.prototype.__isPublicMember = function(member)
  944. { return '__' != member.substr(0,2); }
  945. Object.prototype.__unevalConstructor = function ()
  946. { return 'new Object'; }
  947. // overrides for simple types
  948. Boolean.prototype.__unevalConstructor = function()
  949. { return 'new Boolean(' + String(this) + ')'; }
  950. Number.prototype.__unevalConstructor = function()
  951. { return 'new Number(' + String(this) + ')'; }
  952. String.prototype.__unevalConstructor = function()
  953. { return 'new String(' + UnevalString(this) + ')'; }
  954. Date.prototype.__unevalConstructor = function()
  955. { return 'new Date(Date.parse("' + String(this) + '"))'; }
  956. // overrides for function
  957. Function.prototype.__unevalDecl = function()
  958. { return String(this).replace(/function ([^\(]*)/,'function ' + this.__decl); }
  959. // overrides for array
  960. Array.prototype.__unevalDecl = function()
  961. {
  962. var decl = this.__decl;
  963. var r = '';
  964. var s = 'var ' + decl + '= new Array(' + this.length + ');\n';
  965. var length = this.length;
  966. for (var i = 0; i < length; i++)
  967. {
  968. r += unevalDecl(this[i]);
  969. s += decl + '[' + i + ']=' + unevalInst(this[i]) + ';\n';
  970. }
  971. return r + s;
  972. }
  973. } // end of initUneval