PageRenderTime 80ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 1ms

/dist/qunit/blanket.js

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