PageRenderTime 44ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/paper-rock-scissors/lib/blanket.js

https://github.com/balancerockmedia/random-stuff
JavaScript | 5447 lines | 4506 code | 712 blank | 229 comment | 817 complexity | 9e6337ed3338831689c4c3edc34d2254 MD5 | raw file
  1. /*! blanket - v1.1.5 */
  2. if (typeof QUnit !== 'undefined'){ QUnit.config.autostart = false; }
  3. (function(define){
  4. /*
  5. Copyright (C) 2012 Ariya Hidayat <ariya.hidayat@gmail.com>
  6. Copyright (C) 2012 Mathias Bynens <mathias@qiwi.be>
  7. Copyright (C) 2012 Joost-Wim Boekesteijn <joost-wim@boekesteijn.nl>
  8. Copyright (C) 2012 Kris Kowal <kris.kowal@cixar.com>
  9. Copyright (C) 2012 Yusuke Suzuki <utatane.tea@gmail.com>
  10. Copyright (C) 2012 Arpad Borsos <arpad.borsos@googlemail.com>
  11. Copyright (C) 2011 Ariya Hidayat <ariya.hidayat@gmail.com>
  12. Redistribution and use in source and binary forms, with or without
  13. modification, are permitted provided that the following conditions are met:
  14. * Redistributions of source code must retain the above copyright
  15. notice, this list of conditions and the following disclaimer.
  16. * Redistributions in binary form must reproduce the above copyright
  17. notice, this list of conditions and the following disclaimer in the
  18. documentation and/or other materials provided with the distribution.
  19. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  20. AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  21. IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  22. ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  23. DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  24. (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  25. LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  26. ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  27. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  28. THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29. */
  30. /*jslint bitwise:true plusplus:true */
  31. /*global esprima:true, define:true, exports:true, window: true,
  32. throwError: true, createLiteral: true, generateStatement: true,
  33. parseAssignmentExpression: true, parseBlock: true, parseExpression: true,
  34. parseFunctionDeclaration: true, parseFunctionExpression: true,
  35. parseFunctionSourceElements: true, parseVariableIdentifier: true,
  36. parseLeftHandSideExpression: true,
  37. parseStatement: true, parseSourceElement: true */
  38. (function (root, factory) {
  39. 'use strict';
  40. // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js,
  41. // Rhino, and plain browser loading.
  42. if (typeof define === 'function' && define.amd) {
  43. define(['exports'], factory);
  44. } else if (typeof exports !== 'undefined') {
  45. factory(exports);
  46. } else {
  47. factory((root.esprima = {}));
  48. }
  49. }(this, function (exports) {
  50. 'use strict';
  51. var Token,
  52. TokenName,
  53. Syntax,
  54. PropertyKind,
  55. Messages,
  56. Regex,
  57. source,
  58. strict,
  59. index,
  60. lineNumber,
  61. lineStart,
  62. length,
  63. buffer,
  64. state,
  65. extra;
  66. Token = {
  67. BooleanLiteral: 1,
  68. EOF: 2,
  69. Identifier: 3,
  70. Keyword: 4,
  71. NullLiteral: 5,
  72. NumericLiteral: 6,
  73. Punctuator: 7,
  74. StringLiteral: 8
  75. };
  76. TokenName = {};
  77. TokenName[Token.BooleanLiteral] = 'Boolean';
  78. TokenName[Token.EOF] = '<end>';
  79. TokenName[Token.Identifier] = 'Identifier';
  80. TokenName[Token.Keyword] = 'Keyword';
  81. TokenName[Token.NullLiteral] = 'Null';
  82. TokenName[Token.NumericLiteral] = 'Numeric';
  83. TokenName[Token.Punctuator] = 'Punctuator';
  84. TokenName[Token.StringLiteral] = 'String';
  85. Syntax = {
  86. AssignmentExpression: 'AssignmentExpression',
  87. ArrayExpression: 'ArrayExpression',
  88. BlockStatement: 'BlockStatement',
  89. BinaryExpression: 'BinaryExpression',
  90. BreakStatement: 'BreakStatement',
  91. CallExpression: 'CallExpression',
  92. CatchClause: 'CatchClause',
  93. ConditionalExpression: 'ConditionalExpression',
  94. ContinueStatement: 'ContinueStatement',
  95. DoWhileStatement: 'DoWhileStatement',
  96. DebuggerStatement: 'DebuggerStatement',
  97. EmptyStatement: 'EmptyStatement',
  98. ExpressionStatement: 'ExpressionStatement',
  99. ForStatement: 'ForStatement',
  100. ForInStatement: 'ForInStatement',
  101. FunctionDeclaration: 'FunctionDeclaration',
  102. FunctionExpression: 'FunctionExpression',
  103. Identifier: 'Identifier',
  104. IfStatement: 'IfStatement',
  105. Literal: 'Literal',
  106. LabeledStatement: 'LabeledStatement',
  107. LogicalExpression: 'LogicalExpression',
  108. MemberExpression: 'MemberExpression',
  109. NewExpression: 'NewExpression',
  110. ObjectExpression: 'ObjectExpression',
  111. Program: 'Program',
  112. Property: 'Property',
  113. ReturnStatement: 'ReturnStatement',
  114. SequenceExpression: 'SequenceExpression',
  115. SwitchStatement: 'SwitchStatement',
  116. SwitchCase: 'SwitchCase',
  117. ThisExpression: 'ThisExpression',
  118. ThrowStatement: 'ThrowStatement',
  119. TryStatement: 'TryStatement',
  120. UnaryExpression: 'UnaryExpression',
  121. UpdateExpression: 'UpdateExpression',
  122. VariableDeclaration: 'VariableDeclaration',
  123. VariableDeclarator: 'VariableDeclarator',
  124. WhileStatement: 'WhileStatement',
  125. WithStatement: 'WithStatement'
  126. };
  127. PropertyKind = {
  128. Data: 1,
  129. Get: 2,
  130. Set: 4
  131. };
  132. // Error messages should be identical to V8.
  133. Messages = {
  134. UnexpectedToken: 'Unexpected token %0',
  135. UnexpectedNumber: 'Unexpected number',
  136. UnexpectedString: 'Unexpected string',
  137. UnexpectedIdentifier: 'Unexpected identifier',
  138. UnexpectedReserved: 'Unexpected reserved word',
  139. UnexpectedEOS: 'Unexpected end of input',
  140. NewlineAfterThrow: 'Illegal newline after throw',
  141. InvalidRegExp: 'Invalid regular expression',
  142. UnterminatedRegExp: 'Invalid regular expression: missing /',
  143. InvalidLHSInAssignment: 'Invalid left-hand side in assignment',
  144. InvalidLHSInForIn: 'Invalid left-hand side in for-in',
  145. MultipleDefaultsInSwitch: 'More than one default clause in switch statement',
  146. NoCatchOrFinally: 'Missing catch or finally after try',
  147. UnknownLabel: 'Undefined label \'%0\'',
  148. Redeclaration: '%0 \'%1\' has already been declared',
  149. IllegalContinue: 'Illegal continue statement',
  150. IllegalBreak: 'Illegal break statement',
  151. IllegalReturn: 'Illegal return statement',
  152. StrictModeWith: 'Strict mode code may not include a with statement',
  153. StrictCatchVariable: 'Catch variable may not be eval or arguments in strict mode',
  154. StrictVarName: 'Variable name may not be eval or arguments in strict mode',
  155. StrictParamName: 'Parameter name eval or arguments is not allowed in strict mode',
  156. StrictParamDupe: 'Strict mode function may not have duplicate parameter names',
  157. StrictFunctionName: 'Function name may not be eval or arguments in strict mode',
  158. StrictOctalLiteral: 'Octal literals are not allowed in strict mode.',
  159. StrictDelete: 'Delete of an unqualified identifier in strict mode.',
  160. StrictDuplicateProperty: 'Duplicate data property in object literal not allowed in strict mode',
  161. AccessorDataProperty: 'Object literal may not have data and accessor property with the same name',
  162. AccessorGetSet: 'Object literal may not have multiple get/set accessors with the same name',
  163. StrictLHSAssignment: 'Assignment to eval or arguments is not allowed in strict mode',
  164. StrictLHSPostfix: 'Postfix increment/decrement may not have eval or arguments operand in strict mode',
  165. StrictLHSPrefix: 'Prefix increment/decrement may not have eval or arguments operand in strict mode',
  166. StrictReservedWord: 'Use of future reserved word in strict mode'
  167. };
  168. // See also tools/generate-unicode-regex.py.
  169. Regex = {
  170. NonAsciiIdentifierStart: new RegExp('[\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]'),
  171. NonAsciiIdentifierPart: new RegExp('[\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0300-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u0483-\u0487\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u05d0-\u05ea\u05f0-\u05f2\u0610-\u061a\u0620-\u0669\u066e-\u06d3\u06d5-\u06dc\u06df-\u06e8\u06ea-\u06fc\u06ff\u0710-\u074a\u074d-\u07b1\u07c0-\u07f5\u07fa\u0800-\u082d\u0840-\u085b\u08a0\u08a2-\u08ac\u08e4-\u08fe\u0900-\u0963\u0966-\u096f\u0971-\u0977\u0979-\u097f\u0981-\u0983\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bc-\u09c4\u09c7\u09c8\u09cb-\u09ce\u09d7\u09dc\u09dd\u09df-\u09e3\u09e6-\u09f1\u0a01-\u0a03\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a59-\u0a5c\u0a5e\u0a66-\u0a75\u0a81-\u0a83\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abc-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ad0\u0ae0-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3c-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5c\u0b5d\u0b5f-\u0b63\u0b66-\u0b6f\u0b71\u0b82\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd0\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c58\u0c59\u0c60-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbc-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0cde\u0ce0-\u0ce3\u0ce6-\u0cef\u0cf1\u0cf2\u0d02\u0d03\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d-\u0d44\u0d46-\u0d48\u0d4a-\u0d4e\u0d57\u0d60-\u0d63\u0d66-\u0d6f\u0d7a-\u0d7f\u0d82\u0d83\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e01-\u0e3a\u0e40-\u0e4e\u0e50-\u0e59\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb9\u0ebb-\u0ebd\u0ec0-\u0ec4\u0ec6\u0ec8-\u0ecd\u0ed0-\u0ed9\u0edc-\u0edf\u0f00\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e-\u0f47\u0f49-\u0f6c\u0f71-\u0f84\u0f86-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1049\u1050-\u109d\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u135d-\u135f\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176c\u176e-\u1770\u1772\u1773\u1780-\u17d3\u17d7\u17dc\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1820-\u1877\u1880-\u18aa\u18b0-\u18f5\u1900-\u191c\u1920-\u192b\u1930-\u193b\u1946-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u19d0-\u19d9\u1a00-\u1a1b\u1a20-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1aa7\u1b00-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1bf3\u1c00-\u1c37\u1c40-\u1c49\u1c4d-\u1c7d\u1cd0-\u1cd2\u1cd4-\u1cf6\u1d00-\u1de6\u1dfc-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u200c\u200d\u203f\u2040\u2054\u2071\u207f\u2090-\u209c\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d7f-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2de0-\u2dff\u2e2f\u3005-\u3007\u3021-\u302f\u3031-\u3035\u3038-\u303c\u3041-\u3096\u3099\u309a\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua62b\ua640-\ua66f\ua674-\ua67d\ua67f-\ua697\ua69f-\ua6f1\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua827\ua840-\ua873\ua880-\ua8c4\ua8d0-\ua8d9\ua8e0-\ua8f7\ua8fb\ua900-\ua92d\ua930-\ua953\ua960-\ua97c\ua980-\ua9c0\ua9cf-\ua9d9\uaa00-\uaa36\uaa40-\uaa4d\uaa50-\uaa59\uaa60-\uaa76\uaa7a\uaa7b\uaa80-\uaac2\uaadb-\uaadd\uaae0-\uaaef\uaaf2-\uaaf6\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabea\uabec\uabed\uabf0-\uabf9\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\ufe70-\ufe74\ufe76-\ufefc\uff10-\uff19\uff21-\uff3a\uff3f\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]')
  172. };
  173. // Ensure the condition is true, otherwise throw an error.
  174. // This is only to have a better contract semantic, i.e. another safety net
  175. // to catch a logic error. The condition shall be fulfilled in normal case.
  176. // Do NOT use this to enforce a certain condition on any user input.
  177. function assert(condition, message) {
  178. if (!condition) {
  179. throw new Error('ASSERT: ' + message);
  180. }
  181. }
  182. function sliceSource(from, to) {
  183. return source.slice(from, to);
  184. }
  185. if (typeof 'esprima'[0] === 'undefined') {
  186. sliceSource = function sliceArraySource(from, to) {
  187. return source.slice(from, to).join('');
  188. };
  189. }
  190. function isDecimalDigit(ch) {
  191. return '0123456789'.indexOf(ch) >= 0;
  192. }
  193. function isHexDigit(ch) {
  194. return '0123456789abcdefABCDEF'.indexOf(ch) >= 0;
  195. }
  196. function isOctalDigit(ch) {
  197. return '01234567'.indexOf(ch) >= 0;
  198. }
  199. // 7.2 White Space
  200. function isWhiteSpace(ch) {
  201. return (ch === ' ') || (ch === '\u0009') || (ch === '\u000B') ||
  202. (ch === '\u000C') || (ch === '\u00A0') ||
  203. (ch.charCodeAt(0) >= 0x1680 &&
  204. '\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\uFEFF'.indexOf(ch) >= 0);
  205. }
  206. // 7.3 Line Terminators
  207. function isLineTerminator(ch) {
  208. return (ch === '\n' || ch === '\r' || ch === '\u2028' || ch === '\u2029');
  209. }
  210. // 7.6 Identifier Names and Identifiers
  211. function isIdentifierStart(ch) {
  212. return (ch === '$') || (ch === '_') || (ch === '\\') ||
  213. (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
  214. ((ch.charCodeAt(0) >= 0x80) && Regex.NonAsciiIdentifierStart.test(ch));
  215. }
  216. function isIdentifierPart(ch) {
  217. return (ch === '$') || (ch === '_') || (ch === '\\') ||
  218. (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
  219. ((ch >= '0') && (ch <= '9')) ||
  220. ((ch.charCodeAt(0) >= 0x80) && Regex.NonAsciiIdentifierPart.test(ch));
  221. }
  222. // 7.6.1.2 Future Reserved Words
  223. function isFutureReservedWord(id) {
  224. switch (id) {
  225. // Future reserved words.
  226. case 'class':
  227. case 'enum':
  228. case 'export':
  229. case 'extends':
  230. case 'import':
  231. case 'super':
  232. return true;
  233. }
  234. return false;
  235. }
  236. function isStrictModeReservedWord(id) {
  237. switch (id) {
  238. // Strict Mode reserved words.
  239. case 'implements':
  240. case 'interface':
  241. case 'package':
  242. case 'private':
  243. case 'protected':
  244. case 'public':
  245. case 'static':
  246. case 'yield':
  247. case 'let':
  248. return true;
  249. }
  250. return false;
  251. }
  252. function isRestrictedWord(id) {
  253. return id === 'eval' || id === 'arguments';
  254. }
  255. // 7.6.1.1 Keywords
  256. function isKeyword(id) {
  257. var keyword = false;
  258. switch (id.length) {
  259. case 2:
  260. keyword = (id === 'if') || (id === 'in') || (id === 'do');
  261. break;
  262. case 3:
  263. keyword = (id === 'var') || (id === 'for') || (id === 'new') || (id === 'try');
  264. break;
  265. case 4:
  266. keyword = (id === 'this') || (id === 'else') || (id === 'case') || (id === 'void') || (id === 'with');
  267. break;
  268. case 5:
  269. keyword = (id === 'while') || (id === 'break') || (id === 'catch') || (id === 'throw');
  270. break;
  271. case 6:
  272. keyword = (id === 'return') || (id === 'typeof') || (id === 'delete') || (id === 'switch');
  273. break;
  274. case 7:
  275. keyword = (id === 'default') || (id === 'finally');
  276. break;
  277. case 8:
  278. keyword = (id === 'function') || (id === 'continue') || (id === 'debugger');
  279. break;
  280. case 10:
  281. keyword = (id === 'instanceof');
  282. break;
  283. }
  284. if (keyword) {
  285. return true;
  286. }
  287. switch (id) {
  288. // Future reserved words.
  289. // 'const' is specialized as Keyword in V8.
  290. case 'const':
  291. return true;
  292. // For compatiblity to SpiderMonkey and ES.next
  293. case 'yield':
  294. case 'let':
  295. return true;
  296. }
  297. if (strict && isStrictModeReservedWord(id)) {
  298. return true;
  299. }
  300. return isFutureReservedWord(id);
  301. }
  302. // 7.4 Comments
  303. function skipComment() {
  304. var ch, blockComment, lineComment;
  305. blockComment = false;
  306. lineComment = false;
  307. while (index < length) {
  308. ch = source[index];
  309. if (lineComment) {
  310. ch = source[index++];
  311. if (isLineTerminator(ch)) {
  312. lineComment = false;
  313. if (ch === '\r' && source[index] === '\n') {
  314. ++index;
  315. }
  316. ++lineNumber;
  317. lineStart = index;
  318. }
  319. } else if (blockComment) {
  320. if (isLineTerminator(ch)) {
  321. if (ch === '\r' && source[index + 1] === '\n') {
  322. ++index;
  323. }
  324. ++lineNumber;
  325. ++index;
  326. lineStart = index;
  327. if (index >= length) {
  328. throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
  329. }
  330. } else {
  331. ch = source[index++];
  332. if (index >= length) {
  333. throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
  334. }
  335. if (ch === '*') {
  336. ch = source[index];
  337. if (ch === '/') {
  338. ++index;
  339. blockComment = false;
  340. }
  341. }
  342. }
  343. } else if (ch === '/') {
  344. ch = source[index + 1];
  345. if (ch === '/') {
  346. index += 2;
  347. lineComment = true;
  348. } else if (ch === '*') {
  349. index += 2;
  350. blockComment = true;
  351. if (index >= length) {
  352. throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
  353. }
  354. } else {
  355. break;
  356. }
  357. } else if (isWhiteSpace(ch)) {
  358. ++index;
  359. } else if (isLineTerminator(ch)) {
  360. ++index;
  361. if (ch === '\r' && source[index] === '\n') {
  362. ++index;
  363. }
  364. ++lineNumber;
  365. lineStart = index;
  366. } else {
  367. break;
  368. }
  369. }
  370. }
  371. function scanHexEscape(prefix) {
  372. var i, len, ch, code = 0;
  373. len = (prefix === 'u') ? 4 : 2;
  374. for (i = 0; i < len; ++i) {
  375. if (index < length && isHexDigit(source[index])) {
  376. ch = source[index++];
  377. code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase());
  378. } else {
  379. return '';
  380. }
  381. }
  382. return String.fromCharCode(code);
  383. }
  384. function scanIdentifier() {
  385. var ch, start, id, restore;
  386. ch = source[index];
  387. if (!isIdentifierStart(ch)) {
  388. return;
  389. }
  390. start = index;
  391. if (ch === '\\') {
  392. ++index;
  393. if (source[index] !== 'u') {
  394. return;
  395. }
  396. ++index;
  397. restore = index;
  398. ch = scanHexEscape('u');
  399. if (ch) {
  400. if (ch === '\\' || !isIdentifierStart(ch)) {
  401. return;
  402. }
  403. id = ch;
  404. } else {
  405. index = restore;
  406. id = 'u';
  407. }
  408. } else {
  409. id = source[index++];
  410. }
  411. while (index < length) {
  412. ch = source[index];
  413. if (!isIdentifierPart(ch)) {
  414. break;
  415. }
  416. if (ch === '\\') {
  417. ++index;
  418. if (source[index] !== 'u') {
  419. return;
  420. }
  421. ++index;
  422. restore = index;
  423. ch = scanHexEscape('u');
  424. if (ch) {
  425. if (ch === '\\' || !isIdentifierPart(ch)) {
  426. return;
  427. }
  428. id += ch;
  429. } else {
  430. index = restore;
  431. id += 'u';
  432. }
  433. } else {
  434. id += source[index++];
  435. }
  436. }
  437. // There is no keyword or literal with only one character.
  438. // Thus, it must be an identifier.
  439. if (id.length === 1) {
  440. return {
  441. type: Token.Identifier,
  442. value: id,
  443. lineNumber: lineNumber,
  444. lineStart: lineStart,
  445. range: [start, index]
  446. };
  447. }
  448. if (isKeyword(id)) {
  449. return {
  450. type: Token.Keyword,
  451. value: id,
  452. lineNumber: lineNumber,
  453. lineStart: lineStart,
  454. range: [start, index]
  455. };
  456. }
  457. // 7.8.1 Null Literals
  458. if (id === 'null') {
  459. return {
  460. type: Token.NullLiteral,
  461. value: id,
  462. lineNumber: lineNumber,
  463. lineStart: lineStart,
  464. range: [start, index]
  465. };
  466. }
  467. // 7.8.2 Boolean Literals
  468. if (id === 'true' || id === 'false') {
  469. return {
  470. type: Token.BooleanLiteral,
  471. value: id,
  472. lineNumber: lineNumber,
  473. lineStart: lineStart,
  474. range: [start, index]
  475. };
  476. }
  477. return {
  478. type: Token.Identifier,
  479. value: id,
  480. lineNumber: lineNumber,
  481. lineStart: lineStart,
  482. range: [start, index]
  483. };
  484. }
  485. // 7.7 Punctuators
  486. function scanPunctuator() {
  487. var start = index,
  488. ch1 = source[index],
  489. ch2,
  490. ch3,
  491. ch4;
  492. // Check for most common single-character punctuators.
  493. if (ch1 === ';' || ch1 === '{' || ch1 === '}') {
  494. ++index;
  495. return {
  496. type: Token.Punctuator,
  497. value: ch1,
  498. lineNumber: lineNumber,
  499. lineStart: lineStart,
  500. range: [start, index]
  501. };
  502. }
  503. if (ch1 === ',' || ch1 === '(' || ch1 === ')') {
  504. ++index;
  505. return {
  506. type: Token.Punctuator,
  507. value: ch1,
  508. lineNumber: lineNumber,
  509. lineStart: lineStart,
  510. range: [start, index]
  511. };
  512. }
  513. // Dot (.) can also start a floating-point number, hence the need
  514. // to check the next character.
  515. ch2 = source[index + 1];
  516. if (ch1 === '.' && !isDecimalDigit(ch2)) {
  517. return {
  518. type: Token.Punctuator,
  519. value: source[index++],
  520. lineNumber: lineNumber,
  521. lineStart: lineStart,
  522. range: [start, index]
  523. };
  524. }
  525. // Peek more characters.
  526. ch3 = source[index + 2];
  527. ch4 = source[index + 3];
  528. // 4-character punctuator: >>>=
  529. if (ch1 === '>' && ch2 === '>' && ch3 === '>') {
  530. if (ch4 === '=') {
  531. index += 4;
  532. return {
  533. type: Token.Punctuator,
  534. value: '>>>=',
  535. lineNumber: lineNumber,
  536. lineStart: lineStart,
  537. range: [start, index]
  538. };
  539. }
  540. }
  541. // 3-character punctuators: === !== >>> <<= >>=
  542. if (ch1 === '=' && ch2 === '=' && ch3 === '=') {
  543. index += 3;
  544. return {
  545. type: Token.Punctuator,
  546. value: '===',
  547. lineNumber: lineNumber,
  548. lineStart: lineStart,
  549. range: [start, index]
  550. };
  551. }
  552. if (ch1 === '!' && ch2 === '=' && ch3 === '=') {
  553. index += 3;
  554. return {
  555. type: Token.Punctuator,
  556. value: '!==',
  557. lineNumber: lineNumber,
  558. lineStart: lineStart,
  559. range: [start, index]
  560. };
  561. }
  562. if (ch1 === '>' && ch2 === '>' && ch3 === '>') {
  563. index += 3;
  564. return {
  565. type: Token.Punctuator,
  566. value: '>>>',
  567. lineNumber: lineNumber,
  568. lineStart: lineStart,
  569. range: [start, index]
  570. };
  571. }
  572. if (ch1 === '<' && ch2 === '<' && ch3 === '=') {
  573. index += 3;
  574. return {
  575. type: Token.Punctuator,
  576. value: '<<=',
  577. lineNumber: lineNumber,
  578. lineStart: lineStart,
  579. range: [start, index]
  580. };
  581. }
  582. if (ch1 === '>' && ch2 === '>' && ch3 === '=') {
  583. index += 3;
  584. return {
  585. type: Token.Punctuator,
  586. value: '>>=',
  587. lineNumber: lineNumber,
  588. lineStart: lineStart,
  589. range: [start, index]
  590. };
  591. }
  592. // 2-character punctuators: <= >= == != ++ -- << >> && ||
  593. // += -= *= %= &= |= ^= /=
  594. if (ch2 === '=') {
  595. if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) {
  596. index += 2;
  597. return {
  598. type: Token.Punctuator,
  599. value: ch1 + ch2,
  600. lineNumber: lineNumber,
  601. lineStart: lineStart,
  602. range: [start, index]
  603. };
  604. }
  605. }
  606. if (ch1 === ch2 && ('+-<>&|'.indexOf(ch1) >= 0)) {
  607. if ('+-<>&|'.indexOf(ch2) >= 0) {
  608. index += 2;
  609. return {
  610. type: Token.Punctuator,
  611. value: ch1 + ch2,
  612. lineNumber: lineNumber,
  613. lineStart: lineStart,
  614. range: [start, index]
  615. };
  616. }
  617. }
  618. // The remaining 1-character punctuators.
  619. if ('[]<>+-*%&|^!~?:=/'.indexOf(ch1) >= 0) {
  620. return {
  621. type: Token.Punctuator,
  622. value: source[index++],
  623. lineNumber: lineNumber,
  624. lineStart: lineStart,
  625. range: [start, index]
  626. };
  627. }
  628. }
  629. // 7.8.3 Numeric Literals
  630. function scanNumericLiteral() {
  631. var number, start, ch;
  632. ch = source[index];
  633. assert(isDecimalDigit(ch) || (ch === '.'),
  634. 'Numeric literal must start with a decimal digit or a decimal point');
  635. start = index;
  636. number = '';
  637. if (ch !== '.') {
  638. number = source[index++];
  639. ch = source[index];
  640. // Hex number starts with '0x'.
  641. // Octal number starts with '0'.
  642. if (number === '0') {
  643. if (ch === 'x' || ch === 'X') {
  644. number += source[index++];
  645. while (index < length) {
  646. ch = source[index];
  647. if (!isHexDigit(ch)) {
  648. break;
  649. }
  650. number += source[index++];
  651. }
  652. if (number.length <= 2) {
  653. // only 0x
  654. throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
  655. }
  656. if (index < length) {
  657. ch = source[index];
  658. if (isIdentifierStart(ch)) {
  659. throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
  660. }
  661. }
  662. return {
  663. type: Token.NumericLiteral,
  664. value: parseInt(number, 16),
  665. lineNumber: lineNumber,
  666. lineStart: lineStart,
  667. range: [start, index]
  668. };
  669. } else if (isOctalDigit(ch)) {
  670. number += source[index++];
  671. while (index < length) {
  672. ch = source[index];
  673. if (!isOctalDigit(ch)) {
  674. break;
  675. }
  676. number += source[index++];
  677. }
  678. if (index < length) {
  679. ch = source[index];
  680. if (isIdentifierStart(ch) || isDecimalDigit(ch)) {
  681. throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
  682. }
  683. }
  684. return {
  685. type: Token.NumericLiteral,
  686. value: parseInt(number, 8),
  687. octal: true,
  688. lineNumber: lineNumber,
  689. lineStart: lineStart,
  690. range: [start, index]
  691. };
  692. }
  693. // decimal number starts with '0' such as '09' is illegal.
  694. if (isDecimalDigit(ch)) {
  695. throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
  696. }
  697. }
  698. while (index < length) {
  699. ch = source[index];
  700. if (!isDecimalDigit(ch)) {
  701. break;
  702. }
  703. number += source[index++];
  704. }
  705. }
  706. if (ch === '.') {
  707. number += source[index++];
  708. while (index < length) {
  709. ch = source[index];
  710. if (!isDecimalDigit(ch)) {
  711. break;
  712. }
  713. number += source[index++];
  714. }
  715. }
  716. if (ch === 'e' || ch === 'E') {
  717. number += source[index++];
  718. ch = source[index];
  719. if (ch === '+' || ch === '-') {
  720. number += source[index++];
  721. }
  722. ch = source[index];
  723. if (isDecimalDigit(ch)) {
  724. number += source[index++];
  725. while (index < length) {
  726. ch = source[index];
  727. if (!isDecimalDigit(ch)) {
  728. break;
  729. }
  730. number += source[index++];
  731. }
  732. } else {
  733. ch = 'character ' + ch;
  734. if (index >= length) {
  735. ch = '<end>';
  736. }
  737. throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
  738. }
  739. }
  740. if (index < length) {
  741. ch = source[index];
  742. if (isIdentifierStart(ch)) {
  743. throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
  744. }
  745. }
  746. return {
  747. type: Token.NumericLiteral,
  748. value: parseFloat(number),
  749. lineNumber: lineNumber,
  750. lineStart: lineStart,
  751. range: [start, index]
  752. };
  753. }
  754. // 7.8.4 String Literals
  755. function scanStringLiteral() {
  756. var str = '', quote, start, ch, code, unescaped, restore, octal = false;
  757. quote = source[index];
  758. assert((quote === '\'' || quote === '"'),
  759. 'String literal must starts with a quote');
  760. start = index;
  761. ++index;
  762. while (index < length) {
  763. ch = source[index++];
  764. if (ch === quote) {
  765. quote = '';
  766. break;
  767. } else if (ch === '\\') {
  768. ch = source[index++];
  769. if (!isLineTerminator(ch)) {
  770. switch (ch) {
  771. case 'n':
  772. str += '\n';
  773. break;
  774. case 'r':
  775. str += '\r';
  776. break;
  777. case 't':
  778. str += '\t';
  779. break;
  780. case 'u':
  781. case 'x':
  782. restore = index;
  783. unescaped = scanHexEscape(ch);
  784. if (unescaped) {
  785. str += unescaped;
  786. } else {
  787. index = restore;
  788. str += ch;
  789. }
  790. break;
  791. case 'b':
  792. str += '\b';
  793. break;
  794. case 'f':
  795. str += '\f';
  796. break;
  797. case 'v':
  798. str += '\x0B';
  799. break;
  800. default:
  801. if (isOctalDigit(ch)) {
  802. code = '01234567'.indexOf(ch);
  803. // \0 is not octal escape sequence
  804. if (code !== 0) {
  805. octal = true;
  806. }
  807. if (index < length && isOctalDigit(source[index])) {
  808. octal = true;
  809. code = code * 8 + '01234567'.indexOf(source[index++]);
  810. // 3 digits are only allowed when string starts
  811. // with 0, 1, 2, 3
  812. if ('0123'.indexOf(ch) >= 0 &&
  813. index < length &&
  814. isOctalDigit(source[index])) {
  815. code = code * 8 + '01234567'.indexOf(source[index++]);
  816. }
  817. }
  818. str += String.fromCharCode(code);
  819. } else {
  820. str += ch;
  821. }
  822. break;
  823. }
  824. } else {
  825. ++lineNumber;
  826. if (ch === '\r' && source[index] === '\n') {
  827. ++index;
  828. }
  829. }
  830. } else if (isLineTerminator(ch)) {
  831. break;
  832. } else {
  833. str += ch;
  834. }
  835. }
  836. if (quote !== '') {
  837. throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
  838. }
  839. return {
  840. type: Token.StringLiteral,
  841. value: str,
  842. octal: octal,
  843. lineNumber: lineNumber,
  844. lineStart: lineStart,
  845. range: [start, index]
  846. };
  847. }
  848. function scanRegExp() {
  849. var str, ch, start, pattern, flags, value, classMarker = false, restore, terminated = false;
  850. buffer = null;
  851. skipComment();
  852. start = index;
  853. ch = source[index];
  854. assert(ch === '/', 'Regular expression literal must start with a slash');
  855. str = source[index++];
  856. while (index < length) {
  857. ch = source[index++];
  858. str += ch;
  859. if (ch === '\\') {
  860. ch = source[index++];
  861. // ECMA-262 7.8.5
  862. if (isLineTerminator(ch)) {
  863. throwError({}, Messages.UnterminatedRegExp);
  864. }
  865. str += ch;
  866. } else if (classMarker) {
  867. if (ch === ']') {
  868. classMarker = false;
  869. }
  870. } else {
  871. if (ch === '/') {
  872. terminated = true;
  873. break;
  874. } else if (ch === '[') {
  875. classMarker = true;
  876. } else if (isLineTerminator(ch)) {
  877. throwError({}, Messages.UnterminatedRegExp);
  878. }
  879. }
  880. }
  881. if (!terminated) {
  882. throwError({}, Messages.UnterminatedRegExp);
  883. }
  884. // Exclude leading and trailing slash.
  885. pattern = str.substr(1, str.length - 2);
  886. flags = '';
  887. while (index < length) {
  888. ch = source[index];
  889. if (!isIdentifierPart(ch)) {
  890. break;
  891. }
  892. ++index;
  893. if (ch === '\\' && index < length) {
  894. ch = source[index];
  895. if (ch === 'u') {
  896. ++index;
  897. restore = index;
  898. ch = scanHexEscape('u');
  899. if (ch) {
  900. flags += ch;
  901. str += '\\u';
  902. for (; restore < index; ++restore) {
  903. str += source[restore];
  904. }
  905. } else {
  906. index = restore;
  907. flags += 'u';
  908. str += '\\u';
  909. }
  910. } else {
  911. str += '\\';
  912. }
  913. } else {
  914. flags += ch;
  915. str += ch;
  916. }
  917. }
  918. try {
  919. value = new RegExp(pattern, flags);
  920. } catch (e) {
  921. throwError({}, Messages.InvalidRegExp);
  922. }
  923. return {
  924. literal: str,
  925. value: value,
  926. range: [start, index]
  927. };
  928. }
  929. function isIdentifierName(token) {
  930. return token.type === Token.Identifier ||
  931. token.type === Token.Keyword ||
  932. token.type === Token.BooleanLiteral ||
  933. token.type === Token.NullLiteral;
  934. }
  935. function advance() {
  936. var ch, token;
  937. skipComment();
  938. if (index >= length) {
  939. return {
  940. type: Token.EOF,
  941. lineNumber: lineNumber,
  942. lineStart: lineStart,
  943. range: [index, index]
  944. };
  945. }
  946. token = scanPunctuator();
  947. if (typeof token !== 'undefined') {
  948. return token;
  949. }
  950. ch = source[index];
  951. if (ch === '\'' || ch === '"') {
  952. return scanStringLiteral();
  953. }
  954. if (ch === '.' || isDecimalDigit(ch)) {
  955. return scanNumericLiteral();
  956. }
  957. token = scanIdentifier();
  958. if (typeof token !== 'undefined') {
  959. return token;
  960. }
  961. throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
  962. }
  963. function lex() {
  964. var token;
  965. if (buffer) {
  966. index = buffer.range[1];
  967. lineNumber = buffer.lineNumber;
  968. lineStart = buffer.lineStart;
  969. token = buffer;
  970. buffer = null;
  971. return token;
  972. }
  973. buffer = null;
  974. return advance();
  975. }
  976. function lookahead() {
  977. var pos, line, start;
  978. if (buffer !== null) {
  979. return buffer;
  980. }
  981. pos = index;
  982. line = lineNumber;
  983. start = lineStart;
  984. buffer = advance();
  985. index = pos;
  986. lineNumber = line;
  987. lineStart = start;
  988. return buffer;
  989. }
  990. // Return true if there is a line terminator before the next token.
  991. function peekLineTerminator() {
  992. var pos, line, start, found;
  993. pos = index;
  994. line = lineNumber;
  995. start = lineStart;
  996. skipComment();
  997. found = lineNumber !== line;
  998. index = pos;
  999. lineNumber = line;
  1000. lineStart = start;
  1001. return found;
  1002. }
  1003. // Throw an exception
  1004. function throwError(token, messageFormat) {
  1005. var error,
  1006. args = Array.prototype.slice.call(arguments, 2),
  1007. msg = messageFormat.replace(
  1008. /%(\d)/g,
  1009. function (whole, index) {
  1010. return args[index] || '';
  1011. }
  1012. );
  1013. if (typeof token.lineNumber === 'number') {
  1014. error = new Error('Line ' + token.lineNumber + ': ' + msg);
  1015. error.index = token.range[0];
  1016. error.lineNumber = token.lineNumber;
  1017. error.column = token.range[0] - lineStart + 1;
  1018. } else {
  1019. error = new Error('Line ' + lineNumber + ': ' + msg);
  1020. error.index = index;
  1021. error.lineNumber = lineNumber;
  1022. error.column = index - lineStart + 1;
  1023. }
  1024. throw error;
  1025. }
  1026. function throwErrorTolerant() {
  1027. try {
  1028. throwError.apply(null, arguments);
  1029. } catch (e) {
  1030. if (extra.errors) {
  1031. extra.errors.push(e);
  1032. } else {
  1033. throw e;
  1034. }
  1035. }
  1036. }
  1037. // Throw an exception because of the token.
  1038. function throwUnexpected(token) {
  1039. if (token.type === Token.EOF) {
  1040. throwError(token, Messages.UnexpectedEOS);
  1041. }
  1042. if (token.type === Token.NumericLiteral) {
  1043. throwError(token, Messages.UnexpectedNumber);
  1044. }
  1045. if (token.type === Token.StringLiteral) {
  1046. throwError(token, Messages.UnexpectedString);
  1047. }
  1048. if (token.type === Token.Identifier) {
  1049. throwError(token, Messages.UnexpectedIdentifier);
  1050. }
  1051. if (token.type === Token.Keyword) {
  1052. if (isFutureReservedWord(token.value)) {
  1053. throwError(token, Messages.UnexpectedReserved);
  1054. } else if (strict && isStrictModeReservedWord(token.value)) {
  1055. throwErrorTolerant(token, Messages.StrictReservedWord);
  1056. return;
  1057. }
  1058. throwError(token, Messages.UnexpectedToken, token.value);
  1059. }
  1060. // BooleanLiteral, NullLiteral, or Punctuator.
  1061. throwError(token, Messages.UnexpectedToken, token.value);
  1062. }
  1063. // Expect the next token to match the specified punctuator.
  1064. // If not, an exception will be thrown.
  1065. function expect(value) {
  1066. var token = lex();
  1067. if (token.type !== Token.Punctuator || token.value !== value) {
  1068. throwUnexpected(token);
  1069. }
  1070. }
  1071. // Expect the next token to match the specified keyword.
  1072. // If not, an exception will be thrown.
  1073. function expectKeyword(keyword) {
  1074. var token = lex();
  1075. if (token.type !== Token.Keyword || token.value !== keyword) {
  1076. throwUnexpected(token);
  1077. }
  1078. }
  1079. // Return true if the next token matches the specified punctuator.
  1080. function match(value) {
  1081. var token = lookahead();
  1082. return token.type === Token.Punctuator && token.value === value;
  1083. }
  1084. // Return true if the next token matches the specified keyword
  1085. function matchKeyword(keyword) {
  1086. var token = lookahead();
  1087. return token.type === Token.Keyword && token.value === keyword;
  1088. }
  1089. // Return true if the next token is an assignment operator
  1090. function matchAssign() {
  1091. var token = lookahead(),
  1092. op = token.value;
  1093. if (token.type !== Token.Punctuator) {
  1094. return false;
  1095. }
  1096. return op === '=' ||
  1097. op === '*=' ||
  1098. op === '/=' ||
  1099. op === '%=' ||
  1100. op === '+=' ||
  1101. op === '-=' ||
  1102. op === '<<=' ||
  1103. op === '>>=' ||
  1104. op === '>>>=' ||
  1105. op === '&=' ||
  1106. op === '^=' ||
  1107. op === '|=';
  1108. }
  1109. function consumeSemicolon() {
  1110. var token, line;
  1111. // Catch the very common case first.
  1112. if (source[index] === ';') {
  1113. lex();
  1114. return;
  1115. }
  1116. line = lineNumber;
  1117. skipComment();
  1118. if (lineNumber !== line) {
  1119. return;
  1120. }
  1121. if (match(';')) {
  1122. lex();
  1123. return;
  1124. }
  1125. token = lookahead();
  1126. if (token.type !== Token.EOF && !match('}')) {
  1127. throwUnexpected(token);
  1128. }
  1129. }
  1130. // Return true if provided expression is LeftHandSideExpression
  1131. function isLeftHandSide(expr) {
  1132. return expr.type === Syntax.Identifier || expr.type === Syntax.MemberExpression;
  1133. }
  1134. // 11.1.4 Array Initialiser
  1135. function parseArrayInitialiser() {
  1136. var elements = [];
  1137. expect('[');
  1138. while (!match(']')) {
  1139. if (match(',')) {
  1140. lex();
  1141. elements.push(null);
  1142. } else {
  1143. elements.push(parseAssignmentExpression());
  1144. if (!match(']')) {
  1145. expect(',');
  1146. }
  1147. }
  1148. }
  1149. expect(']');
  1150. return {
  1151. type: Syntax.ArrayExpression,
  1152. elements: elements
  1153. };
  1154. }
  1155. // 11.1.5 Object Initialiser
  1156. function parsePropertyFunction(param, first) {
  1157. var previousStrict, body;
  1158. previousStrict = strict;
  1159. body = parseFunctionSourceElements();
  1160. if (first && strict && isRestrictedWord(param[0].name)) {
  1161. throwErrorTolerant(first, Messages.StrictParamName);
  1162. }
  1163. strict = previousStrict;
  1164. return {
  1165. type: Syntax.FunctionExpression,
  1166. id: null,
  1167. params: param,
  1168. defaults: [],
  1169. body: body,
  1170. rest: null,
  1171. generator: false,
  1172. expression: false
  1173. };
  1174. }
  1175. function parseObjectPropertyKey() {
  1176. var token = lex();
  1177. // Note: This function is called only from parseObjectProperty(), where
  1178. // EOF and Punctuator tokens are already filtered out.
  1179. if (token.type === Token.StringLiteral || token.type === Token.NumericLiteral) {
  1180. if (strict && token.octal) {
  1181. throwErrorTolerant(token, Messages.StrictOctalLiteral);
  1182. }
  1183. return createLiteral(token);
  1184. }
  1185. return {
  1186. type: Syntax.Identifier,
  1187. name: token.value
  1188. };
  1189. }
  1190. function parseObjectProperty() {
  1191. var token, key, id, param;
  1192. token = lookahead();
  1193. if (token.type === Token.Identifier) {
  1194. id = parseObjectPropertyKey();
  1195. // Property Assignment: Getter and Setter.
  1196. if (token.value === 'get' && !match(':')) {
  1197. key = parseObjectPropertyKey();
  1198. expect('(');
  1199. expect(')');
  1200. return {
  1201. type: Syntax.Property,
  1202. key: key,
  1203. value: parsePropertyFunction([]),
  1204. kind: 'get'
  1205. };
  1206. } else if (token.value === 'set' && !match(':')) {
  1207. key = parseObjectPropertyKey();
  1208. expect('(');
  1209. token = lookahead();
  1210. if (token.type !== Token.Identifier) {
  1211. expect(')');
  1212. throwErrorTolerant(token, Messages.UnexpectedToken, token.value);
  1213. return {
  1214. type: Syntax.Property,
  1215. key: key,
  1216. value: parsePropertyFunction([]),
  1217. kind: 'set'
  1218. };
  1219. } else {
  1220. param = [ parseVariableIdentifier() ];
  1221. expect(')');
  1222. return {
  1223. type: Syntax.Property,
  1224. key: key,
  1225. value: parsePropertyFunction(param, token),
  1226. kind: 'set'
  1227. };
  1228. }
  1229. } else {
  1230. expect(':');
  1231. return {
  1232. type: Syntax.Property,
  1233. key: id,
  1234. value: parseAssignmentExpression(),
  1235. kind: 'init'
  1236. };
  1237. }
  1238. } else if (token.type === Token.EOF || token.type === Token.Punctuator) {
  1239. throwUnexpected(token);
  1240. } else {
  1241. key = parseObjectPropertyKey();
  1242. expect(':');
  1243. return {
  1244. type: Syntax.Property,
  1245. key: key,
  1246. value: parseAssignmentExpression(),
  1247. kind: 'init'
  1248. };
  1249. }
  1250. }
  1251. function parseObjectInitialiser() {
  1252. var properties = [], property, name, kind, map = {}, toString = String;
  1253. expect('{');
  1254. while (!match('}')) {
  1255. property = parseObjectProperty();
  1256. if (property.key.type === Syntax.Identifier) {
  1257. name = property.key.name;
  1258. } else {
  1259. name = toString(property.key.value);
  1260. }
  1261. kind = (property.kind === 'init') ? PropertyKind.Data : (property.kind === 'get') ? PropertyKind.Get : PropertyKind.Set;
  1262. if (Object.prototype.hasOwnProperty.call(map, name)) {
  1263. if (map[name] === PropertyKind.Data) {
  1264. if (strict && kind === PropertyKind.Data) {
  1265. throwErrorTolerant({}, Messages.StrictDuplicateProperty);
  1266. } else if (kind !== PropertyKind.Data) {
  1267. throwErrorTolerant({}, Messages.AccessorDataProperty);
  1268. }
  1269. } else {
  1270. if (kind === PropertyKind.Data) {
  1271. throwErrorTolerant({}, Messages.AccessorDataProperty);
  1272. } else if (map[name] & kind) {
  1273. throwErrorTolerant({}, Messages.AccessorGetSet);
  1274. }
  1275. }
  1276. map[name] |= kind;
  1277. } else {
  1278. map[name] = kind;
  1279. }
  1280. properties.push(property);
  1281. if (!match('}')) {
  1282. expect(',');
  1283. }
  1284. }
  1285. expect('}');
  1286. return {
  1287. type: Syntax.ObjectExpression,
  1288. properties: properties
  1289. };
  1290. }
  1291. // 11.1.6 The Grouping Operator
  1292. function parseGroupExpression() {
  1293. var expr;
  1294. expect('(');
  1295. expr = parseExpression();
  1296. expect(')');
  1297. return expr;
  1298. }
  1299. // 11.1 Primary Expressions
  1300. function parsePrimaryExpression() {
  1301. var token = lookahead(),
  1302. type = token.type;
  1303. if (type === Token.Identifier) {
  1304. return {
  1305. type: Syntax.Identifier,
  1306. name: lex().value
  1307. };
  1308. }
  1309. if (type === Token.StringLiteral || type === Token.NumericLiteral) {
  1310. if (strict && token.octal) {
  1311. throwErrorTolerant(token, Messages.StrictOctalLiteral);
  1312. }
  1313. return createLiteral(lex());
  1314. }
  1315. if (type === Token.Keyword) {
  1316. if (matchKeyword('this')) {
  1317. lex();
  1318. return {
  1319. type: Syntax.ThisExpression
  1320. };
  1321. }
  1322. if (matchKeyword('function')) {
  1323. return parseFunctionExpression();
  1324. }
  1325. }
  1326. if (type === Token.BooleanLiteral) {
  1327. lex();
  1328. token.value = (token.value === 'true');
  1329. return createLiteral(token);
  1330. }
  1331. if (type === Token.NullLiteral) {
  1332. lex();
  1333. token.value = null;
  1334. return createLiteral(token);
  1335. }
  1336. if (match('[')) {
  1337. return parseArrayInitialiser();
  1338. }
  1339. if (match('{')) {
  1340. return parseObjectInitialiser();
  1341. }
  1342. if (match('(')) {
  1343. return parseGroupExpression();
  1344. }
  1345. if (match('/') || match('/=')) {
  1346. return createLiteral(scanRegExp());
  1347. }
  1348. return throwUnexpected(lex());
  1349. }
  1350. // 11.2 Left-Hand-Side Expressions
  1351. function parseArguments() {
  1352. var args = [];
  1353. expect('(');
  1354. if (!match(')')) {
  1355. while (index < length) {
  1356. args.push(parseAssignmentExpression());
  1357. if (match(')')) {
  1358. break;
  1359. }
  1360. expect(',');
  1361. }
  1362. }
  1363. expect(')');
  1364. return args;
  1365. }
  1366. function parseNonComputedProperty() {
  1367. var token = lex();
  1368. if (!isIdentifierName(token)) {
  1369. throwUnexpected(token);
  1370. }
  1371. return {
  1372. type: Syntax.Identifier,
  1373. name: token.value
  1374. };
  1375. }
  1376. function parseNonComputedMember() {
  1377. expect('.');
  1378. return parseNonComputedProperty();
  1379. }
  1380. function parseComputedMember() {
  1381. var expr;
  1382. expect('[');
  1383. expr = parseExpression();
  1384. expect(']');
  1385. return expr;
  1386. }
  1387. function parseNewExpression() {
  1388. var expr;
  1389. expectKeyword('new');
  1390. expr = {
  1391. type: Syntax.NewExpression,
  1392. callee: parseLeftHandSideExpression(),
  1393. 'arguments': []
  1394. };
  1395. if (match('(')) {
  1396. expr['arguments'] = parseArguments();
  1397. }
  1398. return expr;
  1399. }
  1400. function parseLeftHandSideExpressionAllowCall() {
  1401. var expr;
  1402. expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression();
  1403. while (match('.') || match('[') || match('(')) {
  1404. if (match('(')) {
  1405. expr = {
  1406. type: Syntax.CallExpression,
  1407. callee: expr,
  1408. 'arguments': parseArguments()
  1409. };
  1410. } else if (match('[')) {
  1411. expr = {
  1412. type: Syntax.MemberExpression,
  1413. computed: true,
  1414. object: expr,
  1415. property: parseComputedMember()
  1416. };
  1417. } else {
  1418. expr = {
  1419. type: Syntax.MemberExpression,
  1420. computed: false,
  1421. object: expr,
  1422. property: parseNonComputedMember()
  1423. };
  1424. }
  1425. }
  1426. return expr;
  1427. }
  1428. function parseLeftHandSideExpression() {
  1429. var expr;
  1430. expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression();
  1431. while (match('.') || match('[')) {
  1432. if (match('[')) {
  1433. expr = {
  1434. type: Syntax.MemberExpression,
  1435. computed: true,
  1436. object: expr,
  1437. property: parseComputedMember()
  1438. };
  1439. } else {
  1440. expr = {
  1441. type: Syntax.MemberExpression,
  1442. computed: false,
  1443. object: expr,
  1444. property: parseNonComputedMember()
  1445. };
  1446. }
  1447. }
  1448. return expr;
  1449. }
  1450. // 11.3 Postfix Expressions
  1451. function parsePostfixExpression() {
  1452. var expr = parseLeftHandSideExpressionAllowCall(), token;
  1453. token = lookahead();
  1454. if (token.type !== Token.Punctuator) {
  1455. return expr;
  1456. }
  1457. if ((match('++') || match('--')) && !peekLineTerminator()) {
  1458. // 11.3.1, 11.3.2
  1459. if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) {
  1460. throwErrorTolerant({}, Messages.StrictLHSPostfix);
  1461. }
  1462. if (!isLeftHandSide(expr)) {
  1463. throwErrorTolerant({}, Messages.InvalidLHSInAssignment);
  1464. }
  1465. expr = {
  1466. type: Syntax.UpdateExpression,
  1467. operator: lex().value,
  1468. argument: expr,
  1469. prefix: false
  1470. };
  1471. }
  1472. return expr;
  1473. }
  1474. // 11.4 Unary Operators
  1475. function parseUnaryExpression() {
  1476. var token, expr;
  1477. token = lookahead();
  1478. if (token.type !== Token.Punctuator && token.type !== Token.Keyword) {
  1479. return parsePostfixExpression();
  1480. }
  1481. if (match('++') || match('--')) {
  1482. token = lex();
  1483. expr = parseUnaryExpression();
  1484. // 11.4.4, 11.4.5
  1485. if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) {
  1486. throwErrorTolerant({}, Messages.StrictLHSPrefix);
  1487. }
  1488. if (!isLeftHandSide(expr)) {
  1489. throwErrorTolerant({}, Messages.InvalidLHSInAssignment);
  1490. }
  1491. expr = {
  1492. type: Syntax.UpdateExpression,
  1493. operator: token.value,
  1494. argument: expr,
  1495. prefix: true
  1496. };
  1497. return expr;
  1498. }
  1499. if (match('+') || match('-') || match('~') || match('!')) {
  1500. expr = {
  1501. type: Syntax.UnaryExpression,
  1502. operator: lex().value,
  1503. argument: parseUnaryExpression(),
  1504. prefix: true
  1505. };
  1506. return expr;
  1507. }
  1508. if (matchKeyword('delete') || matchKeyword('void') || matchKeyword('typeof')) {
  1509. expr = {
  1510. type: Syntax.UnaryExpression,
  1511. operator: lex().value,
  1512. argument: parseUnaryExpression(),
  1513. prefix: true
  1514. };
  1515. if (strict && expr.operator === 'delete' && expr.argument.type === Syntax.Identifier) {
  1516. throwErrorTolerant({}, Messages.StrictDelete);
  1517. }
  1518. return expr;
  1519. }
  1520. return parsePostfixExpression();
  1521. }
  1522. // 11.5 Multiplicative Operators
  1523. function parseMultiplicativeExpression() {
  1524. var expr = parseUnaryExpression();
  1525. while (match('*') || match('/') || match('%')) {
  1526. expr = {
  1527. type: Syntax.BinaryExpression,
  1528. operator: lex().value,
  1529. left: expr,
  1530. right: parseUnaryExpression()
  1531. };
  1532. }
  1533. return expr;
  1534. }
  1535. // 11.6 Additive Operators
  1536. function parseAdditiveExpression() {
  1537. var expr = parseMultiplicativeExpression();
  1538. while (match('+') || match('-')) {
  1539. expr = {
  1540. type: Syntax.BinaryExpression,
  1541. operator: lex().value,
  1542. left: expr,
  1543. right: parseMultiplicativeExpression()
  1544. };
  1545. }
  1546. return expr;
  1547. }
  1548. // 11.7 Bitwise Shift Operators
  1549. function parseShiftExpression() {
  1550. var expr = parseAdditiveExpression();
  1551. while (match('<<') || match('>>') || match('>>>')) {
  1552. expr = {
  1553. type: Syntax.BinaryExpression,
  1554. operator: lex().value,
  1555. left: expr,
  1556. right: parseAdditiveExpression()
  1557. };
  1558. }
  1559. return expr;
  1560. }
  1561. // 11.8 Relational Operators
  1562. function parseRelationalExpression() {
  1563. var expr, previousAllowIn;
  1564. previousAllowIn = state.allowIn;
  1565. state.allowIn = true;
  1566. expr = parseShiftExpression();
  1567. while (match('<') || match('>') || match('<=') || match('>=') || (previousAllowIn && matchKeyword('in')) || matchKeyword('instanceof')) {
  1568. expr = {
  1569. type: Syntax.BinaryExpression,
  1570. operator: lex().value,
  1571. left: expr,
  1572. right: parseShiftExpression()
  1573. };
  1574. }
  1575. state.allowIn = previousAllowIn;
  1576. return expr;
  1577. }
  1578. // 11.9 Equality Operators
  1579. function parseEqualityExpression() {
  1580. var expr = parseRelationalExpression();
  1581. while (match('==') || match('!=') || match('===') || match('!==')) {
  1582. expr = {
  1583. type: Syntax.BinaryExpression,
  1584. operator: lex().value,
  1585. left: expr,
  1586. right: parseRelationalExpression()
  1587. };
  1588. }
  1589. return expr;
  1590. }
  1591. // 11.10 Binary Bitwise Operators
  1592. function parseBitwiseANDExpression() {
  1593. var expr = parseEqualityExpression();
  1594. while (match('&')) {
  1595. lex();
  1596. expr = {
  1597. type: Syntax.BinaryExpression,
  1598. operator: '&',
  1599. left: expr,
  1600. right: parseEqualityExpression()
  1601. };
  1602. }
  1603. return expr;
  1604. }
  1605. function parseBitwiseXORExpression() {
  1606. var expr = parseBitwiseANDExpression();
  1607. while (match('^')) {
  1608. lex();
  1609. expr = {
  1610. type: Syntax.BinaryExpression,
  1611. operator: '^',
  1612. left: expr,
  1613. right: parseBitwiseANDExpression()
  1614. };
  1615. }
  1616. return expr;
  1617. }
  1618. function parseBitwiseORExpression() {
  1619. var expr = parseBitwiseXORExpression();
  1620. while (match('|')) {
  1621. lex();
  1622. expr = {
  1623. type: Syntax.BinaryExpression,
  1624. operator: '|',
  1625. left: expr,
  1626. right: parseBitwiseXORExpression()
  1627. };
  1628. }
  1629. return expr;
  1630. }
  1631. // 11.11 Binary Logical Operators
  1632. function parseLogicalANDExpression() {
  1633. var expr = parseBitwiseORExpression();
  1634. while (match('&&')) {
  1635. lex();
  1636. expr = {
  1637. type: Syntax.LogicalExpression,
  1638. operator: '&&',
  1639. left: expr,
  1640. right: parseBitwiseORExpression()
  1641. };
  1642. }
  1643. return expr;
  1644. }
  1645. function parseLogicalORExpression() {
  1646. var expr = parseLogicalANDExpression();
  1647. while (match('||')) {
  1648. lex();
  1649. expr = {
  1650. type: Syntax.LogicalExpression,
  1651. operator: '||',
  1652. left: expr,
  1653. right: parseLogicalANDExpression()
  1654. };
  1655. }
  1656. return expr;
  1657. }
  1658. // 11.12 Conditional Operator
  1659. function parseConditionalExpression() {
  1660. var expr, previousAllowIn, consequent;
  1661. expr = parseLogicalORExpression();
  1662. if (match('?')) {
  1663. lex();
  1664. previousAllowIn = state.allowIn;
  1665. state.allowIn = true;
  1666. consequent = parseAssignmentExpression();
  1667. state.allowIn = previousAllowIn;
  1668. expect(':');
  1669. expr = {
  1670. type: Syntax.ConditionalExpression,
  1671. test: expr,
  1672. consequent: consequent,
  1673. alternate: parseAssignmentExpression()
  1674. };
  1675. }
  1676. return expr;
  1677. }
  1678. // 11.13 Assignment Operators
  1679. function parseAssignmentExpression() {
  1680. var token, expr;
  1681. token = lookahead();
  1682. expr = parseConditionalExpression();
  1683. if (matchAssign()) {
  1684. // LeftHandSideExpression
  1685. if (!isLeftHandSide(expr)) {
  1686. throwErrorTolerant({}, Messages.InvalidLHSInAssignment);
  1687. }
  1688. // 11.13.1
  1689. if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) {
  1690. throwErrorTolerant(token, Messages.StrictLHSAssignment);
  1691. }
  1692. expr = {
  1693. type: Syntax.AssignmentExpression,
  1694. operator: lex().value,
  1695. left: expr,
  1696. right: parseAssignmentExpression()
  1697. };
  1698. }
  1699. return expr;
  1700. }
  1701. // 11.14 Comma Operator
  1702. function parseExpression() {
  1703. var expr = parseAssignmentExpression();
  1704. if (match(',')) {
  1705. expr = {
  1706. type: Syntax.SequenceExpression,
  1707. expressions: [ expr ]
  1708. };
  1709. while (index < length) {
  1710. if (!match(',')) {
  1711. break;
  1712. }
  1713. lex();
  1714. expr.expressions.push(parseAssignmentExpression());
  1715. }
  1716. }
  1717. return expr;
  1718. }
  1719. // 12.1 Block
  1720. function parseStatementList() {
  1721. var list = [],
  1722. statement;
  1723. while (index < length) {
  1724. if (match('}')) {
  1725. break;
  1726. }
  1727. statement = parseSourceElement();
  1728. if (typeof statement === 'undefined') {
  1729. break;
  1730. }
  1731. list.push(statement);
  1732. }
  1733. return list;
  1734. }
  1735. function parseBlock() {
  1736. var block;
  1737. expect('{');
  1738. block = parseStatementList();
  1739. expect('}');
  1740. return {
  1741. type: Syntax.BlockStatement,
  1742. body: block
  1743. };
  1744. }
  1745. // 12.2 Variable Statement
  1746. function parseVariableIdentifier() {
  1747. var token = lex();
  1748. if (token.type !== Token.Identifier) {
  1749. throwUnexpected(token);
  1750. }
  1751. return {
  1752. type: Syntax.Identifier,
  1753. name: token.value
  1754. };
  1755. }
  1756. function parseVariableDeclaration(kind) {
  1757. var id = parseVariableIdentifier(),
  1758. init = null;
  1759. // 12.2.1
  1760. if (strict && isRestrictedWord(id.name)) {
  1761. throwErrorTolerant({}, Messages.StrictVarName);
  1762. }
  1763. if (kind === 'const') {
  1764. expect('=');
  1765. init = parseAssignmentExpression();
  1766. } else if (match('=')) {
  1767. lex();
  1768. init = parseAssignmentExpression();
  1769. }
  1770. return {
  1771. type: Syntax.VariableDeclarator,
  1772. id: id,
  1773. init: init
  1774. };
  1775. }
  1776. function parseVariableDeclarationList(kind) {
  1777. var list = [];
  1778. do {
  1779. list.push(parseVariableDeclaration(kind));
  1780. if (!match(',')) {
  1781. break;
  1782. }
  1783. lex();
  1784. } while (index < length);
  1785. return list;
  1786. }
  1787. function parseVariableStatement() {
  1788. var declarations;
  1789. expectKeyword('var');
  1790. declarations = parseVariableDeclarationList();
  1791. consumeSemicolon();
  1792. return {
  1793. type: Syntax.VariableDeclaration,
  1794. declarations: declarations,
  1795. kind: 'var'
  1796. };
  1797. }
  1798. // kind may be `const` or `let`
  1799. // Both are experimental and not in the specification yet.
  1800. // see http://wiki.ecmascript.org/doku.php?id=harmony:const
  1801. // and http://wiki.ecmascript.org/doku.php?id=harmony:let
  1802. function parseConstLetDeclaration(kind) {
  1803. var declarations;
  1804. expectKeyword(kind);
  1805. declarations = parseVariableDeclarationList(kind);
  1806. consumeSemicolon();
  1807. return {
  1808. type: Syntax.VariableDeclaration,
  1809. declarations: declarations,
  1810. kind: kind
  1811. };
  1812. }
  1813. // 12.3 Empty Statement
  1814. function parseEmptyStatement() {
  1815. expect(';');
  1816. return {
  1817. type: Syntax.EmptyStatement
  1818. };
  1819. }
  1820. // 12.4 Expression Statement
  1821. function parseExpressionStatement() {
  1822. var expr = parseExpression();
  1823. consumeSemicolon();
  1824. return {
  1825. type: Syntax.ExpressionStatement,
  1826. expression: expr
  1827. };
  1828. }
  1829. // 12.5 If statement
  1830. function parseIfStatement() {
  1831. var test, consequent, alternate;
  1832. expectKeyword('if');
  1833. expect('(');
  1834. test = parseExpression();
  1835. expect(')');
  1836. consequent = parseStatement();
  1837. if (matchKeyword('else')) {
  1838. lex();
  1839. alternate = parseStatement();
  1840. } else {
  1841. alternate = null;
  1842. }
  1843. return {
  1844. type: Syntax.IfStatement,
  1845. test: test,
  1846. consequent: consequent,
  1847. alternate: alternate
  1848. };
  1849. }
  1850. // 12.6 Iteration Statements
  1851. function parseDoWhileStatement() {
  1852. var body, test, oldInIteration;
  1853. expectKeyword('do');
  1854. oldInIteration = state.inIteration;
  1855. state.inIteration = true;
  1856. body = parseStatement();
  1857. state.inIteration = oldInIteration;
  1858. expectKeyword('while');
  1859. expect('(');
  1860. test = parseExpression();
  1861. expect(')');
  1862. if (match(';')) {
  1863. lex();
  1864. }
  1865. return {
  1866. type: Syntax.DoWhileStatement,
  1867. body: body,
  1868. test: test
  1869. };
  1870. }
  1871. function parseWhileStatement() {
  1872. var test, body, oldInIteration;
  1873. expectKeyword('while');
  1874. expect('(');
  1875. test = parseExpression();
  1876. expect(')');
  1877. oldInIteration = state.inIteration;
  1878. state.inIteration = true;
  1879. body = parseStatement();
  1880. state.inIteration = oldInIteration;
  1881. return {
  1882. type: Syntax.WhileStatement,
  1883. test: test,
  1884. body: body
  1885. };
  1886. }
  1887. function parseForVariableDeclaration() {
  1888. var token = lex();
  1889. return {
  1890. type: Syntax.VariableDeclaration,
  1891. declarations: parseVariableDeclarationList(),
  1892. kind: token.value
  1893. };
  1894. }
  1895. function parseForStatement() {
  1896. var init, test, update, left, right, body, oldInIteration;
  1897. init = test = update = null;
  1898. expectKeyword('for');
  1899. expect('(');
  1900. if (match(';')) {
  1901. lex();
  1902. } else {
  1903. if (matchKeyword('var') || matchKeyword('let')) {
  1904. state.allowIn = false;
  1905. init = parseForVariableDeclaration();
  1906. state.allowIn = true;
  1907. if (init.declarations.length === 1 && matchKeyword('in')) {
  1908. lex();
  1909. left = init;
  1910. right = parseExpression();
  1911. init = null;
  1912. }
  1913. } else {
  1914. state.allowIn = false;
  1915. init = parseExpression();
  1916. state.allowIn = true;
  1917. if (matchKeyword('in')) {
  1918. // LeftHandSideExpression
  1919. if (!isLeftHandSide(init)) {
  1920. throwErrorTolerant({}, Messages.InvalidLHSInForIn);
  1921. }
  1922. lex();
  1923. left = init;
  1924. right = parseExpression();
  1925. init = null;
  1926. }
  1927. }
  1928. if (typeof left === 'undefined') {
  1929. expect(';');
  1930. }
  1931. }
  1932. if (typeof left === 'undefined') {
  1933. if (!match(';')) {
  1934. test = parseExpression();
  1935. }
  1936. expect(';');
  1937. if (!match(')')) {
  1938. update = parseExpression();
  1939. }
  1940. }
  1941. expect(')');
  1942. oldInIteration = state.inIteration;
  1943. state.inIteration = true;
  1944. body = parseStatement();
  1945. state.inIteration = oldInIteration;
  1946. if (typeof left === 'undefined') {
  1947. return {
  1948. type: Syntax.ForStatement,
  1949. init: init,
  1950. test: test,
  1951. update: update,
  1952. body: body
  1953. };
  1954. }
  1955. return {
  1956. type: Syntax.ForInStatement,
  1957. left: left,
  1958. right: right,
  1959. body: body,
  1960. each: false
  1961. };
  1962. }
  1963. // 12.7 The continue statement
  1964. function parseContinueStatement() {
  1965. var token, label = null;
  1966. expectKeyword('continue');
  1967. // Optimize the most common form: 'continue;'.
  1968. if (source[index] === ';') {
  1969. lex();
  1970. if (!state.inIteration) {
  1971. throwError({}, Messages.IllegalContinue);
  1972. }
  1973. return {
  1974. type: Syntax.ContinueStatement,
  1975. label: null
  1976. };
  1977. }
  1978. if (peekLineTerminator()) {
  1979. if (!state.inIteration) {
  1980. throwError({}, Messages.IllegalContinue);
  1981. }
  1982. return {
  1983. type: Syntax.ContinueStatement,
  1984. label: null
  1985. };
  1986. }
  1987. token = lookahead();
  1988. if (token.type === Token.Identifier) {
  1989. label = parseVariableIdentifier();
  1990. if (!Object.prototype.hasOwnProperty.call(state.labelSet, label.name)) {
  1991. throwError({}, Messages.UnknownLabel, label.name);
  1992. }
  1993. }
  1994. consumeSemicolon();
  1995. if (label === null && !state.inIteration) {
  1996. throwError({}, Messages.IllegalContinue);
  1997. }
  1998. return {
  1999. type: Syntax.ContinueStatement,
  2000. label: label
  2001. };
  2002. }
  2003. // 12.8 The break statement
  2004. function parseBreakStatement() {
  2005. var token, label = null;
  2006. expectKeyword('break');
  2007. // Optimize the most common form: 'break;'.
  2008. if (source[index] === ';') {
  2009. lex();
  2010. if (!(state.inIteration || state.inSwitch)) {
  2011. throwError({}, Messages.IllegalBreak);
  2012. }
  2013. return {
  2014. type: Syntax.BreakStatement,
  2015. label: null
  2016. };
  2017. }
  2018. if (peekLineTerminator()) {
  2019. if (!(state.inIteration || state.inSwitch)) {
  2020. throwError({}, Messages.IllegalBreak);
  2021. }
  2022. return {
  2023. type: Syntax.BreakStatement,
  2024. label: null
  2025. };
  2026. }
  2027. token = lookahead();
  2028. if (token.type === Token.Identifier) {
  2029. label = parseVariableIdentifier();
  2030. if (!Object.prototype.hasOwnProperty.call(state.labelSet, label.name)) {
  2031. throwError({}, Messages.UnknownLabel, label.name);
  2032. }
  2033. }
  2034. consumeSemicolon();
  2035. if (label === null && !(state.inIteration || state.inSwitch)) {
  2036. throwError({}, Messages.IllegalBreak);
  2037. }
  2038. return {
  2039. type: Syntax.BreakStatement,
  2040. label: label
  2041. };
  2042. }
  2043. // 12.9 The return statement
  2044. function parseReturnStatement() {
  2045. var token, argument = null;
  2046. expectKeyword('return');
  2047. if (!state.inFunctionBody) {
  2048. throwErrorTolerant({}, Messages.IllegalReturn);
  2049. }
  2050. // 'return' followed by a space and an identifier is very common.
  2051. if (source[index] === ' ') {
  2052. if (isIdentifierStart(source[index + 1])) {
  2053. argument = parseExpression();
  2054. consumeSemicolon();
  2055. return {
  2056. type: Syntax.ReturnStatement,
  2057. argument: argument
  2058. };
  2059. }
  2060. }
  2061. if (peekLineTerminator()) {
  2062. return {
  2063. type: Syntax.ReturnStatement,
  2064. argument: null
  2065. };
  2066. }
  2067. if (!match(';')) {
  2068. token = lookahead();
  2069. if (!match('}') && token.type !== Token.EOF) {
  2070. argument = parseExpression();
  2071. }
  2072. }
  2073. consumeSemicolon();
  2074. return {
  2075. type: Syntax.ReturnStatement,
  2076. argument: argument
  2077. };
  2078. }
  2079. // 12.10 The with statement
  2080. function parseWithStatement() {
  2081. var object, body;
  2082. if (strict) {
  2083. throwErrorTolerant({}, Messages.StrictModeWith);
  2084. }
  2085. expectKeyword('with');
  2086. expect('(');
  2087. object = parseExpression();
  2088. expect(')');
  2089. body = parseStatement();
  2090. return {
  2091. type: Syntax.WithStatement,
  2092. object: object,
  2093. body: body
  2094. };
  2095. }
  2096. // 12.10 The swith statement
  2097. function parseSwitchCase() {
  2098. var test,
  2099. consequent = [],
  2100. statement;
  2101. if (matchKeyword('default')) {
  2102. lex();
  2103. test = null;
  2104. } else {
  2105. expectKeyword('case');
  2106. test = parseExpression();
  2107. }
  2108. expect(':');
  2109. while (index < length) {
  2110. if (match('}') || matchKeyword('default') || matchKeyword('case')) {
  2111. break;
  2112. }
  2113. statement = parseStatement();
  2114. if (typeof statement === 'undefined') {
  2115. break;
  2116. }
  2117. consequent.push(statement);
  2118. }
  2119. return {
  2120. type: Syntax.SwitchCase,
  2121. test: test,
  2122. consequent: consequent
  2123. };
  2124. }
  2125. function parseSwitchStatement() {
  2126. var discriminant, cases, clause, oldInSwitch, defaultFound;
  2127. expectKeyword('switch');
  2128. expect('(');
  2129. discriminant = parseExpression();
  2130. expect(')');
  2131. expect('{');
  2132. cases = [];
  2133. if (match('}')) {
  2134. lex();
  2135. return {
  2136. type: Syntax.SwitchStatement,
  2137. discriminant: discriminant,
  2138. cases: cases
  2139. };
  2140. }
  2141. oldInSwitch = state.inSwitch;
  2142. state.inSwitch = true;
  2143. defaultFound = false;
  2144. while (index < length) {
  2145. if (match('}')) {
  2146. break;
  2147. }
  2148. clause = parseSwitchCase();
  2149. if (clause.test === null) {
  2150. if (defaultFound) {
  2151. throwError({}, Messages.MultipleDefaultsInSwitch);
  2152. }
  2153. defaultFound = true;
  2154. }
  2155. cases.push(clause);
  2156. }
  2157. state.inSwitch = oldInSwitch;
  2158. expect('}');
  2159. return {
  2160. type: Syntax.SwitchStatement,
  2161. discriminant: discriminant,
  2162. cases: cases
  2163. };
  2164. }
  2165. // 12.13 The throw statement
  2166. function parseThrowStatement() {
  2167. var argument;
  2168. expectKeyword('throw');
  2169. if (peekLineTerminator()) {
  2170. throwError({}, Messages.NewlineAfterThrow);
  2171. }
  2172. argument = parseExpression();
  2173. consumeSemicolon();
  2174. return {
  2175. type: Syntax.ThrowStatement,
  2176. argument: argument
  2177. };
  2178. }
  2179. // 12.14 The try statement
  2180. function parseCatchClause() {
  2181. var param;
  2182. expectKeyword('catch');
  2183. expect('(');
  2184. if (match(')')) {
  2185. throwUnexpected(lookahead());
  2186. }
  2187. param = parseVariableIdentifier();
  2188. // 12.14.1
  2189. if (strict && isRestrictedWord(param.name)) {
  2190. throwErrorTolerant({}, Messages.StrictCatchVariable);
  2191. }
  2192. expect(')');
  2193. return {
  2194. type: Syntax.CatchClause,
  2195. param: param,
  2196. body: parseBlock()
  2197. };
  2198. }
  2199. function parseTryStatement() {
  2200. var block, handlers = [], finalizer = null;
  2201. expectKeyword('try');
  2202. block = parseBlock();
  2203. if (matchKeyword('catch')) {
  2204. handlers.push(parseCatchClause());
  2205. }
  2206. if (matchKeyword('finally')) {
  2207. lex();
  2208. finalizer = parseBlock();
  2209. }
  2210. if (handlers.length === 0 && !finalizer) {
  2211. throwError({}, Messages.NoCatchOrFinally);
  2212. }
  2213. return {
  2214. type: Syntax.TryStatement,
  2215. block: block,
  2216. guardedHandlers: [],
  2217. handlers: handlers,
  2218. finalizer: finalizer
  2219. };
  2220. }
  2221. // 12.15 The debugger statement
  2222. function parseDebuggerStatement() {
  2223. expectKeyword('debugger');
  2224. consumeSemicolon();
  2225. return {
  2226. type: Syntax.DebuggerStatement
  2227. };
  2228. }
  2229. // 12 Statements
  2230. function parseStatement() {
  2231. var token = lookahead(),
  2232. expr,
  2233. labeledBody;
  2234. if (token.type === Token.EOF) {
  2235. throwUnexpected(token);
  2236. }
  2237. if (token.type === Token.Punctuator) {
  2238. switch (token.value) {
  2239. case ';':
  2240. return parseEmptyStatement();
  2241. case '{':
  2242. return parseBlock();
  2243. case '(':
  2244. return parseExpressionStatement();
  2245. default:
  2246. break;
  2247. }
  2248. }
  2249. if (token.type === Token.Keyword) {
  2250. switch (token.value) {
  2251. case 'break':
  2252. return parseBreakStatement();
  2253. case 'continue':
  2254. return parseContinueStatement();
  2255. case 'debugger':
  2256. return parseDebuggerStatement();
  2257. case 'do':
  2258. return parseDoWhileStatement();
  2259. case 'for':
  2260. return parseForStatement();
  2261. case 'function':
  2262. return parseFunctionDeclaration();
  2263. case 'if':
  2264. return parseIfStatement();
  2265. case 'return':
  2266. return parseReturnStatement();
  2267. case 'switch':
  2268. return parseSwitchStatement();
  2269. case 'throw':
  2270. return parseThrowStatement();
  2271. case 'try':
  2272. return parseTryStatement();
  2273. case 'var':
  2274. return parseVariableStatement();
  2275. case 'while':
  2276. return parseWhileStatement();
  2277. case 'with':
  2278. return parseWithStatement();
  2279. default:
  2280. break;
  2281. }
  2282. }
  2283. expr = parseExpression();
  2284. // 12.12 Labelled Statements
  2285. if ((expr.type === Syntax.Identifier) && match(':')) {
  2286. lex();
  2287. if (Object.prototype.hasOwnProperty.call(state.labelSet, expr.name)) {
  2288. throwError({}, Messages.Redeclaration, 'Label', expr.name);
  2289. }
  2290. state.labelSet[expr.name] = true;
  2291. labeledBody = parseStatement();
  2292. delete state.labelSet[expr.name];
  2293. return {
  2294. type: Syntax.LabeledStatement,
  2295. label: expr,
  2296. body: labeledBody
  2297. };
  2298. }
  2299. consumeSemicolon();
  2300. return {
  2301. type: Syntax.ExpressionStatement,
  2302. expression: expr
  2303. };
  2304. }
  2305. // 13 Function Definition
  2306. function parseFunctionSourceElements() {
  2307. var sourceElement, sourceElements = [], token, directive, firstRestricted,
  2308. oldLabelSet, oldInIteration, oldInSwitch, oldInFunctionBody;
  2309. expect('{');
  2310. while (index < length) {
  2311. token = lookahead();
  2312. if (token.type !== Token.StringLiteral) {
  2313. break;
  2314. }
  2315. sourceElement = parseSourceElement();
  2316. sourceElements.push(sourceElement);
  2317. if (sourceElement.expression.type !== Syntax.Literal) {
  2318. // this is not directive
  2319. break;
  2320. }
  2321. directive = sliceSource(token.range[0] + 1, token.range[1] - 1);
  2322. if (directive === 'use strict') {
  2323. strict = true;
  2324. if (firstRestricted) {
  2325. throwErrorTolerant(firstRestricted, Messages.StrictOctalLiteral);
  2326. }
  2327. } else {
  2328. if (!firstRestricted && token.octal) {
  2329. firstRestricted = token;
  2330. }
  2331. }
  2332. }
  2333. oldLabelSet = state.labelSet;
  2334. oldInIteration = state.inIteration;
  2335. oldInSwitch = state.inSwitch;
  2336. oldInFunctionBody = state.inFunctionBody;
  2337. state.labelSet = {};
  2338. state.inIteration = false;
  2339. state.inSwitch = false;
  2340. state.inFunctionBody = true;
  2341. while (index < length) {
  2342. if (match('}')) {
  2343. break;
  2344. }
  2345. sourceElement = parseSourceElement();
  2346. if (typeof sourceElement === 'undefined') {
  2347. break;
  2348. }
  2349. sourceElements.push(sourceElement);
  2350. }
  2351. expect('}');
  2352. state.labelSet = oldLabelSet;
  2353. state.inIteration = oldInIteration;
  2354. state.inSwitch = oldInSwitch;
  2355. state.inFunctionBody = oldInFunctionBody;
  2356. return {
  2357. type: Syntax.BlockStatement,
  2358. body: sourceElements
  2359. };
  2360. }
  2361. function parseFunctionDeclaration() {
  2362. var id, param, params = [], body, token, stricted, firstRestricted, message, previousStrict, paramSet;
  2363. expectKeyword('function');
  2364. token = lookahead();
  2365. id = parseVariableIdentifier();
  2366. if (strict) {
  2367. if (isRestrictedWord(token.value)) {
  2368. throwErrorTolerant(token, Messages.StrictFunctionName);
  2369. }
  2370. } else {
  2371. if (isRestrictedWord(token.value)) {
  2372. firstRestricted = token;
  2373. message = Messages.StrictFunctionName;
  2374. } else if (isStrictModeReservedWord(token.value)) {
  2375. firstRestricted = token;
  2376. message = Messages.StrictReservedWord;
  2377. }
  2378. }
  2379. expect('(');
  2380. if (!match(')')) {
  2381. paramSet = {};
  2382. while (index < length) {
  2383. token = lookahead();
  2384. param = parseVariableIdentifier();
  2385. if (strict) {
  2386. if (isRestrictedWord(token.value)) {
  2387. stricted = token;
  2388. message = Messages.StrictParamName;
  2389. }
  2390. if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) {
  2391. stricted = token;
  2392. message = Messages.StrictParamDupe;
  2393. }
  2394. } else if (!firstRestricted) {
  2395. if (isRestrictedWord(token.value)) {
  2396. firstRestricted = token;
  2397. message = Messages.StrictParamName;
  2398. } else if (isStrictModeReservedWord(token.value)) {
  2399. firstRestricted = token;
  2400. message = Messages.StrictReservedWord;
  2401. } else if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) {
  2402. firstRestricted = token;
  2403. message = Messages.StrictParamDupe;
  2404. }
  2405. }
  2406. params.push(param);
  2407. paramSet[param.name] = true;
  2408. if (match(')')) {
  2409. break;
  2410. }
  2411. expect(',');
  2412. }
  2413. }
  2414. expect(')');
  2415. previousStrict = strict;
  2416. body = parseFunctionSourceElements();
  2417. if (strict && firstRestricted) {
  2418. throwError(firstRestricted, message);
  2419. }
  2420. if (strict && stricted) {
  2421. throwErrorTolerant(stricted, message);
  2422. }
  2423. strict = previousStrict;
  2424. return {
  2425. type: Syntax.FunctionDeclaration,
  2426. id: id,
  2427. params: params,
  2428. defaults: [],
  2429. body: body,
  2430. rest: null,
  2431. generator: false,
  2432. expression: false
  2433. };
  2434. }
  2435. function parseFunctionExpression() {
  2436. var token, id = null, stricted, firstRestricted, message, param, params = [], body, previousStrict, paramSet;
  2437. expectKeyword('function');
  2438. if (!match('(')) {
  2439. token = lookahead();
  2440. id = parseVariableIdentifier();
  2441. if (strict) {
  2442. if (isRestrictedWord(token.value)) {
  2443. throwErrorTolerant(token, Messages.StrictFunctionName);
  2444. }
  2445. } else {
  2446. if (isRestrictedWord(token.value)) {
  2447. firstRestricted = token;
  2448. message = Messages.StrictFunctionName;
  2449. } else if (isStrictModeReservedWord(token.value)) {
  2450. firstRestricted = token;
  2451. message = Messages.StrictReservedWord;
  2452. }
  2453. }
  2454. }
  2455. expect('(');
  2456. if (!match(')')) {
  2457. paramSet = {};
  2458. while (index < length) {
  2459. token = lookahead();
  2460. param = parseVariableIdentifier();
  2461. if (strict) {
  2462. if (isRestrictedWord(token.value)) {
  2463. stricted = token;
  2464. message = Messages.StrictParamName;
  2465. }
  2466. if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) {
  2467. stricted = token;
  2468. message = Messages.StrictParamDupe;
  2469. }
  2470. } else if (!firstRestricted) {
  2471. if (isRestrictedWord(token.value)) {
  2472. firstRestricted = token;
  2473. message = Messages.StrictParamName;
  2474. } else if (isStrictModeReservedWord(token.value)) {
  2475. firstRestricted = token;
  2476. message = Messages.StrictReservedWord;
  2477. } else if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) {
  2478. firstRestricted = token;
  2479. message = Messages.StrictParamDupe;
  2480. }
  2481. }
  2482. params.push(param);
  2483. paramSet[param.name] = true;
  2484. if (match(')')) {
  2485. break;
  2486. }
  2487. expect(',');
  2488. }
  2489. }
  2490. expect(')');
  2491. previousStrict = strict;
  2492. body = parseFunctionSourceElements();
  2493. if (strict && firstRestricted) {
  2494. throwError(firstRestricted, message);
  2495. }
  2496. if (strict && stricted) {
  2497. throwErrorTolerant(stricted, message);
  2498. }
  2499. strict = previousStrict;
  2500. return {
  2501. type: Syntax.FunctionExpression,
  2502. id: id,
  2503. params: params,
  2504. defaults: [],
  2505. body: body,
  2506. rest: null,
  2507. generator: false,
  2508. expression: false
  2509. };
  2510. }
  2511. // 14 Program
  2512. function parseSourceElement() {
  2513. var token = lookahead();
  2514. if (token.type === Token.Keyword) {
  2515. switch (token.value) {
  2516. case 'const':
  2517. case 'let':
  2518. return parseConstLetDeclaration(token.value);
  2519. case 'function':
  2520. return parseFunctionDeclaration();
  2521. default:
  2522. return parseStatement();
  2523. }
  2524. }
  2525. if (token.type !== Token.EOF) {
  2526. return parseStatement();
  2527. }
  2528. }
  2529. function parseSourceElements() {
  2530. var sourceElement, sourceElements = [], token, directive, firstRestricted;
  2531. while (index < length) {
  2532. token = lookahead();
  2533. if (token.type !== Token.StringLiteral) {
  2534. break;
  2535. }
  2536. sourceElement = parseSourceElement();
  2537. sourceElements.push(sourceElement);
  2538. if (sourceElement.expression.type !== Syntax.Literal) {
  2539. // this is not directive
  2540. break;
  2541. }
  2542. directive = sliceSource(token.range[0] + 1, token.range[1] - 1);
  2543. if (directive === 'use strict') {
  2544. strict = true;
  2545. if (firstRestricted) {
  2546. throwErrorTolerant(firstRestricted, Messages.StrictOctalLiteral);
  2547. }
  2548. } else {
  2549. if (!firstRestricted && token.octal) {
  2550. firstRestricted = token;
  2551. }
  2552. }
  2553. }
  2554. while (index < length) {
  2555. sourceElement = parseSourceElement();
  2556. if (typeof sourceElement === 'undefined') {
  2557. break;
  2558. }
  2559. sourceElements.push(sourceElement);
  2560. }
  2561. return sourceElements;
  2562. }
  2563. function parseProgram() {
  2564. var program;
  2565. strict = false;
  2566. program = {
  2567. type: Syntax.Program,
  2568. body: parseSourceElements()
  2569. };
  2570. return program;
  2571. }
  2572. // The following functions are needed only when the option to preserve
  2573. // the comments is active.
  2574. function addComment(type, value, start, end, loc) {
  2575. assert(typeof start === 'number', 'Comment must have valid position');
  2576. // Because the way the actual token is scanned, often the comments
  2577. // (if any) are skipped twice during the lexical analysis.
  2578. // Thus, we need to skip adding a comment if the comment array already
  2579. // handled it.
  2580. if (extra.comments.length > 0) {
  2581. if (extra.comments[extra.comments.length - 1].range[1] > start) {
  2582. return;
  2583. }
  2584. }
  2585. extra.comments.push({
  2586. type: type,
  2587. value: value,
  2588. range: [start, end],
  2589. loc: loc
  2590. });
  2591. }
  2592. function scanComment() {
  2593. var comment, ch, loc, start, blockComment, lineComment;
  2594. comment = '';
  2595. blockComment = false;
  2596. lineComment = false;
  2597. while (index < length) {
  2598. ch = source[index];
  2599. if (lineComment) {
  2600. ch = source[index++];
  2601. if (isLineTerminator(ch)) {
  2602. loc.end = {
  2603. line: lineNumber,
  2604. column: index - lineStart - 1
  2605. };
  2606. lineComment = false;
  2607. addComment('Line', comment, start, index - 1, loc);
  2608. if (ch === '\r' && source[index] === '\n') {
  2609. ++index;
  2610. }
  2611. ++lineNumber;
  2612. lineStart = index;
  2613. comment = '';
  2614. } else if (index >= length) {
  2615. lineComment = false;
  2616. comment += ch;
  2617. loc.end = {
  2618. line: lineNumber,
  2619. column: length - lineStart
  2620. };
  2621. addComment('Line', comment, start, length, loc);
  2622. } else {
  2623. comment += ch;
  2624. }
  2625. } else if (blockComment) {
  2626. if (isLineTerminator(ch)) {
  2627. if (ch === '\r' && source[index + 1] === '\n') {
  2628. ++index;
  2629. comment += '\r\n';
  2630. } else {
  2631. comment += ch;
  2632. }
  2633. ++lineNumber;
  2634. ++index;
  2635. lineStart = index;
  2636. if (index >= length) {
  2637. throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
  2638. }
  2639. } else {
  2640. ch = source[index++];
  2641. if (index >= length) {
  2642. throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
  2643. }
  2644. comment += ch;
  2645. if (ch === '*') {
  2646. ch = source[index];
  2647. if (ch === '/') {
  2648. comment = comment.substr(0, comment.length - 1);
  2649. blockComment = false;
  2650. ++index;
  2651. loc.end = {
  2652. line: lineNumber,
  2653. column: index - lineStart
  2654. };
  2655. addComment('Block', comment, start, index, loc);
  2656. comment = '';
  2657. }
  2658. }
  2659. }
  2660. } else if (ch === '/') {
  2661. ch = source[index + 1];
  2662. if (ch === '/') {
  2663. loc = {
  2664. start: {
  2665. line: lineNumber,
  2666. column: index - lineStart
  2667. }
  2668. };
  2669. start = index;
  2670. index += 2;
  2671. lineComment = true;
  2672. if (index >= length) {
  2673. loc.end = {
  2674. line: lineNumber,
  2675. column: index - lineStart
  2676. };
  2677. lineComment = false;
  2678. addComment('Line', comment, start, index, loc);
  2679. }
  2680. } else if (ch === '*') {
  2681. start = index;
  2682. index += 2;
  2683. blockComment = true;
  2684. loc = {
  2685. start: {
  2686. line: lineNumber,
  2687. column: index - lineStart - 2
  2688. }
  2689. };
  2690. if (index >= length) {
  2691. throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
  2692. }
  2693. } else {
  2694. break;
  2695. }
  2696. } else if (isWhiteSpace(ch)) {
  2697. ++index;
  2698. } else if (isLineTerminator(ch)) {
  2699. ++index;
  2700. if (ch === '\r' && source[index] === '\n') {
  2701. ++index;
  2702. }
  2703. ++lineNumber;
  2704. lineStart = index;
  2705. } else {
  2706. break;
  2707. }
  2708. }
  2709. }
  2710. function filterCommentLocation() {
  2711. var i, entry, comment, comments = [];
  2712. for (i = 0; i < extra.comments.length; ++i) {
  2713. entry = extra.comments[i];
  2714. comment = {
  2715. type: entry.type,
  2716. value: entry.value
  2717. };
  2718. if (extra.range) {
  2719. comment.range = entry.range;
  2720. }
  2721. if (extra.loc) {
  2722. comment.loc = entry.loc;
  2723. }
  2724. comments.push(comment);
  2725. }
  2726. extra.comments = comments;
  2727. }
  2728. function collectToken() {
  2729. var start, loc, token, range, value;
  2730. skipComment();
  2731. start = index;
  2732. loc = {
  2733. start: {
  2734. line: lineNumber,
  2735. column: index - lineStart
  2736. }
  2737. };
  2738. token = extra.advance();
  2739. loc.end = {
  2740. line: lineNumber,
  2741. column: index - lineStart
  2742. };
  2743. if (token.type !== Token.EOF) {
  2744. range = [token.range[0], token.range[1]];
  2745. value = sliceSource(token.range[0], token.range[1]);
  2746. extra.tokens.push({
  2747. type: TokenName[token.type],
  2748. value: value,
  2749. range: range,
  2750. loc: loc
  2751. });
  2752. }
  2753. return token;
  2754. }
  2755. function collectRegex() {
  2756. var pos, loc, regex, token;
  2757. skipComment();
  2758. pos = index;
  2759. loc = {
  2760. start: {
  2761. line: lineNumber,
  2762. column: index - lineStart
  2763. }
  2764. };
  2765. regex = extra.scanRegExp();
  2766. loc.end = {
  2767. line: lineNumber,
  2768. column: index - lineStart
  2769. };
  2770. // Pop the previous token, which is likely '/' or '/='
  2771. if (extra.tokens.length > 0) {
  2772. token = extra.tokens[extra.tokens.length - 1];
  2773. if (token.range[0] === pos && token.type === 'Punctuator') {
  2774. if (token.value === '/' || token.value === '/=') {
  2775. extra.tokens.pop();
  2776. }
  2777. }
  2778. }
  2779. extra.tokens.push({
  2780. type: 'RegularExpression',
  2781. value: regex.literal,
  2782. range: [pos, index],
  2783. loc: loc
  2784. });
  2785. return regex;
  2786. }
  2787. function filterTokenLocation() {
  2788. var i, entry, token, tokens = [];
  2789. for (i = 0; i < extra.tokens.length; ++i) {
  2790. entry = extra.tokens[i];
  2791. token = {
  2792. type: entry.type,
  2793. value: entry.value
  2794. };
  2795. if (extra.range) {
  2796. token.range = entry.range;
  2797. }
  2798. if (extra.loc) {
  2799. token.loc = entry.loc;
  2800. }
  2801. tokens.push(token);
  2802. }
  2803. extra.tokens = tokens;
  2804. }
  2805. function createLiteral(token) {
  2806. return {
  2807. type: Syntax.Literal,
  2808. value: token.value
  2809. };
  2810. }
  2811. function createRawLiteral(token) {
  2812. return {
  2813. type: Syntax.Literal,
  2814. value: token.value,
  2815. raw: sliceSource(token.range[0], token.range[1])
  2816. };
  2817. }
  2818. function createLocationMarker() {
  2819. var marker = {};
  2820. marker.range = [index, index];
  2821. marker.loc = {
  2822. start: {
  2823. line: lineNumber,
  2824. column: index - lineStart
  2825. },
  2826. end: {
  2827. line: lineNumber,
  2828. column: index - lineStart
  2829. }
  2830. };
  2831. marker.end = function () {
  2832. this.range[1] = index;
  2833. this.loc.end.line = lineNumber;
  2834. this.loc.end.column = index - lineStart;
  2835. };
  2836. marker.applyGroup = function (node) {
  2837. if (extra.range) {
  2838. node.groupRange = [this.range[0], this.range[1]];
  2839. }
  2840. if (extra.loc) {
  2841. node.groupLoc = {
  2842. start: {
  2843. line: this.loc.start.line,
  2844. column: this.loc.start.column
  2845. },
  2846. end: {
  2847. line: this.loc.end.line,
  2848. column: this.loc.end.column
  2849. }
  2850. };
  2851. }
  2852. };
  2853. marker.apply = function (node) {
  2854. if (extra.range) {
  2855. node.range = [this.range[0], this.range[1]];
  2856. }
  2857. if (extra.loc) {
  2858. node.loc = {
  2859. start: {
  2860. line: this.loc.start.line,
  2861. column: this.loc.start.column
  2862. },
  2863. end: {
  2864. line: this.loc.end.line,
  2865. column: this.loc.end.column
  2866. }
  2867. };
  2868. }
  2869. };
  2870. return marker;
  2871. }
  2872. function trackGroupExpression() {
  2873. var marker, expr;
  2874. skipComment();
  2875. marker = createLocationMarker();
  2876. expect('(');
  2877. expr = parseExpression();
  2878. expect(')');
  2879. marker.end();
  2880. marker.applyGroup(expr);
  2881. return expr;
  2882. }
  2883. function trackLeftHandSideExpression() {
  2884. var marker, expr;
  2885. skipComment();
  2886. marker = createLocationMarker();
  2887. expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression();
  2888. while (match('.') || match('[')) {
  2889. if (match('[')) {
  2890. expr = {
  2891. type: Syntax.MemberExpression,
  2892. computed: true,
  2893. object: expr,
  2894. property: parseComputedMember()
  2895. };
  2896. marker.end();
  2897. marker.apply(expr);
  2898. } else {
  2899. expr = {
  2900. type: Syntax.MemberExpression,
  2901. computed: false,
  2902. object: expr,
  2903. property: parseNonComputedMember()
  2904. };
  2905. marker.end();
  2906. marker.apply(expr);
  2907. }
  2908. }
  2909. return expr;
  2910. }
  2911. function trackLeftHandSideExpressionAllowCall() {
  2912. var marker, expr;
  2913. skipComment();
  2914. marker = createLocationMarker();
  2915. expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression();
  2916. while (match('.') || match('[') || match('(')) {
  2917. if (match('(')) {
  2918. expr = {
  2919. type: Syntax.CallExpression,
  2920. callee: expr,
  2921. 'arguments': parseArguments()
  2922. };
  2923. marker.end();
  2924. marker.apply(expr);
  2925. } else if (match('[')) {
  2926. expr = {
  2927. type: Syntax.MemberExpression,
  2928. computed: true,
  2929. object: expr,
  2930. property: parseComputedMember()
  2931. };
  2932. marker.end();
  2933. marker.apply(expr);
  2934. } else {
  2935. expr = {
  2936. type: Syntax.MemberExpression,
  2937. computed: false,
  2938. object: expr,
  2939. property: parseNonComputedMember()
  2940. };
  2941. marker.end();
  2942. marker.apply(expr);
  2943. }
  2944. }
  2945. return expr;
  2946. }
  2947. function filterGroup(node) {
  2948. var n, i, entry;
  2949. n = (Object.prototype.toString.apply(node) === '[object Array]') ? [] : {};
  2950. for (i in node) {
  2951. if (node.hasOwnProperty(i) && i !== 'groupRange' && i !== 'groupLoc') {
  2952. entry = node[i];
  2953. if (entry === null || typeof entry !== 'object' || entry instanceof RegExp) {
  2954. n[i] = entry;
  2955. } else {
  2956. n[i] = filterGroup(entry);
  2957. }
  2958. }
  2959. }
  2960. return n;
  2961. }
  2962. function wrapTrackingFunction(range, loc) {
  2963. return function (parseFunction) {
  2964. function isBinary(node) {
  2965. return node.type === Syntax.LogicalExpression ||
  2966. node.type === Syntax.BinaryExpression;
  2967. }
  2968. function visit(node) {
  2969. var start, end;
  2970. if (isBinary(node.left)) {
  2971. visit(node.left);
  2972. }
  2973. if (isBinary(node.right)) {
  2974. visit(node.right);
  2975. }
  2976. if (range) {
  2977. if (node.left.groupRange || node.right.groupRange) {
  2978. start = node.left.groupRange ? node.left.groupRange[0] : node.left.range[0];
  2979. end = node.right.groupRange ? node.right.groupRange[1] : node.right.range[1];
  2980. node.range = [start, end];
  2981. } else if (typeof node.range === 'undefined') {
  2982. start = node.left.range[0];
  2983. end = node.right.range[1];
  2984. node.range = [start, end];
  2985. }
  2986. }
  2987. if (loc) {
  2988. if (node.left.groupLoc || node.right.groupLoc) {
  2989. start = node.left.groupLoc ? node.left.groupLoc.start : node.left.loc.start;
  2990. end = node.right.groupLoc ? node.right.groupLoc.end : node.right.loc.end;
  2991. node.loc = {
  2992. start: start,
  2993. end: end
  2994. };
  2995. } else if (typeof node.loc === 'undefined') {
  2996. node.loc = {
  2997. start: node.left.loc.start,
  2998. end: node.right.loc.end
  2999. };
  3000. }
  3001. }
  3002. }
  3003. return function () {
  3004. var marker, node;
  3005. skipComment();
  3006. marker = createLocationMarker();
  3007. node = parseFunction.apply(null, arguments);
  3008. marker.end();
  3009. if (range && typeof node.range === 'undefined') {
  3010. marker.apply(node);
  3011. }
  3012. if (loc && typeof node.loc === 'undefined') {
  3013. marker.apply(node);
  3014. }
  3015. if (isBinary(node)) {
  3016. visit(node);
  3017. }
  3018. return node;
  3019. };
  3020. };
  3021. }
  3022. function patch() {
  3023. var wrapTracking;
  3024. if (extra.comments) {
  3025. extra.skipComment = skipComment;
  3026. skipComment = scanComment;
  3027. }
  3028. if (extra.raw) {
  3029. extra.createLiteral = createLiteral;
  3030. createLiteral = createRawLiteral;
  3031. }
  3032. if (extra.range || extra.loc) {
  3033. extra.parseGroupExpression = parseGroupExpression;
  3034. extra.parseLeftHandSideExpression = parseLeftHandSideExpression;
  3035. extra.parseLeftHandSideExpressionAllowCall = parseLeftHandSideExpressionAllowCall;
  3036. parseGroupExpression = trackGroupExpression;
  3037. parseLeftHandSideExpression = trackLeftHandSideExpression;
  3038. parseLeftHandSideExpressionAllowCall = trackLeftHandSideExpressionAllowCall;
  3039. wrapTracking = wrapTrackingFunction(extra.range, extra.loc);
  3040. extra.parseAdditiveExpression = parseAdditiveExpression;
  3041. extra.parseAssignmentExpression = parseAssignmentExpression;
  3042. extra.parseBitwiseANDExpression = parseBitwiseANDExpression;
  3043. extra.parseBitwiseORExpression = parseBitwiseORExpression;
  3044. extra.parseBitwiseXORExpression = parseBitwiseXORExpression;
  3045. extra.parseBlock = parseBlock;
  3046. extra.parseFunctionSourceElements = parseFunctionSourceElements;
  3047. extra.parseCatchClause = parseCatchClause;
  3048. extra.parseComputedMember = parseComputedMember;
  3049. extra.parseConditionalExpression = parseConditionalExpression;
  3050. extra.parseConstLetDeclaration = parseConstLetDeclaration;
  3051. extra.parseEqualityExpression = parseEqualityExpression;
  3052. extra.parseExpression = parseExpression;
  3053. extra.parseForVariableDeclaration = parseForVariableDeclaration;
  3054. extra.parseFunctionDeclaration = parseFunctionDeclaration;
  3055. extra.parseFunctionExpression = parseFunctionExpression;
  3056. extra.parseLogicalANDExpression = parseLogicalANDExpression;
  3057. extra.parseLogicalORExpression = parseLogicalORExpression;
  3058. extra.parseMultiplicativeExpression = parseMultiplicativeExpression;
  3059. extra.parseNewExpression = parseNewExpression;
  3060. extra.parseNonComputedProperty = parseNonComputedProperty;
  3061. extra.parseObjectProperty = parseObjectProperty;
  3062. extra.parseObjectPropertyKey = parseObjectPropertyKey;
  3063. extra.parsePostfixExpression = parsePostfixExpression;
  3064. extra.parsePrimaryExpression = parsePrimaryExpression;
  3065. extra.parseProgram = parseProgram;
  3066. extra.parsePropertyFunction = parsePropertyFunction;
  3067. extra.parseRelationalExpression = parseRelationalExpression;
  3068. extra.parseStatement = parseStatement;
  3069. extra.parseShiftExpression = parseShiftExpression;
  3070. extra.parseSwitchCase = parseSwitchCase;
  3071. extra.parseUnaryExpression = parseUnaryExpression;
  3072. extra.parseVariableDeclaration = parseVariableDeclaration;
  3073. extra.parseVariableIdentifier = parseVariableIdentifier;
  3074. parseAdditiveExpression = wrapTracking(extra.parseAdditiveExpression);
  3075. parseAssignmentExpression = wrapTracking(extra.parseAssignmentExpression);
  3076. parseBitwiseANDExpression = wrapTracking(extra.parseBitwiseANDExpression);
  3077. parseBitwiseORExpression = wrapTracking(extra.parseBitwiseORExpression);
  3078. parseBitwiseXORExpression = wrapTracking(extra.parseBitwiseXORExpression);
  3079. parseBlock = wrapTracking(extra.parseBlock);
  3080. parseFunctionSourceElements = wrapTracking(extra.parseFunctionSourceElements);
  3081. parseCatchClause = wrapTracking(extra.parseCatchClause);
  3082. parseComputedMember = wrapTracking(extra.parseComputedMember);
  3083. parseConditionalExpression = wrapTracking(extra.parseConditionalExpression);
  3084. parseConstLetDeclaration = wrapTracking(extra.parseConstLetDeclaration);
  3085. parseEqualityExpression = wrapTracking(extra.parseEqualityExpression);
  3086. parseExpression = wrapTracking(extra.parseExpression);
  3087. parseForVariableDeclaration = wrapTracking(extra.parseForVariableDeclaration);
  3088. parseFunctionDeclaration = wrapTracking(extra.parseFunctionDeclaration);
  3089. parseFunctionExpression = wrapTracking(extra.parseFunctionExpression);
  3090. parseLeftHandSideExpression = wrapTracking(parseLeftHandSideExpression);
  3091. parseLogicalANDExpression = wrapTracking(extra.parseLogicalANDExpression);
  3092. parseLogicalORExpression = wrapTracking(extra.parseLogicalORExpression);
  3093. parseMultiplicativeExpression = wrapTracking(extra.parseMultiplicativeExpression);
  3094. parseNewExpression = wrapTracking(extra.parseNewExpression);
  3095. parseNonComputedProperty = wrapTracking(extra.parseNonComputedProperty);
  3096. parseObjectProperty = wrapTracking(extra.parseObjectProperty);
  3097. parseObjectPropertyKey = wrapTracking(extra.parseObjectPropertyKey);
  3098. parsePostfixExpression = wrapTracking(extra.parsePostfixExpression);
  3099. parsePrimaryExpression = wrapTracking(extra.parsePrimaryExpression);
  3100. parseProgram = wrapTracking(extra.parseProgram);
  3101. parsePropertyFunction = wrapTracking(extra.parsePropertyFunction);
  3102. parseRelationalExpression = wrapTracking(extra.parseRelationalExpression);
  3103. parseStatement = wrapTracking(extra.parseStatement);
  3104. parseShiftExpression = wrapTracking(extra.parseShiftExpression);
  3105. parseSwitchCase = wrapTracking(extra.parseSwitchCase);
  3106. parseUnaryExpression = wrapTracking(extra.parseUnaryExpression);
  3107. parseVariableDeclaration = wrapTracking(extra.parseVariableDeclaration);
  3108. parseVariableIdentifier = wrapTracking(extra.parseVariableIdentifier);
  3109. }
  3110. if (typeof extra.tokens !== 'undefined') {
  3111. extra.advance = advance;
  3112. extra.scanRegExp = scanRegExp;
  3113. advance = collectToken;
  3114. scanRegExp = collectRegex;
  3115. }
  3116. }
  3117. function unpatch() {
  3118. if (typeof extra.skipComment === 'function') {
  3119. skipComment = extra.skipComment;
  3120. }
  3121. if (extra.raw) {
  3122. createLiteral = extra.createLiteral;
  3123. }
  3124. if (extra.range || extra.loc) {
  3125. parseAdditiveExpression = extra.parseAdditiveExpression;
  3126. parseAssignmentExpression = extra.parseAssignmentExpression;
  3127. parseBitwiseANDExpression = extra.parseBitwiseANDExpression;
  3128. parseBitwiseORExpression = extra.parseBitwiseORExpression;
  3129. parseBitwiseXORExpression = extra.parseBitwiseXORExpression;
  3130. parseBlock = extra.parseBlock;
  3131. parseFunctionSourceElements = extra.parseFunctionSourceElements;
  3132. parseCatchClause = extra.parseCatchClause;
  3133. parseComputedMember = extra.parseComputedMember;
  3134. parseConditionalExpression = extra.parseConditionalExpression;
  3135. parseConstLetDeclaration = extra.parseConstLetDeclaration;
  3136. parseEqualityExpression = extra.parseEqualityExpression;
  3137. parseExpression = extra.parseExpression;
  3138. parseForVariableDeclaration = extra.parseForVariableDeclaration;
  3139. parseFunctionDeclaration = extra.parseFunctionDeclaration;
  3140. parseFunctionExpression = extra.parseFunctionExpression;
  3141. parseGroupExpression = extra.parseGroupExpression;
  3142. parseLeftHandSideExpression = extra.parseLeftHandSideExpression;
  3143. parseLeftHandSideExpressionAllowCall = extra.parseLeftHandSideExpressionAllowCall;
  3144. parseLogicalANDExpression = extra.parseLogicalANDExpression;
  3145. parseLogicalORExpression = extra.parseLogicalORExpression;
  3146. parseMultiplicativeExpression = extra.parseMultiplicativeExpression;
  3147. parseNewExpression = extra.parseNewExpression;
  3148. parseNonComputedProperty = extra.parseNonComputedProperty;
  3149. parseObjectProperty = extra.parseObjectProperty;
  3150. parseObjectPropertyKey = extra.parseObjectPropertyKey;
  3151. parsePrimaryExpression = extra.parsePrimaryExpression;
  3152. parsePostfixExpression = extra.parsePostfixExpression;
  3153. parseProgram = extra.parseProgram;
  3154. parsePropertyFunction = extra.parsePropertyFunction;
  3155. parseRelationalExpression = extra.parseRelationalExpression;
  3156. parseStatement = extra.parseStatement;
  3157. parseShiftExpression = extra.parseShiftExpression;
  3158. parseSwitchCase = extra.parseSwitchCase;
  3159. parseUnaryExpression = extra.parseUnaryExpression;
  3160. parseVariableDeclaration = extra.parseVariableDeclaration;
  3161. parseVariableIdentifier = extra.parseVariableIdentifier;
  3162. }
  3163. if (typeof extra.scanRegExp === 'function') {
  3164. advance = extra.advance;
  3165. scanRegExp = extra.scanRegExp;
  3166. }
  3167. }
  3168. function stringToArray(str) {
  3169. var length = str.length,
  3170. result = [],
  3171. i;
  3172. for (i = 0; i < length; ++i) {
  3173. result[i] = str.charAt(i);
  3174. }
  3175. return result;
  3176. }
  3177. function parse(code, options) {
  3178. var program, toString;
  3179. toString = String;
  3180. if (typeof code !== 'string' && !(code instanceof String)) {
  3181. code = toString(code);
  3182. }
  3183. source = code;
  3184. index = 0;
  3185. lineNumber = (source.length > 0) ? 1 : 0;
  3186. lineStart = 0;
  3187. length = source.length;
  3188. buffer = null;
  3189. state = {
  3190. allowIn: true,
  3191. labelSet: {},
  3192. inFunctionBody: false,
  3193. inIteration: false,
  3194. inSwitch: false
  3195. };
  3196. extra = {};
  3197. if (typeof options !== 'undefined') {
  3198. extra.range = (typeof options.range === 'boolean') && options.range;
  3199. extra.loc = (typeof options.loc === 'boolean') && options.loc;
  3200. extra.raw = (typeof options.raw === 'boolean') && options.raw;
  3201. if (typeof options.tokens === 'boolean' && options.tokens) {
  3202. extra.tokens = [];
  3203. }
  3204. if (typeof options.comment === 'boolean' && options.comment) {
  3205. extra.comments = [];
  3206. }
  3207. if (typeof options.tolerant === 'boolean' && options.tolerant) {
  3208. extra.errors = [];
  3209. }
  3210. }
  3211. if (length > 0) {
  3212. if (typeof source[0] === 'undefined') {
  3213. // Try first to convert to a string. This is good as fast path
  3214. // for old IE which understands string indexing for string
  3215. // literals only and not for string object.
  3216. if (code instanceof String) {
  3217. source = code.valueOf();
  3218. }
  3219. // Force accessing the characters via an array.
  3220. if (typeof source[0] === 'undefined') {
  3221. source = stringToArray(code);
  3222. }
  3223. }
  3224. }
  3225. patch();
  3226. try {
  3227. program = parseProgram();
  3228. if (typeof extra.comments !== 'undefined') {
  3229. filterCommentLocation();
  3230. program.comments = extra.comments;
  3231. }
  3232. if (typeof extra.tokens !== 'undefined') {
  3233. filterTokenLocation();
  3234. program.tokens = extra.tokens;
  3235. }
  3236. if (typeof extra.errors !== 'undefined') {
  3237. program.errors = extra.errors;
  3238. }
  3239. if (extra.range || extra.loc) {
  3240. program.body = filterGroup(program.body);
  3241. }
  3242. } catch (e) {
  3243. throw e;
  3244. } finally {
  3245. unpatch();
  3246. extra = {};
  3247. }
  3248. return program;
  3249. }
  3250. // Sync with package.json.
  3251. exports.version = '1.0.4';
  3252. exports.parse = parse;
  3253. // Deep copy.
  3254. exports.Syntax = (function () {
  3255. var name, types = {};
  3256. if (typeof Object.create === 'function') {
  3257. types = Object.create(null);
  3258. }
  3259. for (name in Syntax) {
  3260. if (Syntax.hasOwnProperty(name)) {
  3261. types[name] = Syntax[name];
  3262. }
  3263. }
  3264. if (typeof Object.freeze === 'function') {
  3265. Object.freeze(types);
  3266. }
  3267. return types;
  3268. }());
  3269. }));
  3270. /* vim: set sw=4 ts=4 et tw=80 : */
  3271. })(null);
  3272. /*!
  3273. * falafel (c) James Halliday / MIT License
  3274. * https://github.com/substack/node-falafel
  3275. */
  3276. (function(require,module){
  3277. var parse = require('esprima').parse;
  3278. var objectKeys = Object.keys || function (obj) {
  3279. var keys = [];
  3280. for (var key in obj) keys.push(key);
  3281. return keys;
  3282. };
  3283. var forEach = function (xs, fn) {
  3284. if (xs.forEach) return xs.forEach(fn);
  3285. for (var i = 0; i < xs.length; i++) {
  3286. fn.call(xs, xs[i], i, xs);
  3287. }
  3288. };
  3289. var isArray = Array.isArray || function (xs) {
  3290. return Object.prototype.toString.call(xs) === '[object Array]';
  3291. };
  3292. module.exports = function (src, opts, fn) {
  3293. if (typeof opts === 'function') {
  3294. fn = opts;
  3295. opts = {};
  3296. }
  3297. if (typeof src === 'object') {
  3298. opts = src;
  3299. src = opts.source;
  3300. delete opts.source;
  3301. }
  3302. src = src === undefined ? opts.source : src;
  3303. opts.range = true;
  3304. if (typeof src !== 'string') src = String(src);
  3305. var ast = parse(src, opts);
  3306. var result = {
  3307. chunks : src.split(''),
  3308. toString : function () { return result.chunks.join('') },
  3309. inspect : function () { return result.toString() }
  3310. };
  3311. var index = 0;
  3312. (function walk (node, parent) {
  3313. insertHelpers(node, parent, result.chunks);
  3314. forEach(objectKeys(node), function (key) {
  3315. if (key === 'parent') return;
  3316. var child = node[key];
  3317. if (isArray(child)) {
  3318. forEach(child, function (c) {
  3319. if (c && typeof c.type === 'string') {
  3320. walk(c, node);
  3321. }
  3322. });
  3323. }
  3324. else if (child && typeof child.type === 'string') {
  3325. insertHelpers(child, node, result.chunks);
  3326. walk(child, node);
  3327. }
  3328. });
  3329. fn(node);
  3330. })(ast, undefined);
  3331. return result;
  3332. };
  3333. function insertHelpers (node, parent, chunks) {
  3334. if (!node.range) return;
  3335. node.parent = parent;
  3336. node.source = function () {
  3337. return chunks.slice(
  3338. node.range[0], node.range[1]
  3339. ).join('');
  3340. };
  3341. if (node.update && typeof node.update === 'object') {
  3342. var prev = node.update;
  3343. forEach(objectKeys(prev), function (key) {
  3344. update[key] = prev[key];
  3345. });
  3346. node.update = update;
  3347. }
  3348. else {
  3349. node.update = update;
  3350. }
  3351. function update (s) {
  3352. chunks[node.range[0]] = s;
  3353. for (var i = node.range[0] + 1; i < node.range[1]; i++) {
  3354. chunks[i] = '';
  3355. }
  3356. };
  3357. }
  3358. window.falafel = module.exports;})(function(){return {parse: esprima.parse};},{exports: {}});
  3359. var inBrowser = typeof window !== 'undefined' && this === window;
  3360. var parseAndModify = (inBrowser ? window.falafel : require("falafel"));
  3361. (inBrowser ? window : exports).blanket = (function(){
  3362. var linesToAddTracking = [
  3363. "ExpressionStatement",
  3364. "BreakStatement" ,
  3365. "ContinueStatement" ,
  3366. "VariableDeclaration",
  3367. "ReturnStatement" ,
  3368. "ThrowStatement" ,
  3369. "TryStatement" ,
  3370. "FunctionDeclaration" ,
  3371. "IfStatement" ,
  3372. "WhileStatement" ,
  3373. "DoWhileStatement" ,
  3374. "ForStatement" ,
  3375. "ForInStatement" ,
  3376. "SwitchStatement" ,
  3377. "WithStatement"
  3378. ],
  3379. linesToAddBrackets = [
  3380. "IfStatement" ,
  3381. "WhileStatement" ,
  3382. "DoWhileStatement" ,
  3383. "ForStatement" ,
  3384. "ForInStatement" ,
  3385. "WithStatement"
  3386. ],
  3387. __blanket,
  3388. copynumber = Math.floor(Math.random()*1000),
  3389. coverageInfo = {},options = {
  3390. reporter: null,
  3391. adapter:null,
  3392. filter: null,
  3393. customVariable: null,
  3394. loader: null,
  3395. ignoreScriptError: false,
  3396. existingRequireJS:false,
  3397. autoStart: false,
  3398. timeout: 180,
  3399. ignoreCors: false,
  3400. branchTracking: false,
  3401. sourceURL: false,
  3402. debug:false,
  3403. engineOnly:false,
  3404. testReadyCallback:null,
  3405. commonJS:false,
  3406. instrumentCache:false,
  3407. modulePattern: null
  3408. };
  3409. if (inBrowser && typeof window.blanket !== 'undefined'){
  3410. __blanket = window.blanket.noConflict();
  3411. }
  3412. _blanket = {
  3413. noConflict: function(){
  3414. if (__blanket){
  3415. return __blanket;
  3416. }
  3417. return _blanket;
  3418. },
  3419. _getCopyNumber: function(){
  3420. //internal method
  3421. //for differentiating between instances
  3422. return copynumber;
  3423. },
  3424. extend: function(obj) {
  3425. //borrowed from underscore
  3426. _blanket._extend(_blanket,obj);
  3427. },
  3428. _extend: function(dest,source){
  3429. if (source) {
  3430. for (var prop in source) {
  3431. if ( dest[prop] instanceof Object && typeof dest[prop] !== "function"){
  3432. _blanket._extend(dest[prop],source[prop]);
  3433. }else{
  3434. dest[prop] = source[prop];
  3435. }
  3436. }
  3437. }
  3438. },
  3439. getCovVar: function(){
  3440. var opt = _blanket.options("customVariable");
  3441. if (opt){
  3442. if (_blanket.options("debug")) {console.log("BLANKET-Using custom tracking variable:",opt);}
  3443. return inBrowser ? "window."+opt : opt;
  3444. }
  3445. return inBrowser ? "window._$blanket" : "_$jscoverage";
  3446. },
  3447. options: function(key,value){
  3448. if (typeof key !== "string"){
  3449. _blanket._extend(options,key);
  3450. }else if (typeof value === 'undefined'){
  3451. return options[key];
  3452. }else{
  3453. options[key]=value;
  3454. }
  3455. },
  3456. instrument: function(config, next){
  3457. //check instrumented hash table,
  3458. //return instrumented code if available.
  3459. var inFile = config.inputFile,
  3460. inFileName = config.inputFileName;
  3461. //check instrument cache
  3462. if (_blanket.options("instrumentCache") && sessionStorage && sessionStorage.getItem("blanket_instrument_store-"+inFileName)){
  3463. if (_blanket.options("debug")) {console.log("BLANKET-Reading instrumentation from cache: ",inFileName);}
  3464. next(sessionStorage.getItem("blanket_instrument_store-"+inFileName));
  3465. }else{
  3466. var sourceArray = _blanket._prepareSource(inFile);
  3467. _blanket._trackingArraySetup=[];
  3468. //remove shebang
  3469. inFile = inFile.replace(/^\#\!.*/, "");
  3470. var instrumented = parseAndModify(inFile,{loc:true,comment:true}, _blanket._addTracking(inFileName));
  3471. instrumented = _blanket._trackingSetup(inFileName,sourceArray)+instrumented;
  3472. if (_blanket.options("sourceURL")){
  3473. instrumented += "\n//@ sourceURL="+inFileName.replace("http://","");
  3474. }
  3475. if (_blanket.options("debug")) {console.log("BLANKET-Instrumented file: ",inFileName);}
  3476. if (_blanket.options("instrumentCache") && sessionStorage){
  3477. if (_blanket.options("debug")) {console.log("BLANKET-Saving instrumentation to cache: ",inFileName);}
  3478. sessionStorage.setItem("blanket_instrument_store-"+inFileName,instrumented);
  3479. }
  3480. next(instrumented);
  3481. }
  3482. },
  3483. _trackingArraySetup: [],
  3484. _branchingArraySetup: [],
  3485. _prepareSource: function(source){
  3486. return source.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(/(\r\n|\n|\r)/gm,"\n").split('\n');
  3487. },
  3488. _trackingSetup: function(filename,sourceArray){
  3489. var branches = _blanket.options("branchTracking");
  3490. var sourceString = sourceArray.join("',\n'");
  3491. var intro = "";
  3492. var covVar = _blanket.getCovVar();
  3493. intro += "if (typeof "+covVar+" === 'undefined') "+covVar+" = {};\n";
  3494. if (branches){
  3495. intro += "var _$branchFcn=function(f,l,c,r){ ";
  3496. intro += "if (!!r) { ";
  3497. intro += covVar+"[f].branchData[l][c][0] = "+covVar+"[f].branchData[l][c][0] || [];";
  3498. intro += covVar+"[f].branchData[l][c][0].push(r); }";
  3499. intro += "else { ";
  3500. intro += covVar+"[f].branchData[l][c][1] = "+covVar+"[f].branchData[l][c][1] || [];";
  3501. intro += covVar+"[f].branchData[l][c][1].push(r); }";
  3502. intro += "return r;};\n";
  3503. }
  3504. intro += "if (typeof "+covVar+"['"+filename+"'] === 'undefined'){";
  3505. intro += covVar+"['"+filename+"']=[];\n";
  3506. if (branches){
  3507. intro += covVar+"['"+filename+"'].branchData=[];\n";
  3508. }
  3509. intro += covVar+"['"+filename+"'].source=['"+sourceString+"'];\n";
  3510. //initialize array values
  3511. _blanket._trackingArraySetup.sort(function(a,b){
  3512. return parseInt(a,10) > parseInt(b,10);
  3513. }).forEach(function(item){
  3514. intro += covVar+"['"+filename+"']["+item+"]=0;\n";
  3515. });
  3516. if (branches){
  3517. _blanket._branchingArraySetup.sort(function(a,b){
  3518. return a.line > b.line;
  3519. }).sort(function(a,b){
  3520. return a.column > b.column;
  3521. }).forEach(function(item){
  3522. if (item.file === filename){
  3523. intro += "if (typeof "+ covVar+"['"+filename+"'].branchData["+item.line+"] === 'undefined'){\n";
  3524. intro += covVar+"['"+filename+"'].branchData["+item.line+"]=[];\n";
  3525. intro += "}";
  3526. intro += covVar+"['"+filename+"'].branchData["+item.line+"]["+item.column+"] = [];\n";
  3527. intro += covVar+"['"+filename+"'].branchData["+item.line+"]["+item.column+"].consequent = "+JSON.stringify(item.consequent)+";\n";
  3528. intro += covVar+"['"+filename+"'].branchData["+item.line+"]["+item.column+"].alternate = "+JSON.stringify(item.alternate)+";\n";
  3529. }
  3530. });
  3531. }
  3532. intro += "}";
  3533. return intro;
  3534. },
  3535. _blockifyIf: function(node){
  3536. if (linesToAddBrackets.indexOf(node.type) > -1){
  3537. var bracketsExistObject = node.consequent || node.body;
  3538. var bracketsExistAlt = node.alternate;
  3539. if( bracketsExistAlt && bracketsExistAlt.type !== "BlockStatement") {
  3540. bracketsExistAlt.update("{\n"+bracketsExistAlt.source()+"}\n");
  3541. }
  3542. if( bracketsExistObject && bracketsExistObject.type !== "BlockStatement") {
  3543. bracketsExistObject.update("{\n"+bracketsExistObject.source()+"}\n");
  3544. }
  3545. }
  3546. },
  3547. _trackBranch: function(node,filename){
  3548. //recursive on consequent and alternative
  3549. var line = node.loc.start.line;
  3550. var col = node.loc.start.column;
  3551. _blanket._branchingArraySetup.push({
  3552. line: line,
  3553. column: col,
  3554. file:filename,
  3555. consequent: node.consequent.loc,
  3556. alternate: node.alternate.loc
  3557. });
  3558. var updated = "_$branchFcn"+
  3559. "('"+filename+"',"+line+","+col+","+node.test.source()+
  3560. ")?"+node.consequent.source()+":"+node.alternate.source();
  3561. node.update(updated);
  3562. },
  3563. _addTracking: function (filename) {
  3564. //falafel doesn't take a file name
  3565. //so we include the filename in a closure
  3566. //and return the function to falafel
  3567. var covVar = _blanket.getCovVar();
  3568. return function(node){
  3569. _blanket._blockifyIf(node);
  3570. if (linesToAddTracking.indexOf(node.type) > -1 && node.parent.type !== "LabeledStatement") {
  3571. _blanket._checkDefs(node,filename);
  3572. if (node.type === "VariableDeclaration" &&
  3573. (node.parent.type === "ForStatement" || node.parent.type === "ForInStatement")){
  3574. return;
  3575. }
  3576. if (node.loc && node.loc.start){
  3577. node.update(covVar+"['"+filename+"']["+node.loc.start.line+"]++;\n"+node.source());
  3578. _blanket._trackingArraySetup.push(node.loc.start.line);
  3579. }else{
  3580. //I don't think we can handle a node with no location
  3581. throw new Error("The instrumenter encountered a node with no location: "+Object.keys(node));
  3582. }
  3583. }else if (_blanket.options("branchTracking") && node.type === "ConditionalExpression"){
  3584. _blanket._trackBranch(node,filename);
  3585. }
  3586. };
  3587. },
  3588. _checkDefs: function(node,filename){
  3589. // Make sure developers don't redefine window. if they do, inform them it is wrong.
  3590. if (inBrowser){
  3591. if (node.type === "VariableDeclaration" && node.declarations) {
  3592. node.declarations.forEach(function(declaration) {
  3593. if (declaration.id.name === "window") {
  3594. throw new Error("Instrumentation error, you cannot redefine the 'window' variable in " + filename + ":" + node.loc.start.line);
  3595. }
  3596. });
  3597. }
  3598. if (node.type === "FunctionDeclaration" && node.params) {
  3599. node.params.forEach(function(param) {
  3600. if (param.name === "window") {
  3601. throw new Error("Instrumentation error, you cannot redefine the 'window' variable in " + filename + ":" + node.loc.start.line);
  3602. }
  3603. });
  3604. }
  3605. //Make sure developers don't redefine the coverage variable
  3606. if (node.type === "ExpressionStatement" &&
  3607. node.expression && node.expression.left &&
  3608. node.expression.left.object && node.expression.left.property &&
  3609. node.expression.left.object.name +
  3610. "." + node.expression.left.property.name === _blanket.getCovVar()) {
  3611. throw new Error("Instrumentation error, you cannot redefine the coverage variable in " + filename + ":" + node.loc.start.line);
  3612. }
  3613. }else{
  3614. //Make sure developers don't redefine the coverage variable in node
  3615. if (node.type === "ExpressionStatement" &&
  3616. node.expression && node.expression.left &&
  3617. !node.expression.left.object && !node.expression.left.property &&
  3618. node.expression.left.name === _blanket.getCovVar()) {
  3619. throw new Error("Instrumentation error, you cannot redefine the coverage variable in " + filename + ":" + node.loc.start.line);
  3620. }
  3621. }
  3622. },
  3623. setupCoverage: function(){
  3624. coverageInfo.instrumentation = "blanket";
  3625. coverageInfo.stats = {
  3626. "suites": 0,
  3627. "tests": 0,
  3628. "passes": 0,
  3629. "pending": 0,
  3630. "failures": 0,
  3631. "start": new Date()
  3632. };
  3633. },
  3634. _checkIfSetup: function(){
  3635. if (!coverageInfo.stats){
  3636. throw new Error("You must call blanket.setupCoverage() first.");
  3637. }
  3638. },
  3639. onTestStart: function(){
  3640. if (_blanket.options("debug")) {console.log("BLANKET-Test event started");}
  3641. this._checkIfSetup();
  3642. coverageInfo.stats.tests++;
  3643. coverageInfo.stats.pending++;
  3644. },
  3645. onTestDone: function(total,passed){
  3646. this._checkIfSetup();
  3647. if(passed === total){
  3648. coverageInfo.stats.passes++;
  3649. }else{
  3650. coverageInfo.stats.failures++;
  3651. }
  3652. coverageInfo.stats.pending--;
  3653. },
  3654. onModuleStart: function(){
  3655. this._checkIfSetup();
  3656. coverageInfo.stats.suites++;
  3657. },
  3658. onTestsDone: function(){
  3659. if (_blanket.options("debug")) {console.log("BLANKET-Test event done");}
  3660. this._checkIfSetup();
  3661. coverageInfo.stats.end = new Date();
  3662. if (inBrowser){
  3663. this.report(coverageInfo);
  3664. }else{
  3665. if (!_blanket.options("branchTracking")){
  3666. delete (inBrowser ? window : global)[_blanket.getCovVar()].branchFcn;
  3667. }
  3668. this.options("reporter").call(this,coverageInfo);
  3669. }
  3670. }
  3671. };
  3672. return _blanket;
  3673. })();
  3674. (function(_blanket){
  3675. var oldOptions = _blanket.options;
  3676. _blanket.extend({
  3677. outstandingRequireFiles:[],
  3678. options: function(key,value){
  3679. var newVal={};
  3680. if (typeof key !== "string"){
  3681. //key is key/value map
  3682. oldOptions(key);
  3683. newVal = key;
  3684. }else if (typeof value === 'undefined'){
  3685. //accessor
  3686. return oldOptions(key);
  3687. }else{
  3688. //setter
  3689. oldOptions(key,value);
  3690. newVal[key] = value;
  3691. }
  3692. if (newVal.adapter){
  3693. _blanket._loadFile(newVal.adapter);
  3694. }
  3695. if (newVal.loader){
  3696. _blanket._loadFile(newVal.loader);
  3697. }
  3698. },
  3699. requiringFile: function(filename,done){
  3700. if (typeof filename === "undefined"){
  3701. _blanket.outstandingRequireFiles=[];
  3702. }else if (typeof done === "undefined"){
  3703. _blanket.outstandingRequireFiles.push(filename);
  3704. }else{
  3705. _blanket.outstandingRequireFiles.splice(_blanket.outstandingRequireFiles.indexOf(filename),1);
  3706. }
  3707. },
  3708. requireFilesLoaded: function(){
  3709. return _blanket.outstandingRequireFiles.length === 0;
  3710. },
  3711. showManualLoader: function(){
  3712. if (document.getElementById("blanketLoaderDialog")){
  3713. return;
  3714. }
  3715. //copied from http://blog.avtex.com/2012/01/26/cross-browser-css-only-modal-box/
  3716. var loader = "<div class='blanketDialogOverlay'>";
  3717. loader += "&nbsp;</div>";
  3718. loader += "<div class='blanketDialogVerticalOffset'>";
  3719. loader += "<div class='blanketDialogBox'>";
  3720. loader += "<b>Error:</b> Blanket.js encountered a cross origin request error while instrumenting the source files. ";
  3721. loader += "<br><br>This is likely caused by the source files being referenced locally (using the file:// protocol). ";
  3722. loader += "<br><br>Some solutions include <a href='http://askubuntu.com/questions/160245/making-google-chrome-option-allow-file-access-from-files-permanent' target='_blank'>starting Chrome with special flags</a>, <a target='_blank' href='https://github.com/remy/servedir'>running a server locally</a>, or using a browser without these CORS restrictions (Safari).";
  3723. loader += "<br>";
  3724. if (typeof FileReader !== "undefined"){
  3725. loader += "<br>Or, try the experimental loader. When prompted, simply click on the directory containing all the source files you want covered.";
  3726. loader += "<a href='javascript:document.getElementById(\"fileInput\").click();'>Start Loader</a>";
  3727. loader += "<input type='file' type='application/x-javascript' accept='application/x-javascript' webkitdirectory id='fileInput' multiple onchange='window.blanket.manualFileLoader(this.files)' style='visibility:hidden;position:absolute;top:-50;left:-50'/>";
  3728. }
  3729. loader += "<br><span style='float:right;cursor:pointer;' onclick=document.getElementById('blanketLoaderDialog').style.display='none';>Close</span>";
  3730. loader += "<div style='clear:both'></div>";
  3731. loader += "</div></div>";
  3732. var css = ".blanketDialogWrapper {";
  3733. css += "display:block;";
  3734. css += "position:fixed;";
  3735. css += "z-index:40001; }";
  3736. css += ".blanketDialogOverlay {";
  3737. css += "position:fixed;";
  3738. css += "width:100%;";
  3739. css += "height:100%;";
  3740. css += "background-color:black;";
  3741. css += "opacity:.5; ";
  3742. css += "-ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=50)'; ";
  3743. css += "filter:alpha(opacity=50); ";
  3744. css += "z-index:40001; }";
  3745. css += ".blanketDialogVerticalOffset { ";
  3746. css += "position:fixed;";
  3747. css += "top:30%;";
  3748. css += "width:100%;";
  3749. css += "z-index:40002; }";
  3750. css += ".blanketDialogBox { ";
  3751. css += "width:405px; ";
  3752. css += "position:relative;";
  3753. css += "margin:0 auto;";
  3754. css += "background-color:white;";
  3755. css += "padding:10px;";
  3756. css += "border:1px solid black; }";
  3757. var dom = document.createElement("style");
  3758. dom.innerHTML = css;
  3759. document.head.appendChild(dom);
  3760. var div = document.createElement("div");
  3761. div.id = "blanketLoaderDialog";
  3762. div.className = "blanketDialogWrapper";
  3763. div.innerHTML = loader;
  3764. document.body.insertBefore(div,document.body.firstChild);
  3765. },
  3766. manualFileLoader: function(files){
  3767. var toArray =Array.prototype.slice;
  3768. files = toArray.call(files).filter(function(item){
  3769. return item.type !== "";
  3770. });
  3771. var sessionLength = files.length-1;
  3772. var sessionIndx=0;
  3773. var sessionArray = {};
  3774. if (sessionStorage["blanketSessionLoader"]){
  3775. sessionArray = JSON.parse(sessionStorage["blanketSessionLoader"]);
  3776. }
  3777. var fileLoader = function(event){
  3778. var fileContent = event.currentTarget.result;
  3779. var file = files[sessionIndx];
  3780. var filename = file.webkitRelativePath && file.webkitRelativePath !== '' ? file.webkitRelativePath : file.name;
  3781. sessionArray[filename] = fileContent;
  3782. sessionIndx++;
  3783. if (sessionIndx === sessionLength){
  3784. sessionStorage.setItem("blanketSessionLoader", JSON.stringify(sessionArray));
  3785. document.location.reload();
  3786. }else{
  3787. readFile(files[sessionIndx]);
  3788. }
  3789. };
  3790. function readFile(file){
  3791. var reader = new FileReader();
  3792. reader.onload = fileLoader;
  3793. reader.readAsText(file);
  3794. }
  3795. readFile(files[sessionIndx]);
  3796. },
  3797. _loadFile: function(path){
  3798. if (typeof path !== "undefined"){
  3799. var request = new XMLHttpRequest();
  3800. request.open('GET', path, false);
  3801. request.send();
  3802. _blanket._addScript(request.responseText);
  3803. }
  3804. },
  3805. _addScript: function(data){
  3806. var script = document.createElement("script");
  3807. script.type = "text/javascript";
  3808. script.text = data;
  3809. (document.body || document.getElementsByTagName('head')[0]).appendChild(script);
  3810. },
  3811. hasAdapter: function(callback){
  3812. return _blanket.options("adapter") !== null;
  3813. },
  3814. report: function(coverage_data){
  3815. if (!document.getElementById("blanketLoaderDialog")){
  3816. //all found, clear it
  3817. _blanket.blanketSession = null;
  3818. }
  3819. coverage_data.files = window._$blanket;
  3820. var require = blanket.options("commonJS") ? blanket._commonjs.require : window.require;
  3821. // Check if we have any covered files that requires reporting
  3822. // otherwise just exit gracefully.
  3823. if (!coverage_data.files || !Object.keys(coverage_data.files).length) {
  3824. if (_blanket.options("debug")) {console.log("BLANKET-Reporting No files were instrumented.");}
  3825. return;
  3826. }
  3827. if (typeof coverage_data.files.branchFcn !== "undefined"){
  3828. delete coverage_data.files.branchFcn;
  3829. }
  3830. if (typeof _blanket.options("reporter") === "string"){
  3831. _blanket._loadFile(_blanket.options("reporter"));
  3832. _blanket.customReporter(coverage_data,_blanket.options("reporter_options"));
  3833. }else if (typeof _blanket.options("reporter") === "function"){
  3834. _blanket.options("reporter")(coverage_data,_blanket.options("reporter_options"));
  3835. }else if (typeof _blanket.defaultReporter === 'function'){
  3836. _blanket.defaultReporter(coverage_data,_blanket.options("reporter_options"));
  3837. }else{
  3838. throw new Error("no reporter defined.");
  3839. }
  3840. },
  3841. _bindStartTestRunner: function(bindEvent,startEvent){
  3842. if (bindEvent){
  3843. bindEvent(startEvent);
  3844. }else{
  3845. window.addEventListener("load",startEvent,false);
  3846. }
  3847. },
  3848. _loadSourceFiles: function(callback){
  3849. var require = blanket.options("commonJS") ? blanket._commonjs.require : window.require;
  3850. function copy(o){
  3851. var _copy = Object.create( Object.getPrototypeOf(o) );
  3852. var propNames = Object.getOwnPropertyNames(o);
  3853. propNames.forEach(function(name){
  3854. var desc = Object.getOwnPropertyDescriptor(o, name);
  3855. Object.defineProperty(_copy, name, desc);
  3856. });
  3857. return _copy;
  3858. }
  3859. if (_blanket.options("debug")) {console.log("BLANKET-Collecting page scripts");}
  3860. var scripts = _blanket.utils.collectPageScripts();
  3861. //_blanket.options("filter",scripts);
  3862. if (scripts.length === 0){
  3863. callback();
  3864. }else{
  3865. //check session state
  3866. if (sessionStorage["blanketSessionLoader"]){
  3867. _blanket.blanketSession = JSON.parse(sessionStorage["blanketSessionLoader"]);
  3868. }
  3869. scripts.forEach(function(file,indx){
  3870. _blanket.utils.cache[file]={
  3871. loaded:false
  3872. };
  3873. });
  3874. var currScript=-1;
  3875. _blanket.utils.loadAll(function(test){
  3876. if (test){
  3877. return typeof scripts[currScript+1] !== 'undefined';
  3878. }
  3879. currScript++;
  3880. if (currScript >= scripts.length){
  3881. return null;
  3882. }
  3883. return scripts[currScript];
  3884. },callback);
  3885. }
  3886. },
  3887. beforeStartTestRunner: function(opts){
  3888. opts = opts || {};
  3889. opts.checkRequirejs = typeof opts.checkRequirejs === "undefined" ? true : opts.checkRequirejs;
  3890. opts.callback = opts.callback || function() { };
  3891. opts.coverage = typeof opts.coverage === "undefined" ? true : opts.coverage;
  3892. if (opts.coverage) {
  3893. _blanket._bindStartTestRunner(opts.bindEvent,
  3894. function(){
  3895. _blanket._loadSourceFiles(function() {
  3896. var allLoaded = function(){
  3897. return opts.condition ? opts.condition() : _blanket.requireFilesLoaded();
  3898. };
  3899. var check = function() {
  3900. if (allLoaded()) {
  3901. if (_blanket.options("debug")) {console.log("BLANKET-All files loaded, init start test runner callback.");}
  3902. var cb = _blanket.options("testReadyCallback");
  3903. if (cb){
  3904. if (typeof cb === "function"){
  3905. cb(opts.callback);
  3906. }else if (typeof cb === "string"){
  3907. _blanket._addScript(cb);
  3908. opts.callback();
  3909. }
  3910. }else{
  3911. opts.callback();
  3912. }
  3913. } else {
  3914. setTimeout(check, 13);
  3915. }
  3916. };
  3917. check();
  3918. });
  3919. });
  3920. }else{
  3921. opts.callback();
  3922. }
  3923. },
  3924. utils: {
  3925. qualifyURL: function (url) {
  3926. //http://stackoverflow.com/questions/470832/getting-an-absolute-url-from-a-relative-one-ie6-issue
  3927. var a = document.createElement('a');
  3928. a.href = url;
  3929. return a.href;
  3930. }
  3931. }
  3932. });
  3933. })(blanket);
  3934. blanket.defaultReporter = function(coverage){
  3935. var cssSytle = "#blanket-main {margin:2px;background:#EEE;color:#333;clear:both;font-family:'Helvetica Neue Light', 'HelveticaNeue-Light', 'Helvetica Neue', Calibri, Helvetica, Arial, sans-serif; font-size:17px;} #blanket-main a {color:#333;text-decoration:none;} #blanket-main a:hover {text-decoration:underline;} .blanket {margin:0;padding:5px;clear:both;border-bottom: 1px solid #FFFFFF;} .bl-error {color:red;}.bl-success {color:#5E7D00;} .bl-file{width:auto;} .bl-cl{float:left;} .blanket div.rs {margin-left:50px; width:150px; float:right} .bl-nb {padding-right:10px;} #blanket-main a.bl-logo {color: #EB1764;cursor: pointer;font-weight: bold;text-decoration: none} .bl-source{ overflow-x:scroll; background-color: #FFFFFF; border: 1px solid #CBCBCB; color: #363636; margin: 25px 20px; width: 80%;} .bl-source div{white-space: pre;font-family: monospace;} .bl-source > div > span:first-child{background-color: #EAEAEA;color: #949494;display: inline-block;padding: 0 10px;text-align: center;width: 30px;} .bl-source .miss{background-color:#e6c3c7} .bl-source span.branchWarning{color:#000;background-color:yellow;} .bl-source span.branchOkay{color:#000;background-color:transparent;}",
  3936. successRate = 60,
  3937. head = document.head,
  3938. fileNumber = 0,
  3939. body = document.body,
  3940. headerContent,
  3941. hasBranchTracking = Object.keys(coverage.files).some(function(elem){
  3942. return typeof coverage.files[elem].branchData !== 'undefined';
  3943. }),
  3944. bodyContent = "<div id='blanket-main'><div class='blanket bl-title'><div class='bl-cl bl-file'><a href='http://alex-seville.github.com/blanket/' target='_blank' class='bl-logo'>Blanket.js</a> results</div><div class='bl-cl rs'>Coverage (%)</div><div class='bl-cl rs'>Covered/Total Smts.</div>"+(hasBranchTracking ? "<div class='bl-cl rs'>Covered/Total Branches</div>":"")+"<div style='clear:both;'></div></div>",
  3945. fileTemplate = "<div class='blanket {{statusclass}}'><div class='bl-cl bl-file'><span class='bl-nb'>{{fileNumber}}.</span><a href='javascript:blanket_toggleSource(\"file-{{fileNumber}}\")'>{{file}}</a></div><div class='bl-cl rs'>{{percentage}} %</div><div class='bl-cl rs'>{{numberCovered}}/{{totalSmts}}</div>"+( hasBranchTracking ? "<div class='bl-cl rs'>{{passedBranches}}/{{totalBranches}}</div>" : "" )+"<div id='file-{{fileNumber}}' class='bl-source' style='display:none;'>{{source}}</div><div style='clear:both;'></div></div>";
  3946. grandTotalTemplate = "<div class='blanket grand-total {{statusclass}}'><div class='bl-cl'>{{rowTitle}}</div><div class='bl-cl rs'>{{percentage}} %</div><div class='bl-cl rs'>{{numberCovered}}/{{totalSmts}}</div>"+( hasBranchTracking ? "<div class='bl-cl rs'>{{passedBranches}}/{{totalBranches}}</div>" : "" ) + "<div style='clear:both;'></div></div>";
  3947. function blanket_toggleSource(id) {
  3948. var element = document.getElementById(id);
  3949. if(element.style.display === 'block') {
  3950. element.style.display = 'none';
  3951. } else {
  3952. element.style.display = 'block';
  3953. }
  3954. }
  3955. var script = document.createElement("script");
  3956. script.type = "text/javascript";
  3957. script.text = blanket_toggleSource.toString().replace('function ' + blanket_toggleSource.name, 'function blanket_toggleSource');
  3958. body.appendChild(script);
  3959. var percentage = function(number, total) {
  3960. return (Math.round(((number/total) * 100)*100)/100);
  3961. };
  3962. var appendTag = function (type, el, str) {
  3963. var dom = document.createElement(type);
  3964. dom.innerHTML = str;
  3965. el.appendChild(dom);
  3966. };
  3967. function escapeInvalidXmlChars(str) {
  3968. return str.replace(/\&/g, "&amp;")
  3969. .replace(/</g, "&lt;")
  3970. .replace(/\>/g, "&gt;")
  3971. .replace(/\"/g, "&quot;")
  3972. .replace(/\'/g, "&apos;");
  3973. }
  3974. function isBranchFollowed(data,bool){
  3975. var mode = bool ? 0 : 1;
  3976. if (typeof data === 'undefined' ||
  3977. typeof data === null ||
  3978. typeof data[mode] === 'undefined'){
  3979. return false;
  3980. }
  3981. return data[mode].length > 0;
  3982. }
  3983. var branchStack = [];
  3984. function branchReport(colsIndex,src,cols,offset,lineNum){
  3985. var newsrc="";
  3986. var postfix="";
  3987. if (branchStack.length > 0){
  3988. newsrc += "<span class='" + (isBranchFollowed(branchStack[0][1],branchStack[0][1].consequent === branchStack[0][0]) ? 'branchOkay' : 'branchWarning') + "'>";
  3989. if (branchStack[0][0].end.line === lineNum){
  3990. newsrc += escapeInvalidXmlChars(src.slice(0,branchStack[0][0].end.column)) + "</span>";
  3991. src = src.slice(branchStack[0][0].end.column);
  3992. branchStack.shift();
  3993. if (branchStack.length > 0){
  3994. newsrc += "<span class='" + (isBranchFollowed(branchStack[0][1],false) ? 'branchOkay' : 'branchWarning') + "'>";
  3995. if (branchStack[0][0].end.line === lineNum){
  3996. newsrc += escapeInvalidXmlChars(src.slice(0,branchStack[0][0].end.column)) + "</span>";
  3997. src = src.slice(branchStack[0][0].end.column);
  3998. branchStack.shift();
  3999. if (!cols){
  4000. return {src: newsrc + escapeInvalidXmlChars(src) ,cols:cols};
  4001. }
  4002. }
  4003. else if (!cols){
  4004. return {src: newsrc + escapeInvalidXmlChars(src) + "</span>",cols:cols};
  4005. }
  4006. else{
  4007. postfix = "</span>";
  4008. }
  4009. }else if (!cols){
  4010. return {src: newsrc + escapeInvalidXmlChars(src) ,cols:cols};
  4011. }
  4012. }else if(!cols){
  4013. return {src: newsrc + escapeInvalidXmlChars(src) + "</span>",cols:cols};
  4014. }else{
  4015. postfix = "</span>";
  4016. }
  4017. }
  4018. var thisline = cols[colsIndex];
  4019. //consequent
  4020. var cons = thisline.consequent;
  4021. if (cons.start.line > lineNum){
  4022. branchStack.unshift([thisline.alternate,thisline]);
  4023. branchStack.unshift([cons,thisline]);
  4024. src = escapeInvalidXmlChars(src);
  4025. }else{
  4026. var style = "<span class='" + (isBranchFollowed(thisline,true) ? 'branchOkay' : 'branchWarning') + "'>";
  4027. newsrc += escapeInvalidXmlChars(src.slice(0,cons.start.column-offset)) + style;
  4028. if (cols.length > colsIndex+1 &&
  4029. cols[colsIndex+1].consequent.start.line === lineNum &&
  4030. cols[colsIndex+1].consequent.start.column-offset < cols[colsIndex].consequent.end.column-offset)
  4031. {
  4032. var res = branchReport(colsIndex+1,src.slice(cons.start.column-offset,cons.end.column-offset),cols,cons.start.column-offset,lineNum);
  4033. newsrc += res.src;
  4034. cols = res.cols;
  4035. cols[colsIndex+1] = cols[colsIndex+2];
  4036. cols.length--;
  4037. }else{
  4038. newsrc += escapeInvalidXmlChars(src.slice(cons.start.column-offset,cons.end.column-offset));
  4039. }
  4040. newsrc += "</span>";
  4041. var alt = thisline.alternate;
  4042. if (alt.start.line > lineNum){
  4043. newsrc += escapeInvalidXmlChars(src.slice(cons.end.column-offset));
  4044. branchStack.unshift([alt,thisline]);
  4045. }else{
  4046. newsrc += escapeInvalidXmlChars(src.slice(cons.end.column-offset,alt.start.column-offset));
  4047. style = "<span class='" + (isBranchFollowed(thisline,false) ? 'branchOkay' : 'branchWarning') + "'>";
  4048. newsrc += style;
  4049. if (cols.length > colsIndex+1 &&
  4050. cols[colsIndex+1].consequent.start.line === lineNum &&
  4051. cols[colsIndex+1].consequent.start.column-offset < cols[colsIndex].alternate.end.column-offset)
  4052. {
  4053. var res2 = branchReport(colsIndex+1,src.slice(alt.start.column-offset,alt.end.column-offset),cols,alt.start.column-offset,lineNum);
  4054. newsrc += res2.src;
  4055. cols = res2.cols;
  4056. cols[colsIndex+1] = cols[colsIndex+2];
  4057. cols.length--;
  4058. }else{
  4059. newsrc += escapeInvalidXmlChars(src.slice(alt.start.column-offset,alt.end.column-offset));
  4060. }
  4061. newsrc += "</span>";
  4062. newsrc += escapeInvalidXmlChars(src.slice(alt.end.column-offset));
  4063. src = newsrc;
  4064. }
  4065. }
  4066. return {src:src+postfix, cols:cols};
  4067. }
  4068. var isUndefined = function(item){
  4069. return typeof item !== 'undefined';
  4070. };
  4071. var files = coverage.files;
  4072. var totals = {
  4073. totalSmts: 0,
  4074. numberOfFilesCovered: 0,
  4075. passedBranches: 0,
  4076. totalBranches: 0,
  4077. moduleTotalStatements : {},
  4078. moduleTotalCoveredStatements : {},
  4079. moduleTotalBranches : {},
  4080. moduleTotalCoveredBranches : {}
  4081. };
  4082. // check if a data-cover-modulepattern was provided for per-module coverage reporting
  4083. var modulePattern = _blanket.options("modulePattern");
  4084. var modulePatternRegex = ( modulePattern ? new RegExp(modulePattern) : null );
  4085. for(var file in files)
  4086. {
  4087. fileNumber++;
  4088. var statsForFile = files[file],
  4089. totalSmts = 0,
  4090. numberOfFilesCovered = 0,
  4091. code = [],
  4092. i;
  4093. var end = [];
  4094. for(i = 0; i < statsForFile.source.length; i +=1){
  4095. var src = statsForFile.source[i];
  4096. if (branchStack.length > 0 ||
  4097. typeof statsForFile.branchData !== 'undefined')
  4098. {
  4099. if (typeof statsForFile.branchData[i+1] !== 'undefined')
  4100. {
  4101. var cols = statsForFile.branchData[i+1].filter(isUndefined);
  4102. var colsIndex=0;
  4103. src = branchReport(colsIndex,src,cols,0,i+1).src;
  4104. }else if (branchStack.length){
  4105. src = branchReport(0,src,null,0,i+1).src;
  4106. }else{
  4107. src = escapeInvalidXmlChars(src);
  4108. }
  4109. }else{
  4110. src = escapeInvalidXmlChars(src);
  4111. }
  4112. var lineClass="";
  4113. if(statsForFile[i+1]) {
  4114. numberOfFilesCovered += 1;
  4115. totalSmts += 1;
  4116. lineClass = 'hit';
  4117. }else{
  4118. if(statsForFile[i+1] === 0){
  4119. totalSmts++;
  4120. lineClass = 'miss';
  4121. }
  4122. }
  4123. code[i + 1] = "<div class='"+lineClass+"'><span class=''>"+(i + 1)+"</span>"+src+"</div>";
  4124. }
  4125. totals.totalSmts += totalSmts;
  4126. totals.numberOfFilesCovered += numberOfFilesCovered;
  4127. var totalBranches=0;
  4128. var passedBranches=0;
  4129. if (typeof statsForFile.branchData !== 'undefined'){
  4130. for(var j=0;j<statsForFile.branchData.length;j++){
  4131. if (typeof statsForFile.branchData[j] !== 'undefined'){
  4132. for(var k=0;k<statsForFile.branchData[j].length;k++){
  4133. if (typeof statsForFile.branchData[j][k] !== 'undefined'){
  4134. totalBranches++;
  4135. if (typeof statsForFile.branchData[j][k][0] !== 'undefined' &&
  4136. statsForFile.branchData[j][k][0].length > 0 &&
  4137. typeof statsForFile.branchData[j][k][1] !== 'undefined' &&
  4138. statsForFile.branchData[j][k][1].length > 0){
  4139. passedBranches++;
  4140. }
  4141. }
  4142. }
  4143. }
  4144. }
  4145. }
  4146. totals.passedBranches += passedBranches;
  4147. totals.totalBranches += totalBranches;
  4148. // if "data-cover-modulepattern" was provided,
  4149. // track totals per module name as well as globally
  4150. if (modulePatternRegex) {
  4151. var moduleName = file.match(modulePatternRegex)[1];
  4152. if(!totals.moduleTotalStatements.hasOwnProperty(moduleName)) {
  4153. totals.moduleTotalStatements[moduleName] = 0;
  4154. totals.moduleTotalCoveredStatements[moduleName] = 0;
  4155. }
  4156. totals.moduleTotalStatements[moduleName] += totalSmts;
  4157. totals.moduleTotalCoveredStatements[moduleName] += numberOfFilesCovered;
  4158. if(!totals.moduleTotalBranches.hasOwnProperty(moduleName)) {
  4159. totals.moduleTotalBranches[moduleName] = 0;
  4160. totals.moduleTotalCoveredBranches[moduleName] = 0;
  4161. }
  4162. totals.moduleTotalBranches[moduleName] += totalBranches;
  4163. totals.moduleTotalCoveredBranches[moduleName] += passedBranches;
  4164. }
  4165. var result = percentage(numberOfFilesCovered, totalSmts);
  4166. var output = fileTemplate.replace("{{file}}", file)
  4167. .replace("{{percentage}}",result)
  4168. .replace("{{numberCovered}}", numberOfFilesCovered)
  4169. .replace(/\{\{fileNumber\}\}/g, fileNumber)
  4170. .replace("{{totalSmts}}", totalSmts)
  4171. .replace("{{totalBranches}}", totalBranches)
  4172. .replace("{{passedBranches}}", passedBranches)
  4173. .replace("{{source}}", code.join(" "));
  4174. if(result < successRate)
  4175. {
  4176. output = output.replace("{{statusclass}}", "bl-error");
  4177. } else {
  4178. output = output.replace("{{statusclass}}", "bl-success");
  4179. }
  4180. bodyContent += output;
  4181. }
  4182. // create temporary function for use by the global totals reporter,
  4183. // as well as the per-module totals reporter
  4184. var createAggregateTotal = function(numSt, numCov, numBranch, numCovBr, moduleName) {
  4185. var totalPercent = percentage(numCov, numSt);
  4186. var statusClass = totalPercent < successRate ? "bl-error" : "bl-success";
  4187. var rowTitle = ( moduleName ? "Total for module: " + moduleName : "Global total" );
  4188. var totalsOutput = grandTotalTemplate.replace("{{rowTitle}}", rowTitle)
  4189. .replace("{{percentage}}", totalPercent)
  4190. .replace("{{numberCovered}}", numCov)
  4191. .replace("{{totalSmts}}", numSt)
  4192. .replace("{{passedBranches}}", numCovBr)
  4193. .replace("{{totalBranches}}", numBranch)
  4194. .replace("{{statusclass}}", statusClass);
  4195. bodyContent += totalsOutput;
  4196. };
  4197. // if "data-cover-modulepattern" was provided,
  4198. // output the per-module totals alongside the global totals
  4199. if (modulePatternRegex) {
  4200. for (var thisModuleName in totals.moduleTotalStatements) {
  4201. if (totals.moduleTotalStatements.hasOwnProperty(thisModuleName)) {
  4202. var moduleTotalSt = totals.moduleTotalStatements[thisModuleName];
  4203. var moduleTotalCovSt = totals.moduleTotalCoveredStatements[thisModuleName];
  4204. var moduleTotalBr = totals.moduleTotalBranches[thisModuleName];
  4205. var moduleTotalCovBr = totals.moduleTotalCoveredBranches[thisModuleName];
  4206. createAggregateTotal(moduleTotalSt, moduleTotalCovSt, moduleTotalBr, moduleTotalCovBr, thisModuleName);
  4207. }
  4208. }
  4209. }
  4210. createAggregateTotal(totals.totalSmts, totals.numberOfFilesCovered, totals.totalBranches, totals.passedBranches, null);
  4211. bodyContent += "</div>"; //closing main
  4212. appendTag('style', head, cssSytle);
  4213. //appendStyle(body, headerContent);
  4214. if (document.getElementById("blanket-main")){
  4215. document.getElementById("blanket-main").innerHTML=
  4216. bodyContent.slice(23,-6);
  4217. }else{
  4218. appendTag('div', body, bodyContent);
  4219. }
  4220. //appendHtml(body, '</div>');
  4221. };
  4222. (function(){
  4223. var newOptions={};
  4224. //http://stackoverflow.com/a/2954896
  4225. var toArray =Array.prototype.slice;
  4226. var scripts = toArray.call(document.scripts);
  4227. toArray.call(scripts[scripts.length - 1].attributes)
  4228. .forEach(function(es){
  4229. if(es.nodeName === "data-cover-only"){
  4230. newOptions.filter = es.nodeValue;
  4231. }
  4232. if(es.nodeName === "data-cover-never"){
  4233. newOptions.antifilter = es.nodeValue;
  4234. }
  4235. if(es.nodeName === "data-cover-reporter"){
  4236. newOptions.reporter = es.nodeValue;
  4237. }
  4238. if (es.nodeName === "data-cover-adapter"){
  4239. newOptions.adapter = es.nodeValue;
  4240. }
  4241. if (es.nodeName === "data-cover-loader"){
  4242. newOptions.loader = es.nodeValue;
  4243. }
  4244. if (es.nodeName === "data-cover-timeout"){
  4245. newOptions.timeout = es.nodeValue;
  4246. }
  4247. if (es.nodeName === "data-cover-modulepattern") {
  4248. newOptions.modulePattern = es.nodeValue;
  4249. }
  4250. if (es.nodeName === "data-cover-reporter-options"){
  4251. try{
  4252. newOptions.reporter_options = JSON.parse(es.nodeValue);
  4253. }catch(e){
  4254. if (blanket.options("debug")){
  4255. throw new Error("Invalid reporter options. Must be a valid stringified JSON object.");
  4256. }
  4257. }
  4258. }
  4259. if (es.nodeName === "data-cover-testReadyCallback"){
  4260. newOptions.testReadyCallback = es.nodeValue;
  4261. }
  4262. if (es.nodeName === "data-cover-customVariable"){
  4263. newOptions.customVariable = es.nodeValue;
  4264. }
  4265. if (es.nodeName === "data-cover-flags"){
  4266. var flags = " "+es.nodeValue+" ";
  4267. if (flags.indexOf(" ignoreError ") > -1){
  4268. newOptions.ignoreScriptError = true;
  4269. }
  4270. if (flags.indexOf(" autoStart ") > -1){
  4271. newOptions.autoStart = true;
  4272. }
  4273. if (flags.indexOf(" ignoreCors ") > -1){
  4274. newOptions.ignoreCors = true;
  4275. }
  4276. if (flags.indexOf(" branchTracking ") > -1){
  4277. newOptions.branchTracking = true;
  4278. }
  4279. if (flags.indexOf(" sourceURL ") > -1){
  4280. newOptions.sourceURL = true;
  4281. }
  4282. if (flags.indexOf(" debug ") > -1){
  4283. newOptions.debug = true;
  4284. }
  4285. if (flags.indexOf(" engineOnly ") > -1){
  4286. newOptions.engineOnly = true;
  4287. }
  4288. if (flags.indexOf(" commonJS ") > -1){
  4289. newOptions.commonJS = true;
  4290. }
  4291. if (flags.indexOf(" instrumentCache ") > -1){
  4292. newOptions.instrumentCache = true;
  4293. }
  4294. }
  4295. });
  4296. blanket.options(newOptions);
  4297. if (typeof requirejs !== 'undefined'){
  4298. blanket.options("existingRequireJS",true);
  4299. }
  4300. /* setup requirejs loader, if needed */
  4301. if (blanket.options("commonJS")){
  4302. blanket._commonjs = {};
  4303. }
  4304. })();
  4305. (function(_blanket){
  4306. _blanket.extend({
  4307. utils: {
  4308. normalizeBackslashes: function(str) {
  4309. return str.replace(/\\/g, '/');
  4310. },
  4311. matchPatternAttribute: function(filename,pattern){
  4312. if (typeof pattern === 'string'){
  4313. if (pattern.indexOf("[") === 0){
  4314. //treat as array
  4315. var pattenArr = pattern.slice(1,pattern.length-1).split(",");
  4316. return pattenArr.some(function(elem){
  4317. return _blanket.utils.matchPatternAttribute(filename,_blanket.utils.normalizeBackslashes(elem.slice(1,-1)));
  4318. //return filename.indexOf(_blanket.utils.normalizeBackslashes(elem.slice(1,-1))) > -1;
  4319. });
  4320. }else if ( pattern.indexOf("//") === 0){
  4321. var ex = pattern.slice(2,pattern.lastIndexOf('/'));
  4322. var mods = pattern.slice(pattern.lastIndexOf('/')+1);
  4323. var regex = new RegExp(ex,mods);
  4324. return regex.test(filename);
  4325. }else if (pattern.indexOf("#") === 0){
  4326. return window[pattern.slice(1)].call(window,filename);
  4327. }else{
  4328. return filename.indexOf(_blanket.utils.normalizeBackslashes(pattern)) > -1;
  4329. }
  4330. }else if ( pattern instanceof Array ){
  4331. return pattern.some(function(elem){
  4332. return _blanket.utils.matchPatternAttribute(filename,elem);
  4333. });
  4334. }else if (pattern instanceof RegExp){
  4335. return pattern.test(filename);
  4336. }else if (typeof pattern === "function"){
  4337. return pattern.call(window,filename);
  4338. }
  4339. },
  4340. blanketEval: function(data){
  4341. _blanket._addScript(data);
  4342. },
  4343. collectPageScripts: function(){
  4344. var toArray = Array.prototype.slice;
  4345. var scripts = toArray.call(document.scripts);
  4346. var selectedScripts=[],scriptNames=[];
  4347. var filter = _blanket.options("filter");
  4348. if(filter != null){
  4349. //global filter in place, data-cover-only
  4350. var antimatch = _blanket.options("antifilter");
  4351. selectedScripts = toArray.call(document.scripts)
  4352. .filter(function(s){
  4353. return toArray.call(s.attributes).filter(function(sn){
  4354. return sn.nodeName === "src" && _blanket.utils.matchPatternAttribute(sn.nodeValue,filter) &&
  4355. (typeof antimatch === "undefined" || !_blanket.utils.matchPatternAttribute(sn.nodeValue,antimatch));
  4356. }).length === 1;
  4357. });
  4358. }else{
  4359. selectedScripts = toArray.call(document.querySelectorAll("script[data-cover]"));
  4360. }
  4361. scriptNames = selectedScripts.map(function(s){
  4362. return _blanket.utils.qualifyURL(
  4363. toArray.call(s.attributes).filter(
  4364. function(sn){
  4365. return sn.nodeName === "src";
  4366. })[0].nodeValue);
  4367. });
  4368. if (!filter){
  4369. _blanket.options("filter","['"+scriptNames.join("','")+"']");
  4370. }
  4371. return scriptNames;
  4372. },
  4373. loadAll: function(nextScript,cb,preprocessor){
  4374. /**
  4375. * load dependencies
  4376. * @param {nextScript} factory for priority level
  4377. * @param {cb} the done callback
  4378. */
  4379. var currScript=nextScript();
  4380. var isLoaded = _blanket.utils.scriptIsLoaded(
  4381. currScript,
  4382. _blanket.utils.ifOrdered,
  4383. nextScript,
  4384. cb
  4385. );
  4386. if (!(_blanket.utils.cache[currScript] && _blanket.utils.cache[currScript].loaded)){
  4387. var attach = function(){
  4388. if (_blanket.options("debug")) {console.log("BLANKET-Mark script:"+currScript+", as loaded and move to next script.");}
  4389. isLoaded();
  4390. };
  4391. var whenDone = function(result){
  4392. if (_blanket.options("debug")) {console.log("BLANKET-File loading finished");}
  4393. if (typeof result !== 'undefined'){
  4394. if (_blanket.options("debug")) {console.log("BLANKET-Add file to DOM.");}
  4395. _blanket._addScript(result);
  4396. }
  4397. attach();
  4398. };
  4399. _blanket.utils.attachScript(
  4400. {
  4401. url: currScript
  4402. },
  4403. function (content){
  4404. _blanket.utils.processFile(
  4405. content,
  4406. currScript,
  4407. whenDone,
  4408. whenDone
  4409. );
  4410. }
  4411. );
  4412. }else{
  4413. isLoaded();
  4414. }
  4415. },
  4416. attachScript: function(options,cb){
  4417. var timeout = _blanket.options("timeout") || 3000;
  4418. setTimeout(function(){
  4419. if (!_blanket.utils.cache[options.url].loaded){
  4420. throw new Error("error loading source script");
  4421. }
  4422. },timeout);
  4423. _blanket.utils.getFile(
  4424. options.url,
  4425. cb,
  4426. function(){ throw new Error("error loading source script");}
  4427. );
  4428. },
  4429. ifOrdered: function(nextScript,cb){
  4430. /**
  4431. * ordered loading callback
  4432. * @param {nextScript} factory for priority level
  4433. * @param {cb} the done callback
  4434. */
  4435. var currScript = nextScript(true);
  4436. if (currScript){
  4437. _blanket.utils.loadAll(nextScript,cb);
  4438. }else{
  4439. cb(new Error("Error in loading chain."));
  4440. }
  4441. },
  4442. scriptIsLoaded: function(url,orderedCb,nextScript,cb){
  4443. /**
  4444. * returns a callback that checks a loading list to see if a script is loaded.
  4445. * @param {orderedCb} callback if ordered loading is being done
  4446. * @param {nextScript} factory for next priority level
  4447. * @param {cb} the done callback
  4448. */
  4449. if (_blanket.options("debug")) {console.log("BLANKET-Returning function");}
  4450. return function(){
  4451. if (_blanket.options("debug")) {console.log("BLANKET-Marking file as loaded: "+url);}
  4452. _blanket.utils.cache[url].loaded=true;
  4453. if (_blanket.utils.allLoaded()){
  4454. if (_blanket.options("debug")) {console.log("BLANKET-All files loaded");}
  4455. cb();
  4456. }else if (orderedCb){
  4457. //if it's ordered we need to
  4458. //traverse down to the next
  4459. //priority level
  4460. if (_blanket.options("debug")) {console.log("BLANKET-Load next file.");}
  4461. orderedCb(nextScript,cb);
  4462. }
  4463. };
  4464. },
  4465. cache: {},
  4466. allLoaded: function (){
  4467. /**
  4468. * check if depdencies are loaded in cache
  4469. */
  4470. var cached = Object.keys(_blanket.utils.cache);
  4471. for (var i=0;i<cached.length;i++){
  4472. if (!_blanket.utils.cache[cached[i]].loaded){
  4473. return false;
  4474. }
  4475. }
  4476. return true;
  4477. },
  4478. processFile: function (content,url,cb,oldCb) {
  4479. var match = _blanket.options("filter");
  4480. //we check the never matches first
  4481. var antimatch = _blanket.options("antifilter");
  4482. if (typeof antimatch !== "undefined" &&
  4483. _blanket.utils.matchPatternAttribute(url,antimatch)
  4484. ){
  4485. oldCb(content);
  4486. if (_blanket.options("debug")) {console.log("BLANKET-File will never be instrumented:"+url);}
  4487. _blanket.requiringFile(url,true);
  4488. }else if (_blanket.utils.matchPatternAttribute(url,match)){
  4489. if (_blanket.options("debug")) {console.log("BLANKET-Attempting instrument of:"+url);}
  4490. _blanket.instrument({
  4491. inputFile: content,
  4492. inputFileName: url
  4493. },function(instrumented){
  4494. try{
  4495. if (_blanket.options("debug")) {console.log("BLANKET-instrument of:"+url+" was successfull.");}
  4496. _blanket.utils.blanketEval(instrumented);
  4497. cb();
  4498. _blanket.requiringFile(url,true);
  4499. }
  4500. catch(err){
  4501. if (_blanket.options("ignoreScriptError")){
  4502. //we can continue like normal if
  4503. //we're ignoring script errors,
  4504. //but otherwise we don't want
  4505. //to completeLoad or the error might be
  4506. //missed.
  4507. if (_blanket.options("debug")) { console.log("BLANKET-There was an error loading the file:"+url); }
  4508. cb(content);
  4509. _blanket.requiringFile(url,true);
  4510. }else{
  4511. throw new Error("Error parsing instrumented code: "+err);
  4512. }
  4513. }
  4514. });
  4515. }else{
  4516. if (_blanket.options("debug")) { console.log("BLANKET-Loading (without instrumenting) the file:"+url);}
  4517. oldCb(content);
  4518. _blanket.requiringFile(url,true);
  4519. }
  4520. },
  4521. cacheXhrConstructor: function(){
  4522. var Constructor, createXhr, i, progId;
  4523. if (typeof XMLHttpRequest !== "undefined") {
  4524. Constructor = XMLHttpRequest;
  4525. this.createXhr = function() { return new Constructor(); };
  4526. } else if (typeof ActiveXObject !== "undefined") {
  4527. Constructor = ActiveXObject;
  4528. for (i = 0; i < 3; i += 1) {
  4529. progId = progIds[i];
  4530. try {
  4531. new ActiveXObject(progId);
  4532. break;
  4533. } catch (e) {}
  4534. }
  4535. this.createXhr = function() { return new Constructor(progId); };
  4536. }
  4537. },
  4538. craeteXhr: function () {
  4539. throw new Error("cacheXhrConstructor is supposed to overwrite this function.");
  4540. },
  4541. getFile: function(url, callback, errback, onXhr){
  4542. var foundInSession = false;
  4543. if (_blanket.blanketSession){
  4544. var files = Object.keys(_blanket.blanketSession);
  4545. for (var i=0; i<files.length;i++ ){
  4546. var key = files[i];
  4547. if (url.indexOf(key) > -1){
  4548. callback(_blanket.blanketSession[key]);
  4549. foundInSession=true;
  4550. return;
  4551. }
  4552. }
  4553. }
  4554. if (!foundInSession){
  4555. var xhr = _blanket.utils.createXhr();
  4556. xhr.open('GET', url, true);
  4557. //Allow overrides specified in config
  4558. if (onXhr) {
  4559. onXhr(xhr, url);
  4560. }
  4561. xhr.onreadystatechange = function (evt) {
  4562. var status, err;
  4563. //Do not explicitly handle errors, those should be
  4564. //visible via console output in the browser.
  4565. if (xhr.readyState === 4) {
  4566. status = xhr.status;
  4567. if ((status > 399 && status < 600) /*||
  4568. (status === 0 &&
  4569. navigator.userAgent.toLowerCase().indexOf('firefox') > -1)
  4570. */ ) {
  4571. //An http 4xx or 5xx error. Signal an error.
  4572. err = new Error(url + ' HTTP status: ' + status);
  4573. err.xhr = xhr;
  4574. errback(err);
  4575. } else {
  4576. callback(xhr.responseText);
  4577. }
  4578. }
  4579. };
  4580. try{
  4581. xhr.send(null);
  4582. }catch(e){
  4583. if (e.code && (e.code === 101 || e.code === 1012) && _blanket.options("ignoreCors") === false){
  4584. //running locally and getting error from browser
  4585. _blanket.showManualLoader();
  4586. } else {
  4587. throw e;
  4588. }
  4589. }
  4590. }
  4591. }
  4592. }
  4593. });
  4594. (function(){
  4595. var require = blanket.options("commonJS") ? blanket._commonjs.require : window.require;
  4596. var requirejs = blanket.options("commonJS") ? blanket._commonjs.requirejs : window.requirejs;
  4597. if (!_blanket.options("engineOnly") && _blanket.options("existingRequireJS")){
  4598. _blanket.utils.oldloader = requirejs.load;
  4599. requirejs.load = function (context, moduleName, url) {
  4600. _blanket.requiringFile(url);
  4601. _blanket.utils.getFile(url,
  4602. function(content){
  4603. _blanket.utils.processFile(
  4604. content,
  4605. url,
  4606. function newLoader(){
  4607. context.completeLoad(moduleName);
  4608. },
  4609. function oldLoader(){
  4610. _blanket.utils.oldloader(context, moduleName, url);
  4611. }
  4612. );
  4613. }, function (err) {
  4614. _blanket.requiringFile();
  4615. throw err;
  4616. });
  4617. };
  4618. }
  4619. // Save the XHR constructor, just in case frameworks like Sinon would sandbox it.
  4620. _blanket.utils.cacheXhrConstructor();
  4621. })();
  4622. })(blanket);
  4623. (function(){
  4624. if (typeof QUnit !== 'undefined'){
  4625. //check to make sure requirejs is completed before we start the test runner
  4626. var allLoaded = function() {
  4627. return window.QUnit.config.queue.length > 0 && blanket.noConflict().requireFilesLoaded();
  4628. };
  4629. if (!QUnit.config.urlConfig[0].tooltip){
  4630. //older versions we run coverage automatically
  4631. //and we change how events are binded
  4632. QUnit.begin=function(){
  4633. blanket.noConflict().setupCoverage();
  4634. };
  4635. QUnit.done=function(failures, total) {
  4636. blanket.noConflict().onTestsDone();
  4637. };
  4638. QUnit.moduleStart=function( details ) {
  4639. blanket.noConflict().onModuleStart();
  4640. };
  4641. QUnit.testStart=function( details ) {
  4642. blanket.noConflict().onTestStart();
  4643. };
  4644. QUnit.testDone=function( details ) {
  4645. blanket.noConflict().onTestDone(details.total,details.passed);
  4646. };
  4647. blanket.beforeStartTestRunner({
  4648. condition: allLoaded,
  4649. callback: QUnit.start
  4650. });
  4651. }else{
  4652. QUnit.config.urlConfig.push({
  4653. id: "coverage",
  4654. label: "Enable coverage",
  4655. tooltip: "Enable code coverage."
  4656. });
  4657. if ( QUnit.urlParams.coverage || blanket.options("autoStart") ) {
  4658. QUnit.begin(function(){
  4659. blanket.noConflict().setupCoverage();
  4660. });
  4661. QUnit.done(function(failures, total) {
  4662. blanket.noConflict().onTestsDone();
  4663. });
  4664. QUnit.moduleStart(function( details ) {
  4665. blanket.noConflict().onModuleStart();
  4666. });
  4667. QUnit.testStart(function( details ) {
  4668. blanket.noConflict().onTestStart();
  4669. });
  4670. QUnit.testDone(function( details ) {
  4671. blanket.noConflict().onTestDone(details.total,details.passed);
  4672. });
  4673. blanket.noConflict().beforeStartTestRunner({
  4674. condition: allLoaded,
  4675. callback: function(){
  4676. if (!(blanket.options("existingRequireJS") && !blanket.options("autoStart"))){
  4677. QUnit.start();
  4678. }
  4679. }
  4680. });
  4681. }else{
  4682. if (blanket.options("existingRequireJS")){ requirejs.load = _blanket.utils.oldloader; }
  4683. blanket.noConflict().beforeStartTestRunner({
  4684. condition: allLoaded,
  4685. callback: function(){
  4686. if (!(blanket.options("existingRequireJS") && !blanket.options("autoStart"))){
  4687. QUnit.start();
  4688. }
  4689. },
  4690. coverage:false
  4691. });
  4692. }
  4693. }
  4694. }
  4695. })();