2020-10-30 20:34:04 +00:00
// Copyright 2014-2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
2014-07-26 22:44:12 +00:00
/*! blanket - v1.1.5 */
2018-04-27 21:25:04 +00:00
( function ( define ) {
/ *
2014-07-26 22:44:12 +00:00
Copyright ( C ) 2013 Ariya Hidayat < ariya . hidayat @ gmail . com >
Copyright ( C ) 2013 Thaddee Tyl < thaddee . tyl @ gmail . com >
Copyright ( C ) 2013 Mathias Bynens < mathias @ qiwi . be >
Copyright ( C ) 2012 Ariya Hidayat < ariya . hidayat @ gmail . com >
Copyright ( C ) 2012 Mathias Bynens < mathias @ qiwi . be >
Copyright ( C ) 2012 Joost - Wim Boekesteijn < joost - wim @ boekesteijn . nl >
Copyright ( C ) 2012 Kris Kowal < kris . kowal @ cixar . com >
Copyright ( C ) 2012 Yusuke Suzuki < utatane . tea @ gmail . com >
Copyright ( C ) 2012 Arpad Borsos < arpad . borsos @ googlemail . com >
Copyright ( C ) 2011 Ariya Hidayat < ariya . hidayat @ gmail . com >
Redistribution and use in source and binary forms , with or without
modification , are permitted provided that the following conditions are met :
* Redistributions of source code must retain the above copyright
notice , this list of conditions and the following disclaimer .
* Redistributions in binary form must reproduce the above copyright
notice , this list of conditions and the following disclaimer in the
documentation and / or other materials provided with the distribution .
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED . IN NO EVENT SHALL < COPYRIGHT HOLDER > BE LIABLE FOR ANY
DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES
( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ;
LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
* /
2018-04-27 21:25:04 +00:00
/*jslint bitwise:true plusplus:true */
/ * g l o b a l e s p r i m a : t r u e , d e f i n e : t r u e , e x p o r t s : t r u e , w i n d o w : t r u e ,
2014-07-26 22:44:12 +00:00
throwErrorTolerant : true ,
throwError : true , generateStatement : true , peek : true ,
parseAssignmentExpression : true , parseBlock : true , parseExpression : true ,
parseFunctionDeclaration : true , parseFunctionExpression : true ,
parseFunctionSourceElements : true , parseVariableIdentifier : true ,
parseLeftHandSideExpression : true ,
parseUnaryExpression : true ,
parseStatement : true , parseSourceElement : true * /
2018-04-27 21:25:04 +00:00
( function ( root , factory ) {
2014-07-26 22:44:12 +00:00
'use strict' ;
// Universal Module Definition (UMD) to support AMD, CommonJS/Node.js,
// Rhino, and plain browser loading.
/* istanbul ignore next */
if ( typeof define === 'function' && define . amd ) {
2018-04-27 21:25:04 +00:00
define ( [ 'exports' ] , factory ) ;
2014-07-26 22:44:12 +00:00
} else if ( typeof exports !== 'undefined' ) {
2018-04-27 21:25:04 +00:00
factory ( exports ) ;
2014-07-26 22:44:12 +00:00
} else {
2018-04-27 21:25:04 +00:00
factory ( ( root . esprima = { } ) ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
} ) ( this , function ( exports ) {
2014-07-26 22:44:12 +00:00
'use strict' ;
var Token ,
2018-04-27 21:25:04 +00:00
TokenName ,
FnExprTokens ,
Syntax ,
PropertyKind ,
Messages ,
Regex ,
SyntaxTreeDelegate ,
source ,
strict ,
index ,
lineNumber ,
lineStart ,
length ,
delegate ,
lookahead ,
state ,
extra ;
2014-07-26 22:44:12 +00:00
Token = {
2018-04-27 21:25:04 +00:00
BooleanLiteral : 1 ,
EOF : 2 ,
Identifier : 3 ,
Keyword : 4 ,
NullLiteral : 5 ,
NumericLiteral : 6 ,
Punctuator : 7 ,
StringLiteral : 8 ,
RegularExpression : 9 ,
2014-07-26 22:44:12 +00:00
} ;
TokenName = { } ;
TokenName [ Token . BooleanLiteral ] = 'Boolean' ;
TokenName [ Token . EOF ] = '<end>' ;
TokenName [ Token . Identifier ] = 'Identifier' ;
TokenName [ Token . Keyword ] = 'Keyword' ;
TokenName [ Token . NullLiteral ] = 'Null' ;
TokenName [ Token . NumericLiteral ] = 'Numeric' ;
TokenName [ Token . Punctuator ] = 'Punctuator' ;
TokenName [ Token . StringLiteral ] = 'String' ;
TokenName [ Token . RegularExpression ] = 'RegularExpression' ;
// A function following one of those tokens is an expression.
2018-04-27 21:25:04 +00:00
FnExprTokens = [
'(' ,
'{' ,
'[' ,
'in' ,
'typeof' ,
'instanceof' ,
'new' ,
'return' ,
'case' ,
'delete' ,
'throw' ,
'void' ,
// assignment operators
'=' ,
'+=' ,
'-=' ,
'*=' ,
'/=' ,
'%=' ,
'<<=' ,
'>>=' ,
'>>>=' ,
'&=' ,
'|=' ,
'^=' ,
',' ,
// binary/unary operators
'+' ,
'-' ,
'*' ,
'/' ,
'%' ,
'++' ,
'--' ,
'<<' ,
'>>' ,
'>>>' ,
'&' ,
'|' ,
'^' ,
'!' ,
'~' ,
'&&' ,
'||' ,
'?' ,
':' ,
'===' ,
'==' ,
'>=' ,
'<=' ,
'<' ,
'>' ,
'!=' ,
'!==' ,
] ;
2014-07-26 22:44:12 +00:00
Syntax = {
2018-04-27 21:25:04 +00:00
AssignmentExpression : 'AssignmentExpression' ,
ArrayExpression : 'ArrayExpression' ,
BlockStatement : 'BlockStatement' ,
BinaryExpression : 'BinaryExpression' ,
BreakStatement : 'BreakStatement' ,
CallExpression : 'CallExpression' ,
CatchClause : 'CatchClause' ,
ConditionalExpression : 'ConditionalExpression' ,
ContinueStatement : 'ContinueStatement' ,
DoWhileStatement : 'DoWhileStatement' ,
DebuggerStatement : 'DebuggerStatement' ,
EmptyStatement : 'EmptyStatement' ,
ExpressionStatement : 'ExpressionStatement' ,
ForStatement : 'ForStatement' ,
ForInStatement : 'ForInStatement' ,
FunctionDeclaration : 'FunctionDeclaration' ,
FunctionExpression : 'FunctionExpression' ,
Identifier : 'Identifier' ,
IfStatement : 'IfStatement' ,
Literal : 'Literal' ,
LabeledStatement : 'LabeledStatement' ,
LogicalExpression : 'LogicalExpression' ,
MemberExpression : 'MemberExpression' ,
NewExpression : 'NewExpression' ,
ObjectExpression : 'ObjectExpression' ,
Program : 'Program' ,
Property : 'Property' ,
ReturnStatement : 'ReturnStatement' ,
SequenceExpression : 'SequenceExpression' ,
SwitchStatement : 'SwitchStatement' ,
SwitchCase : 'SwitchCase' ,
ThisExpression : 'ThisExpression' ,
ThrowStatement : 'ThrowStatement' ,
TryStatement : 'TryStatement' ,
UnaryExpression : 'UnaryExpression' ,
UpdateExpression : 'UpdateExpression' ,
VariableDeclaration : 'VariableDeclaration' ,
VariableDeclarator : 'VariableDeclarator' ,
WhileStatement : 'WhileStatement' ,
WithStatement : 'WithStatement' ,
2014-07-26 22:44:12 +00:00
} ;
PropertyKind = {
2018-04-27 21:25:04 +00:00
Data : 1 ,
Get : 2 ,
Set : 4 ,
2014-07-26 22:44:12 +00:00
} ;
// Error messages should be identical to V8.
Messages = {
2018-04-27 21:25:04 +00:00
UnexpectedToken : 'Unexpected token %0' ,
UnexpectedNumber : 'Unexpected number' ,
UnexpectedString : 'Unexpected string' ,
UnexpectedIdentifier : 'Unexpected identifier' ,
UnexpectedReserved : 'Unexpected reserved word' ,
UnexpectedEOS : 'Unexpected end of input' ,
NewlineAfterThrow : 'Illegal newline after throw' ,
InvalidRegExp : 'Invalid regular expression' ,
UnterminatedRegExp : 'Invalid regular expression: missing /' ,
InvalidLHSInAssignment : 'Invalid left-hand side in assignment' ,
InvalidLHSInForIn : 'Invalid left-hand side in for-in' ,
MultipleDefaultsInSwitch :
'More than one default clause in switch statement' ,
NoCatchOrFinally : 'Missing catch or finally after try' ,
UnknownLabel : "Undefined label '%0'" ,
Redeclaration : "%0 '%1' has already been declared" ,
IllegalContinue : 'Illegal continue statement' ,
IllegalBreak : 'Illegal break statement' ,
IllegalReturn : 'Illegal return statement' ,
StrictModeWith : 'Strict mode code may not include a with statement' ,
StrictCatchVariable :
'Catch variable may not be eval or arguments in strict mode' ,
StrictVarName :
'Variable name may not be eval or arguments in strict mode' ,
StrictParamName :
'Parameter name eval or arguments is not allowed in strict mode' ,
StrictParamDupe :
'Strict mode function may not have duplicate parameter names' ,
StrictFunctionName :
'Function name may not be eval or arguments in strict mode' ,
StrictOctalLiteral : 'Octal literals are not allowed in strict mode.' ,
StrictDelete : 'Delete of an unqualified identifier in strict mode.' ,
StrictDuplicateProperty :
'Duplicate data property in object literal not allowed in strict mode' ,
AccessorDataProperty :
'Object literal may not have data and accessor property with the same name' ,
AccessorGetSet :
'Object literal may not have multiple get/set accessors with the same name' ,
StrictLHSAssignment :
'Assignment to eval or arguments is not allowed in strict mode' ,
StrictLHSPostfix :
'Postfix increment/decrement may not have eval or arguments operand in strict mode' ,
StrictLHSPrefix :
'Prefix increment/decrement may not have eval or arguments operand in strict mode' ,
StrictReservedWord : 'Use of future reserved word in strict mode' ,
2014-07-26 22:44:12 +00:00
} ;
// See also tools/generate-unicode-regex.py.
Regex = {
2018-04-27 21:25:04 +00:00
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
) ,
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 - \ u4D
) ,
2014-07-26 22:44:12 +00:00
} ;
// Ensure the condition is true, otherwise throw an error.
// This is only to have a better contract semantic, i.e. another safety net
// to catch a logic error. The condition shall be fulfilled in normal case.
// Do NOT use this to enforce a certain condition on any user input.
function assert ( condition , message ) {
2018-04-27 21:25:04 +00:00
/* istanbul ignore if */
if ( ! condition ) {
throw new Error ( 'ASSERT: ' + message ) ;
}
2014-07-26 22:44:12 +00:00
}
function isDecimalDigit ( ch ) {
2018-04-27 21:25:04 +00:00
return ch >= 48 && ch <= 57 ; // 0..9
2014-07-26 22:44:12 +00:00
}
function isHexDigit ( ch ) {
2018-04-27 21:25:04 +00:00
return '0123456789abcdefABCDEF' . indexOf ( ch ) >= 0 ;
2014-07-26 22:44:12 +00:00
}
function isOctalDigit ( ch ) {
2018-04-27 21:25:04 +00:00
return '01234567' . indexOf ( ch ) >= 0 ;
2014-07-26 22:44:12 +00:00
}
// 7.2 White Space
function isWhiteSpace ( ch ) {
2018-04-27 21:25:04 +00:00
return (
ch === 0x20 ||
ch === 0x09 ||
ch === 0x0b ||
ch === 0x0c ||
ch === 0xa0 ||
( ch >= 0x1680 &&
[
0x1680 ,
0x180e ,
0x2000 ,
0x2001 ,
0x2002 ,
0x2003 ,
0x2004 ,
0x2005 ,
0x2006 ,
0x2007 ,
0x2008 ,
0x2009 ,
0x200a ,
0x202f ,
0x205f ,
0x3000 ,
0xfeff ,
] . indexOf ( ch ) >= 0 )
) ;
2014-07-26 22:44:12 +00:00
}
// 7.3 Line Terminators
function isLineTerminator ( ch ) {
2018-04-27 21:25:04 +00:00
return ch === 0x0a || ch === 0x0d || ch === 0x2028 || ch === 0x2029 ;
2014-07-26 22:44:12 +00:00
}
// 7.6 Identifier Names and Identifiers
function isIdentifierStart ( ch ) {
2018-04-27 21:25:04 +00:00
return (
ch === 0x24 ||
ch === 0x5f || // $ (dollar) and _ (underscore)
( ch >= 0x41 && ch <= 0x5a ) || // A..Z
( ch >= 0x61 && ch <= 0x7a ) || // a..z
ch === 0x5c || // \ (backslash)
( ch >= 0x80 &&
Regex . NonAsciiIdentifierStart . test ( String . fromCharCode ( ch ) ) )
) ;
2014-07-26 22:44:12 +00:00
}
function isIdentifierPart ( ch ) {
2018-04-27 21:25:04 +00:00
return (
ch === 0x24 ||
ch === 0x5f || // $ (dollar) and _ (underscore)
( ch >= 0x41 && ch <= 0x5a ) || // A..Z
( ch >= 0x61 && ch <= 0x7a ) || // a..z
( ch >= 0x30 && ch <= 0x39 ) || // 0..9
ch === 0x5c || // \ (backslash)
( ch >= 0x80 &&
Regex . NonAsciiIdentifierPart . test ( String . fromCharCode ( ch ) ) )
) ;
2014-07-26 22:44:12 +00:00
}
// 7.6.1.2 Future Reserved Words
function isFutureReservedWord ( id ) {
2018-04-27 21:25:04 +00:00
switch ( id ) {
2014-07-26 22:44:12 +00:00
case 'class' :
case 'enum' :
case 'export' :
case 'extends' :
case 'import' :
case 'super' :
2018-04-27 21:25:04 +00:00
return true ;
2014-07-26 22:44:12 +00:00
default :
2018-04-27 21:25:04 +00:00
return false ;
}
2014-07-26 22:44:12 +00:00
}
function isStrictModeReservedWord ( id ) {
2018-04-27 21:25:04 +00:00
switch ( id ) {
2014-07-26 22:44:12 +00:00
case 'implements' :
case 'interface' :
case 'package' :
case 'private' :
case 'protected' :
case 'public' :
case 'static' :
case 'yield' :
case 'let' :
2018-04-27 21:25:04 +00:00
return true ;
2014-07-26 22:44:12 +00:00
default :
2018-04-27 21:25:04 +00:00
return false ;
}
2014-07-26 22:44:12 +00:00
}
function isRestrictedWord ( id ) {
2018-04-27 21:25:04 +00:00
return id === 'eval' || id === 'arguments' ;
2014-07-26 22:44:12 +00:00
}
// 7.6.1.1 Keywords
function isKeyword ( id ) {
2018-04-27 21:25:04 +00:00
if ( strict && isStrictModeReservedWord ( id ) ) {
return true ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
// 'const' is specialized as Keyword in V8.
// 'yield' and 'let' are for compatiblity with SpiderMonkey and ES.next.
// Some others are from future reserved words.
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
switch ( id . length ) {
2014-07-26 22:44:12 +00:00
case 2 :
2018-04-27 21:25:04 +00:00
return id === 'if' || id === 'in' || id === 'do' ;
2014-07-26 22:44:12 +00:00
case 3 :
2018-04-27 21:25:04 +00:00
return (
id === 'var' ||
id === 'for' ||
id === 'new' ||
id === 'try' ||
id === 'let'
) ;
2014-07-26 22:44:12 +00:00
case 4 :
2018-04-27 21:25:04 +00:00
return (
id === 'this' ||
id === 'else' ||
id === 'case' ||
id === 'void' ||
id === 'with' ||
id === 'enum'
) ;
2014-07-26 22:44:12 +00:00
case 5 :
2018-04-27 21:25:04 +00:00
return (
id === 'while' ||
id === 'break' ||
id === 'catch' ||
id === 'throw' ||
id === 'const' ||
id === 'yield' ||
id === 'class' ||
id === 'super'
) ;
2014-07-26 22:44:12 +00:00
case 6 :
2018-04-27 21:25:04 +00:00
return (
id === 'return' ||
id === 'typeof' ||
id === 'delete' ||
id === 'switch' ||
id === 'export' ||
id === 'import'
) ;
2014-07-26 22:44:12 +00:00
case 7 :
2018-04-27 21:25:04 +00:00
return id === 'default' || id === 'finally' || id === 'extends' ;
2014-07-26 22:44:12 +00:00
case 8 :
2018-04-27 21:25:04 +00:00
return id === 'function' || id === 'continue' || id === 'debugger' ;
2014-07-26 22:44:12 +00:00
case 10 :
2018-04-27 21:25:04 +00:00
return id === 'instanceof' ;
2014-07-26 22:44:12 +00:00
default :
2018-04-27 21:25:04 +00:00
return false ;
}
2014-07-26 22:44:12 +00:00
}
// 7.4 Comments
function addComment ( type , value , start , end , loc ) {
2018-04-27 21:25:04 +00:00
var comment , attacher ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
assert ( typeof start === 'number' , 'Comment must have valid position' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
// Because the way the actual token is scanned, often the comments
// (if any) are skipped twice during the lexical analysis.
// Thus, we need to skip adding a comment if the comment array already
// handled it.
if ( state . lastCommentStart >= start ) {
return ;
}
state . lastCommentStart = start ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
comment = {
type : type ,
value : value ,
} ;
if ( extra . range ) {
comment . range = [ start , end ] ;
}
if ( extra . loc ) {
comment . loc = loc ;
}
extra . comments . push ( comment ) ;
if ( extra . attachComment ) {
extra . leadingComments . push ( comment ) ;
extra . trailingComments . push ( comment ) ;
}
2014-07-26 22:44:12 +00:00
}
function skipSingleLineComment ( offset ) {
2018-04-27 21:25:04 +00:00
var start , loc , ch , comment ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
start = index - offset ;
loc = {
start : {
line : lineNumber ,
column : index - lineStart - offset ,
} ,
} ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
while ( index < length ) {
ch = source . charCodeAt ( index ) ;
++ index ;
if ( isLineTerminator ( ch ) ) {
if ( extra . comments ) {
comment = source . slice ( start + offset , index - 1 ) ;
2014-07-26 22:44:12 +00:00
loc . end = {
2018-04-27 21:25:04 +00:00
line : lineNumber ,
column : index - lineStart - 1 ,
2014-07-26 22:44:12 +00:00
} ;
2018-04-27 21:25:04 +00:00
addComment ( 'Line' , comment , start , index - 1 , loc ) ;
}
if ( ch === 13 && source . charCodeAt ( index ) === 10 ) {
++ index ;
}
++ lineNumber ;
lineStart = index ;
return ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
if ( extra . comments ) {
comment = source . slice ( start + offset , index ) ;
loc . end = {
line : lineNumber ,
column : index - lineStart ,
} ;
addComment ( 'Line' , comment , start , index , loc ) ;
}
2014-07-26 22:44:12 +00:00
}
function skipMultiLineComment ( ) {
2018-04-27 21:25:04 +00:00
var start , loc , ch , comment ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( extra . comments ) {
start = index - 2 ;
loc = {
start : {
line : lineNumber ,
column : index - lineStart - 2 ,
} ,
} ;
}
while ( index < length ) {
ch = source . charCodeAt ( index ) ;
if ( isLineTerminator ( ch ) ) {
if ( ch === 0x0d && source . charCodeAt ( index + 1 ) === 0x0a ) {
++ index ;
}
++ lineNumber ;
++ index ;
lineStart = index ;
if ( index >= length ) {
throwError ( { } , Messages . UnexpectedToken , 'ILLEGAL' ) ;
}
} else if ( ch === 0x2a ) {
// Block comment ends with '*/'.
if ( source . charCodeAt ( index + 1 ) === 0x2f ) {
++ index ;
++ index ;
if ( extra . comments ) {
comment = source . slice ( start + 2 , index - 2 ) ;
loc . end = {
line : lineNumber ,
column : index - lineStart ,
} ;
addComment ( 'Block' , comment , start , index , loc ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
return ;
}
++ index ;
} else {
++ index ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
throwError ( { } , Messages . UnexpectedToken , 'ILLEGAL' ) ;
2014-07-26 22:44:12 +00:00
}
function skipComment ( ) {
2018-04-27 21:25:04 +00:00
var ch , start ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
start = index === 0 ;
while ( index < length ) {
ch = source . charCodeAt ( index ) ;
if ( isWhiteSpace ( ch ) ) {
++ index ;
} else if ( isLineTerminator ( ch ) ) {
++ index ;
if ( ch === 0x0d && source . charCodeAt ( index ) === 0x0a ) {
++ index ;
}
++ lineNumber ;
lineStart = index ;
start = true ;
} else if ( ch === 0x2f ) {
// U+002F is '/'
ch = source . charCodeAt ( index + 1 ) ;
if ( ch === 0x2f ) {
++ index ;
++ index ;
skipSingleLineComment ( 2 ) ;
start = true ;
} else if ( ch === 0x2a ) {
// U+002A is '*'
++ index ;
++ index ;
skipMultiLineComment ( ) ;
} else {
break ;
}
} else if ( start && ch === 0x2d ) {
// U+002D is '-'
// U+003E is '>'
if (
source . charCodeAt ( index + 1 ) === 0x2d &&
source . charCodeAt ( index + 2 ) === 0x3e
) {
// '-->' is a single-line comment
index += 3 ;
skipSingleLineComment ( 3 ) ;
} else {
break ;
}
} else if ( ch === 0x3c ) {
// U+003C is '<'
if ( source . slice ( index + 1 , index + 4 ) === '!--' ) {
++ index ; // `<`
++ index ; // `!`
++ index ; // `-`
++ index ; // `-`
skipSingleLineComment ( 4 ) ;
} else {
break ;
}
} else {
break ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
2014-07-26 22:44:12 +00:00
}
function scanHexEscape ( prefix ) {
2018-04-27 21:25:04 +00:00
var i ,
len ,
ch ,
code = 0 ;
len = prefix === 'u' ? 4 : 2 ;
for ( i = 0 ; i < len ; ++ i ) {
if ( index < length && isHexDigit ( source [ index ] ) ) {
ch = source [ index ++ ] ;
code = code * 16 + '0123456789abcdef' . indexOf ( ch . toLowerCase ( ) ) ;
} else {
return '' ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
return String . fromCharCode ( code ) ;
2014-07-26 22:44:12 +00:00
}
function getEscapedIdentifier ( ) {
2018-04-27 21:25:04 +00:00
var ch , id ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
ch = source . charCodeAt ( index ++ ) ;
id = String . fromCharCode ( ch ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
// '\u' (U+005C, U+0075) denotes an escaped character.
if ( ch === 0x5c ) {
if ( source . charCodeAt ( index ) !== 0x75 ) {
throwError ( { } , Messages . UnexpectedToken , 'ILLEGAL' ) ;
}
++ index ;
ch = scanHexEscape ( 'u' ) ;
if ( ! ch || ch === '\\' || ! isIdentifierStart ( ch . charCodeAt ( 0 ) ) ) {
throwError ( { } , Messages . UnexpectedToken , 'ILLEGAL' ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
id = ch ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
while ( index < length ) {
ch = source . charCodeAt ( index ) ;
if ( ! isIdentifierPart ( ch ) ) {
break ;
}
++ index ;
id += String . fromCharCode ( ch ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
// '\u' (U+005C, U+0075) denotes an escaped character.
if ( ch === 0x5c ) {
id = id . substr ( 0 , id . length - 1 ) ;
if ( source . charCodeAt ( index ) !== 0x75 ) {
throwError ( { } , Messages . UnexpectedToken , 'ILLEGAL' ) ;
}
++ index ;
ch = scanHexEscape ( 'u' ) ;
if ( ! ch || ch === '\\' || ! isIdentifierPart ( ch . charCodeAt ( 0 ) ) ) {
throwError ( { } , Messages . UnexpectedToken , 'ILLEGAL' ) ;
}
id += ch ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return id ;
2014-07-26 22:44:12 +00:00
}
function getIdentifier ( ) {
2018-04-27 21:25:04 +00:00
var start , ch ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
start = index ++ ;
while ( index < length ) {
ch = source . charCodeAt ( index ) ;
if ( ch === 0x5c ) {
// Blackslash (U+005C) marks Unicode escape sequence.
index = start ;
return getEscapedIdentifier ( ) ;
}
if ( isIdentifierPart ( ch ) ) {
++ index ;
} else {
break ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return source . slice ( start , index ) ;
2014-07-26 22:44:12 +00:00
}
function scanIdentifier ( ) {
2018-04-27 21:25:04 +00:00
var start , id , type ;
start = index ;
// Backslash (U+005C) starts an escaped character.
id =
source . charCodeAt ( index ) === 0x5c
? getEscapedIdentifier ( )
: getIdentifier ( ) ;
// There is no keyword or literal with only one character.
// Thus, it must be an identifier.
if ( id . length === 1 ) {
type = Token . Identifier ;
} else if ( isKeyword ( id ) ) {
type = Token . Keyword ;
} else if ( id === 'null' ) {
type = Token . NullLiteral ;
} else if ( id === 'true' || id === 'false' ) {
type = Token . BooleanLiteral ;
} else {
type = Token . Identifier ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return {
type : type ,
value : id ,
lineNumber : lineNumber ,
lineStart : lineStart ,
start : start ,
end : index ,
} ;
2014-07-26 22:44:12 +00:00
}
// 7.7 Punctuators
function scanPunctuator ( ) {
2018-04-27 21:25:04 +00:00
var start = index ,
code = source . charCodeAt ( index ) ,
code2 ,
ch1 = source [ index ] ,
ch2 ,
ch3 ,
ch4 ;
switch ( code ) {
2014-07-26 22:44:12 +00:00
// Check for most common single-character punctuators.
2018-04-27 21:25:04 +00:00
case 0x2e : // . dot
case 0x28 : // ( open bracket
case 0x29 : // ) close bracket
case 0x3b : // ; semicolon
case 0x2c : // , comma
case 0x7b : // { open curly brace
case 0x7d : // } close curly brace
case 0x5b : // [
case 0x5d : // ]
case 0x3a : // :
case 0x3f : // ?
case 0x7e : // ~
++ index ;
if ( extra . tokenize ) {
if ( code === 0x28 ) {
extra . openParenToken = extra . tokens . length ;
} else if ( code === 0x7b ) {
extra . openCurlyToken = extra . tokens . length ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
return {
type : Token . Punctuator ,
value : String . fromCharCode ( code ) ,
lineNumber : lineNumber ,
lineStart : lineStart ,
start : start ,
end : index ,
} ;
2014-07-26 22:44:12 +00:00
default :
2018-04-27 21:25:04 +00:00
code2 = source . charCodeAt ( index + 1 ) ;
// '=' (U+003D) marks an assignment or comparison operator.
if ( code2 === 0x3d ) {
switch ( code ) {
case 0x2b : // +
case 0x2d : // -
case 0x2f : // /
case 0x3c : // <
case 0x3e : // >
case 0x5e : // ^
case 0x7c : // |
case 0x25 : // %
case 0x26 : // &
case 0x2a : // *
index += 2 ;
return {
type : Token . Punctuator ,
value : String . fromCharCode ( code ) + String . fromCharCode ( code2 ) ,
lineNumber : lineNumber ,
lineStart : lineStart ,
start : start ,
end : index ,
} ;
case 0x21 : // !
case 0x3d : // =
index += 2 ;
// !== and ===
if ( source . charCodeAt ( index ) === 0x3d ) {
++ index ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
return {
type : Token . Punctuator ,
value : source . slice ( start , index ) ,
lineNumber : lineNumber ,
lineStart : lineStart ,
start : start ,
end : index ,
} ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
// 4-character punctuator: >>>=
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
ch4 = source . substr ( index , 4 ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( ch4 === '>>>=' ) {
index += 4 ;
return {
type : Token . Punctuator ,
value : ch4 ,
lineNumber : lineNumber ,
lineStart : lineStart ,
start : start ,
end : index ,
} ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
// 3-character punctuators: === !== >>> <<= >>=
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
ch3 = ch4 . substr ( 0 , 3 ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( ch3 === '>>>' || ch3 === '<<=' || ch3 === '>>=' ) {
index += 3 ;
return {
type : Token . Punctuator ,
value : ch3 ,
lineNumber : lineNumber ,
lineStart : lineStart ,
start : start ,
end : index ,
} ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
// Other 2-character punctuators: ++ -- << >> && ||
ch2 = ch3 . substr ( 0 , 2 ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( ( ch1 === ch2 [ 1 ] && '+-<>&|' . indexOf ( ch1 ) >= 0 ) || ch2 === '=>' ) {
index += 2 ;
return {
type : Token . Punctuator ,
value : ch2 ,
lineNumber : lineNumber ,
lineStart : lineStart ,
start : start ,
end : index ,
} ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
// 1-character punctuators: < > = ! + - * % & | ^ /
if ( '<>=!+-*%&|^/' . indexOf ( ch1 ) >= 0 ) {
++ index ;
return {
type : Token . Punctuator ,
value : ch1 ,
lineNumber : lineNumber ,
lineStart : lineStart ,
start : start ,
end : index ,
} ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
throwError ( { } , Messages . UnexpectedToken , 'ILLEGAL' ) ;
2014-07-26 22:44:12 +00:00
}
// 7.8.3 Numeric Literals
function scanHexLiteral ( start ) {
2018-04-27 21:25:04 +00:00
var number = '' ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
while ( index < length ) {
if ( ! isHexDigit ( source [ index ] ) ) {
break ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
number += source [ index ++ ] ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( number . length === 0 ) {
throwError ( { } , Messages . UnexpectedToken , 'ILLEGAL' ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( isIdentifierStart ( source . charCodeAt ( index ) ) ) {
throwError ( { } , Messages . UnexpectedToken , 'ILLEGAL' ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return {
type : Token . NumericLiteral ,
value : parseInt ( '0x' + number , 16 ) ,
lineNumber : lineNumber ,
lineStart : lineStart ,
start : start ,
end : index ,
} ;
2014-07-26 22:44:12 +00:00
}
function scanOctalLiteral ( start ) {
2018-04-27 21:25:04 +00:00
var number = '0' + source [ index ++ ] ;
while ( index < length ) {
if ( ! isOctalDigit ( source [ index ] ) ) {
break ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
number += source [ index ++ ] ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if (
isIdentifierStart ( source . charCodeAt ( index ) ) ||
isDecimalDigit ( source . charCodeAt ( index ) )
) {
throwError ( { } , Messages . UnexpectedToken , 'ILLEGAL' ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return {
type : Token . NumericLiteral ,
value : parseInt ( number , 8 ) ,
octal : true ,
lineNumber : lineNumber ,
lineStart : lineStart ,
start : start ,
end : index ,
} ;
2014-07-26 22:44:12 +00:00
}
function scanNumericLiteral ( ) {
2018-04-27 21:25:04 +00:00
var number , start , ch ;
ch = source [ index ] ;
assert (
isDecimalDigit ( ch . charCodeAt ( 0 ) ) || ch === '.' ,
'Numeric literal must start with a decimal digit or a decimal point'
) ;
start = index ;
number = '' ;
if ( ch !== '.' ) {
number = source [ index ++ ] ;
2014-07-26 22:44:12 +00:00
ch = source [ index ] ;
2018-04-27 21:25:04 +00:00
// Hex number starts with '0x'.
// Octal number starts with '0'.
if ( number === '0' ) {
if ( ch === 'x' || ch === 'X' ) {
++ index ;
return scanHexLiteral ( start ) ;
}
if ( isOctalDigit ( ch ) ) {
return scanOctalLiteral ( start ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
// decimal number starts with '0' such as '09' is illegal.
if ( ch && isDecimalDigit ( ch . charCodeAt ( 0 ) ) ) {
throwError ( { } , Messages . UnexpectedToken , 'ILLEGAL' ) ;
}
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
while ( isDecimalDigit ( source . charCodeAt ( index ) ) ) {
number += source [ index ++ ] ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
ch = source [ index ] ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( ch === '.' ) {
number += source [ index ++ ] ;
while ( isDecimalDigit ( source . charCodeAt ( index ) ) ) {
number += source [ index ++ ] ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
ch = source [ index ] ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( ch === 'e' || ch === 'E' ) {
number += source [ index ++ ] ;
ch = source [ index ] ;
if ( ch === '+' || ch === '-' ) {
number += source [ index ++ ] ;
}
if ( isDecimalDigit ( source . charCodeAt ( index ) ) ) {
while ( isDecimalDigit ( source . charCodeAt ( index ) ) ) {
number += source [ index ++ ] ;
}
} else {
throwError ( { } , Messages . UnexpectedToken , 'ILLEGAL' ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( isIdentifierStart ( source . charCodeAt ( index ) ) ) {
throwError ( { } , Messages . UnexpectedToken , 'ILLEGAL' ) ;
}
return {
type : Token . NumericLiteral ,
value : parseFloat ( number ) ,
lineNumber : lineNumber ,
lineStart : lineStart ,
start : start ,
end : index ,
} ;
2014-07-26 22:44:12 +00:00
}
// 7.8.4 String Literals
function scanStringLiteral ( ) {
2018-04-27 21:25:04 +00:00
var str = '' ,
quote ,
start ,
ch ,
code ,
unescaped ,
restore ,
octal = false ,
startLineNumber ,
startLineStart ;
startLineNumber = lineNumber ;
startLineStart = lineStart ;
quote = source [ index ] ;
assert (
quote === "'" || quote === '"' ,
'String literal must starts with a quote'
) ;
start = index ;
++ index ;
while ( index < length ) {
ch = source [ index ++ ] ;
if ( ch === quote ) {
quote = '' ;
break ;
} else if ( ch === '\\' ) {
ch = source [ index ++ ] ;
if ( ! ch || ! isLineTerminator ( ch . charCodeAt ( 0 ) ) ) {
switch ( ch ) {
case 'u' :
case 'x' :
restore = index ;
unescaped = scanHexEscape ( ch ) ;
if ( unescaped ) {
str += unescaped ;
} else {
index = restore ;
str += ch ;
}
break ;
case 'n' :
str += '\n' ;
break ;
case 'r' :
str += '\r' ;
break ;
case 't' :
str += '\t' ;
break ;
case 'b' :
str += '\b' ;
break ;
case 'f' :
str += '\f' ;
break ;
case 'v' :
str += '\x0B' ;
break ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
default :
if ( isOctalDigit ( ch ) ) {
code = '01234567' . indexOf ( ch ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
// \0 is not octal escape sequence
if ( code !== 0 ) {
octal = true ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( index < length && isOctalDigit ( source [ index ] ) ) {
octal = true ;
code = code * 8 + '01234567' . indexOf ( source [ index ++ ] ) ;
// 3 digits are only allowed when string starts
// with 0, 1, 2, 3
if (
'0123' . indexOf ( ch ) >= 0 &&
index < length &&
isOctalDigit ( source [ index ] )
) {
code = code * 8 + '01234567' . indexOf ( source [ index ++ ] ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
str += String . fromCharCode ( code ) ;
2014-07-26 22:44:12 +00:00
} else {
2018-04-27 21:25:04 +00:00
str += ch ;
2014-07-26 22:44:12 +00:00
}
break ;
}
2018-04-27 21:25:04 +00:00
} else {
++ lineNumber ;
if ( ch === '\r' && source [ index ] === '\n' ) {
++ index ;
}
lineStart = index ;
}
} else if ( isLineTerminator ( ch . charCodeAt ( 0 ) ) ) {
break ;
} else {
str += ch ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( quote !== '' ) {
throwError ( { } , Messages . UnexpectedToken , 'ILLEGAL' ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return {
type : Token . StringLiteral ,
value : str ,
octal : octal ,
startLineNumber : startLineNumber ,
startLineStart : startLineStart ,
lineNumber : lineNumber ,
lineStart : lineStart ,
start : start ,
end : index ,
} ;
2014-07-26 22:44:12 +00:00
}
function testRegExp ( pattern , flags ) {
2018-04-27 21:25:04 +00:00
var value ;
try {
value = new RegExp ( pattern , flags ) ;
} catch ( e ) {
throwError ( { } , Messages . InvalidRegExp ) ;
}
return value ;
2014-07-26 22:44:12 +00:00
}
function scanRegExpBody ( ) {
2018-04-27 21:25:04 +00:00
var ch , str , classMarker , terminated , body ;
ch = source [ index ] ;
assert ( ch === '/' , 'Regular expression literal must start with a slash' ) ;
str = source [ index ++ ] ;
classMarker = false ;
terminated = false ;
while ( index < length ) {
ch = source [ index ++ ] ;
str += ch ;
if ( ch === '\\' ) {
ch = source [ index ++ ] ;
// ECMA-262 7.8.5
if ( isLineTerminator ( ch . charCodeAt ( 0 ) ) ) {
2014-07-26 22:44:12 +00:00
throwError ( { } , Messages . UnterminatedRegExp ) ;
2018-04-27 21:25:04 +00:00
}
str += ch ;
} else if ( isLineTerminator ( ch . charCodeAt ( 0 ) ) ) {
throwError ( { } , Messages . UnterminatedRegExp ) ;
} else if ( classMarker ) {
if ( ch === ']' ) {
classMarker = false ;
}
} else {
if ( ch === '/' ) {
terminated = true ;
break ;
} else if ( ch === '[' ) {
classMarker = true ;
}
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( ! terminated ) {
throwError ( { } , Messages . UnterminatedRegExp ) ;
}
// Exclude leading and trailing slash.
body = str . substr ( 1 , str . length - 2 ) ;
return {
value : body ,
literal : str ,
} ;
2014-07-26 22:44:12 +00:00
}
function scanRegExpFlags ( ) {
2018-04-27 21:25:04 +00:00
var ch , str , flags , restore ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
str = '' ;
flags = '' ;
while ( index < length ) {
ch = source [ index ] ;
if ( ! isIdentifierPart ( ch . charCodeAt ( 0 ) ) ) {
break ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
++ index ;
if ( ch === '\\' && index < length ) {
ch = source [ index ] ;
if ( ch === 'u' ) {
2014-07-26 22:44:12 +00:00
++ index ;
2018-04-27 21:25:04 +00:00
restore = index ;
ch = scanHexEscape ( 'u' ) ;
if ( ch ) {
flags += ch ;
for ( str += '\\u' ; restore < index ; ++ restore ) {
str += source [ restore ] ;
}
2014-07-26 22:44:12 +00:00
} else {
2018-04-27 21:25:04 +00:00
index = restore ;
flags += 'u' ;
str += '\\u' ;
}
throwErrorTolerant ( { } , Messages . UnexpectedToken , 'ILLEGAL' ) ;
} else {
str += '\\' ;
throwErrorTolerant ( { } , Messages . UnexpectedToken , 'ILLEGAL' ) ;
}
} else {
flags += ch ;
str += ch ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return {
value : flags ,
literal : str ,
} ;
2014-07-26 22:44:12 +00:00
}
function scanRegExp ( ) {
2018-04-27 21:25:04 +00:00
var start , body , flags , pattern , value ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
lookahead = null ;
skipComment ( ) ;
start = index ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
body = scanRegExpBody ( ) ;
flags = scanRegExpFlags ( ) ;
value = testRegExp ( body . value , flags . value ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( extra . tokenize ) {
2014-07-26 22:44:12 +00:00
return {
2018-04-27 21:25:04 +00:00
type : Token . RegularExpression ,
value : value ,
lineNumber : lineNumber ,
lineStart : lineStart ,
start : start ,
end : index ,
2014-07-26 22:44:12 +00:00
} ;
2018-04-27 21:25:04 +00:00
}
return {
literal : body . literal + flags . literal ,
value : value ,
start : start ,
end : index ,
} ;
2014-07-26 22:44:12 +00:00
}
function collectRegex ( ) {
2018-04-27 21:25:04 +00:00
var pos , loc , regex , token ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
skipComment ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
pos = index ;
loc = {
start : {
line : lineNumber ,
column : index - lineStart ,
} ,
} ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
regex = scanRegExp ( ) ;
loc . end = {
line : lineNumber ,
column : index - lineStart ,
} ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
/* istanbul ignore next */
if ( ! extra . tokenize ) {
// Pop the previous token, which is likely '/' or '/='
if ( extra . tokens . length > 0 ) {
token = extra . tokens [ extra . tokens . length - 1 ] ;
if ( token . range [ 0 ] === pos && token . type === 'Punctuator' ) {
if ( token . value === '/' || token . value === '/=' ) {
extra . tokens . pop ( ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
extra . tokens . push ( {
type : 'RegularExpression' ,
value : regex . literal ,
range : [ pos , index ] ,
loc : loc ,
} ) ;
}
return regex ;
2014-07-26 22:44:12 +00:00
}
function isIdentifierName ( token ) {
2018-04-27 21:25:04 +00:00
return (
token . type === Token . Identifier ||
token . type === Token . Keyword ||
token . type === Token . BooleanLiteral ||
token . type === Token . NullLiteral
) ;
2014-07-26 22:44:12 +00:00
}
function advanceSlash ( ) {
2018-04-27 21:25:04 +00:00
var prevToken , checkToken ;
// Using the following algorithm:
// https://github.com/mozilla/sweet.js/wiki/design
prevToken = extra . tokens [ extra . tokens . length - 1 ] ;
if ( ! prevToken ) {
// Nothing before that: it cannot be a division.
return collectRegex ( ) ;
}
if ( prevToken . type === 'Punctuator' ) {
if ( prevToken . value === ']' ) {
return scanPunctuator ( ) ;
}
if ( prevToken . value === ')' ) {
checkToken = extra . tokens [ extra . openParenToken - 1 ] ;
if (
checkToken &&
checkToken . type === 'Keyword' &&
( checkToken . value === 'if' ||
checkToken . value === 'while' ||
checkToken . value === 'for' ||
checkToken . value === 'with' )
) {
2014-07-26 22:44:12 +00:00
return collectRegex ( ) ;
2018-04-27 21:25:04 +00:00
}
return scanPunctuator ( ) ;
}
if ( prevToken . value === '}' ) {
// Dividing a function by anything makes little sense,
// but we have to check for that.
if (
extra . tokens [ extra . openCurlyToken - 3 ] &&
extra . tokens [ extra . openCurlyToken - 3 ] . type === 'Keyword'
) {
// Anonymous function.
checkToken = extra . tokens [ extra . openCurlyToken - 4 ] ;
if ( ! checkToken ) {
return scanPunctuator ( ) ;
}
} else if (
extra . tokens [ extra . openCurlyToken - 4 ] &&
extra . tokens [ extra . openCurlyToken - 4 ] . type === 'Keyword'
) {
// Named function.
checkToken = extra . tokens [ extra . openCurlyToken - 5 ] ;
if ( ! checkToken ) {
return collectRegex ( ) ;
}
} else {
return scanPunctuator ( ) ;
}
// checkToken determines whether the function is
// a declaration or an expression.
if ( FnExprTokens . indexOf ( checkToken . value ) >= 0 ) {
// It is an expression.
return scanPunctuator ( ) ;
}
// It is a declaration.
return collectRegex ( ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
return collectRegex ( ) ;
}
if ( prevToken . type === 'Keyword' ) {
return collectRegex ( ) ;
}
return scanPunctuator ( ) ;
2014-07-26 22:44:12 +00:00
}
function advance ( ) {
2018-04-27 21:25:04 +00:00
var ch ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
skipComment ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( index >= length ) {
return {
type : Token . EOF ,
lineNumber : lineNumber ,
lineStart : lineStart ,
start : index ,
end : index ,
} ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
ch = source . charCodeAt ( index ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( isIdentifierStart ( ch ) ) {
return scanIdentifier ( ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
// Very common: ( and ) and ;
if ( ch === 0x28 || ch === 0x29 || ch === 0x3b ) {
return scanPunctuator ( ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
// String literal starts with single quote (U+0027) or double quote (U+0022).
if ( ch === 0x27 || ch === 0x22 ) {
return scanStringLiteral ( ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
// Dot (.) U+002E can also start a floating-point number, hence the need
// to check the next character.
if ( ch === 0x2e ) {
if ( isDecimalDigit ( source . charCodeAt ( index + 1 ) ) ) {
return scanNumericLiteral ( ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
return scanPunctuator ( ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( isDecimalDigit ( ch ) ) {
return scanNumericLiteral ( ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
// Slash (/) U+002F can also start a regex.
if ( extra . tokenize && ch === 0x2f ) {
return advanceSlash ( ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return scanPunctuator ( ) ;
2014-07-26 22:44:12 +00:00
}
function collectToken ( ) {
2018-04-27 21:25:04 +00:00
var loc , token , range , value ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
skipComment ( ) ;
loc = {
start : {
line : lineNumber ,
column : index - lineStart ,
} ,
} ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
token = advance ( ) ;
loc . end = {
line : lineNumber ,
column : index - lineStart ,
} ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( token . type !== Token . EOF ) {
value = source . slice ( token . start , token . end ) ;
extra . tokens . push ( {
type : TokenName [ token . type ] ,
value : value ,
range : [ token . start , token . end ] ,
loc : loc ,
} ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return token ;
2014-07-26 22:44:12 +00:00
}
function lex ( ) {
2018-04-27 21:25:04 +00:00
var token ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
token = lookahead ;
index = token . end ;
lineNumber = token . lineNumber ;
lineStart = token . lineStart ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
lookahead =
typeof extra . tokens !== 'undefined' ? collectToken ( ) : advance ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
index = token . end ;
lineNumber = token . lineNumber ;
lineStart = token . lineStart ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return token ;
2014-07-26 22:44:12 +00:00
}
function peek ( ) {
2018-04-27 21:25:04 +00:00
var pos , line , start ;
pos = index ;
line = lineNumber ;
start = lineStart ;
lookahead =
typeof extra . tokens !== 'undefined' ? collectToken ( ) : advance ( ) ;
index = pos ;
lineNumber = line ;
lineStart = start ;
2014-07-26 22:44:12 +00:00
}
function Position ( line , column ) {
2018-04-27 21:25:04 +00:00
this . line = line ;
this . column = column ;
2014-07-26 22:44:12 +00:00
}
function SourceLocation ( startLine , startColumn , line , column ) {
2018-04-27 21:25:04 +00:00
this . start = new Position ( startLine , startColumn ) ;
this . end = new Position ( line , column ) ;
2014-07-26 22:44:12 +00:00
}
SyntaxTreeDelegate = {
2018-04-27 21:25:04 +00:00
name : 'SyntaxTree' ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
processComment : function ( node ) {
var lastChild , trailingComments ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( node . type === Syntax . Program ) {
if ( node . body . length > 0 ) {
return ;
}
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( extra . trailingComments . length > 0 ) {
if ( extra . trailingComments [ 0 ] . range [ 0 ] >= node . range [ 1 ] ) {
trailingComments = extra . trailingComments ;
extra . trailingComments = [ ] ;
} else {
extra . trailingComments . length = 0 ;
}
} else {
if (
extra . bottomRightStack . length > 0 &&
extra . bottomRightStack [ extra . bottomRightStack . length - 1 ]
. trailingComments &&
extra . bottomRightStack [ extra . bottomRightStack . length - 1 ]
. trailingComments [ 0 ] . range [ 0 ] >= node . range [ 1 ]
) {
trailingComments =
extra . bottomRightStack [ extra . bottomRightStack . length - 1 ]
. trailingComments ;
delete extra . bottomRightStack [ extra . bottomRightStack . length - 1 ]
. trailingComments ;
}
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
// Eating the stack.
while (
extra . bottomRightStack . length > 0 &&
extra . bottomRightStack [ extra . bottomRightStack . length - 1 ] . range [ 0 ] >=
node . range [ 0 ]
) {
lastChild = extra . bottomRightStack . pop ( ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( lastChild ) {
if (
lastChild . leadingComments &&
lastChild . leadingComments [ lastChild . leadingComments . length - 1 ]
. range [ 1 ] <= node . range [ 0 ]
) {
node . leadingComments = lastChild . leadingComments ;
delete lastChild . leadingComments ;
}
} else if (
extra . leadingComments . length > 0 &&
extra . leadingComments [ extra . leadingComments . length - 1 ] . range [ 1 ] <=
node . range [ 0 ]
) {
node . leadingComments = extra . leadingComments ;
extra . leadingComments = [ ] ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( trailingComments ) {
node . trailingComments = trailingComments ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
extra . bottomRightStack . push ( node ) ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
markEnd : function ( node , startToken ) {
if ( extra . range ) {
node . range = [ startToken . start , index ] ;
}
if ( extra . loc ) {
node . loc = new SourceLocation (
startToken . startLineNumber === undefined
? startToken . lineNumber
: startToken . startLineNumber ,
startToken . start -
( startToken . startLineStart === undefined
? startToken . lineStart
: startToken . startLineStart ) ,
lineNumber ,
index - lineStart
) ;
this . postProcess ( node ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( extra . attachComment ) {
this . processComment ( node ) ;
}
return node ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
postProcess : function ( node ) {
if ( extra . source ) {
node . loc . source = extra . source ;
}
return node ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createArrayExpression : function ( elements ) {
return {
type : Syntax . ArrayExpression ,
elements : elements ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createAssignmentExpression : function ( operator , left , right ) {
return {
type : Syntax . AssignmentExpression ,
operator : operator ,
left : left ,
right : right ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createBinaryExpression : function ( operator , left , right ) {
var type =
operator === '||' || operator === '&&'
? Syntax . LogicalExpression
: Syntax . BinaryExpression ;
return {
type : type ,
operator : operator ,
left : left ,
right : right ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createBlockStatement : function ( body ) {
return {
type : Syntax . BlockStatement ,
body : body ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createBreakStatement : function ( label ) {
return {
type : Syntax . BreakStatement ,
label : label ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createCallExpression : function ( callee , args ) {
return {
type : Syntax . CallExpression ,
callee : callee ,
arguments : args ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createCatchClause : function ( param , body ) {
return {
type : Syntax . CatchClause ,
param : param ,
body : body ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createConditionalExpression : function ( test , consequent , alternate ) {
return {
type : Syntax . ConditionalExpression ,
test : test ,
consequent : consequent ,
alternate : alternate ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createContinueStatement : function ( label ) {
return {
type : Syntax . ContinueStatement ,
label : label ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createDebuggerStatement : function ( ) {
return {
type : Syntax . DebuggerStatement ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createDoWhileStatement : function ( body , test ) {
return {
type : Syntax . DoWhileStatement ,
body : body ,
test : test ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createEmptyStatement : function ( ) {
return {
type : Syntax . EmptyStatement ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createExpressionStatement : function ( expression ) {
return {
type : Syntax . ExpressionStatement ,
expression : expression ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createForStatement : function ( init , test , update , body ) {
return {
type : Syntax . ForStatement ,
init : init ,
test : test ,
update : update ,
body : body ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createForInStatement : function ( left , right , body ) {
return {
type : Syntax . ForInStatement ,
left : left ,
right : right ,
body : body ,
each : false ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createFunctionDeclaration : function ( id , params , defaults , body ) {
return {
type : Syntax . FunctionDeclaration ,
id : id ,
params : params ,
defaults : defaults ,
body : body ,
rest : null ,
generator : false ,
expression : false ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createFunctionExpression : function ( id , params , defaults , body ) {
return {
type : Syntax . FunctionExpression ,
id : id ,
params : params ,
defaults : defaults ,
body : body ,
rest : null ,
generator : false ,
expression : false ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createIdentifier : function ( name ) {
return {
type : Syntax . Identifier ,
name : name ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createIfStatement : function ( test , consequent , alternate ) {
return {
type : Syntax . IfStatement ,
test : test ,
consequent : consequent ,
alternate : alternate ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createLabeledStatement : function ( label , body ) {
return {
type : Syntax . LabeledStatement ,
label : label ,
body : body ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createLiteral : function ( token ) {
return {
type : Syntax . Literal ,
value : token . value ,
raw : source . slice ( token . start , token . end ) ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createMemberExpression : function ( accessor , object , property ) {
return {
type : Syntax . MemberExpression ,
computed : accessor === '[' ,
object : object ,
property : property ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createNewExpression : function ( callee , args ) {
return {
type : Syntax . NewExpression ,
callee : callee ,
arguments : args ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createObjectExpression : function ( properties ) {
return {
type : Syntax . ObjectExpression ,
properties : properties ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createPostfixExpression : function ( operator , argument ) {
return {
type : Syntax . UpdateExpression ,
operator : operator ,
argument : argument ,
prefix : false ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createProgram : function ( body ) {
return {
type : Syntax . Program ,
body : body ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createProperty : function ( kind , key , value ) {
return {
type : Syntax . Property ,
key : key ,
value : value ,
kind : kind ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createReturnStatement : function ( argument ) {
return {
type : Syntax . ReturnStatement ,
argument : argument ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createSequenceExpression : function ( expressions ) {
return {
type : Syntax . SequenceExpression ,
expressions : expressions ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createSwitchCase : function ( test , consequent ) {
return {
type : Syntax . SwitchCase ,
test : test ,
consequent : consequent ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createSwitchStatement : function ( discriminant , cases ) {
return {
type : Syntax . SwitchStatement ,
discriminant : discriminant ,
cases : cases ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createThisExpression : function ( ) {
return {
type : Syntax . ThisExpression ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createThrowStatement : function ( argument ) {
return {
type : Syntax . ThrowStatement ,
argument : argument ,
} ;
} ,
createTryStatement : function (
block ,
guardedHandlers ,
handlers ,
finalizer
) {
return {
type : Syntax . TryStatement ,
block : block ,
guardedHandlers : guardedHandlers ,
handlers : handlers ,
finalizer : finalizer ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createUnaryExpression : function ( operator , argument ) {
if ( operator === '++' || operator === '--' ) {
return {
type : Syntax . UpdateExpression ,
operator : operator ,
argument : argument ,
prefix : true ,
} ;
}
return {
type : Syntax . UnaryExpression ,
operator : operator ,
argument : argument ,
prefix : true ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createVariableDeclaration : function ( declarations , kind ) {
return {
type : Syntax . VariableDeclaration ,
declarations : declarations ,
kind : kind ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createVariableDeclarator : function ( id , init ) {
return {
type : Syntax . VariableDeclarator ,
id : id ,
init : init ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createWhileStatement : function ( test , body ) {
return {
type : Syntax . WhileStatement ,
test : test ,
body : body ,
} ;
} ,
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
createWithStatement : function ( object , body ) {
return {
type : Syntax . WithStatement ,
object : object ,
body : body ,
} ;
} ,
2014-07-26 22:44:12 +00:00
} ;
// Return true if there is a line terminator before the next token.
function peekLineTerminator ( ) {
2018-04-27 21:25:04 +00:00
var pos , line , start , found ;
pos = index ;
line = lineNumber ;
start = lineStart ;
skipComment ( ) ;
found = lineNumber !== line ;
index = pos ;
lineNumber = line ;
lineStart = start ;
return found ;
2014-07-26 22:44:12 +00:00
}
// Throw an exception
function throwError ( token , messageFormat ) {
2018-04-27 21:25:04 +00:00
var error ,
args = Array . prototype . slice . call ( arguments , 2 ) ,
msg = messageFormat . replace ( /%(\d)/g , function ( whole , index ) {
assert ( index < args . length , 'Message reference must be in range' ) ;
return args [ index ] ;
} ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( typeof token . lineNumber === 'number' ) {
error = new Error ( 'Line ' + token . lineNumber + ': ' + msg ) ;
error . index = token . start ;
error . lineNumber = token . lineNumber ;
error . column = token . start - lineStart + 1 ;
} else {
error = new Error ( 'Line ' + lineNumber + ': ' + msg ) ;
error . index = index ;
error . lineNumber = lineNumber ;
error . column = index - lineStart + 1 ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
error . description = msg ;
throw error ;
2014-07-26 22:44:12 +00:00
}
function throwErrorTolerant ( ) {
2018-04-27 21:25:04 +00:00
try {
throwError . apply ( null , arguments ) ;
} catch ( e ) {
if ( extra . errors ) {
extra . errors . push ( e ) ;
} else {
throw e ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
2014-07-26 22:44:12 +00:00
}
// Throw an exception because of the token.
function throwUnexpected ( token ) {
2018-04-27 21:25:04 +00:00
if ( token . type === Token . EOF ) {
throwError ( token , Messages . UnexpectedEOS ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( token . type === Token . NumericLiteral ) {
throwError ( token , Messages . UnexpectedNumber ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( token . type === Token . StringLiteral ) {
throwError ( token , Messages . UnexpectedString ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( token . type === Token . Identifier ) {
throwError ( token , Messages . UnexpectedIdentifier ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( token . type === Token . Keyword ) {
if ( isFutureReservedWord ( token . value ) ) {
throwError ( token , Messages . UnexpectedReserved ) ;
} else if ( strict && isStrictModeReservedWord ( token . value ) ) {
throwErrorTolerant ( token , Messages . StrictReservedWord ) ;
return ;
2014-07-26 22:44:12 +00:00
}
throwError ( token , Messages . UnexpectedToken , token . value ) ;
2018-04-27 21:25:04 +00:00
}
// BooleanLiteral, NullLiteral, or Punctuator.
throwError ( token , Messages . UnexpectedToken , token . value ) ;
2014-07-26 22:44:12 +00:00
}
// Expect the next token to match the specified punctuator.
// If not, an exception will be thrown.
function expect ( value ) {
2018-04-27 21:25:04 +00:00
var token = lex ( ) ;
if ( token . type !== Token . Punctuator || token . value !== value ) {
throwUnexpected ( token ) ;
}
2014-07-26 22:44:12 +00:00
}
// Expect the next token to match the specified keyword.
// If not, an exception will be thrown.
function expectKeyword ( keyword ) {
2018-04-27 21:25:04 +00:00
var token = lex ( ) ;
if ( token . type !== Token . Keyword || token . value !== keyword ) {
throwUnexpected ( token ) ;
}
2014-07-26 22:44:12 +00:00
}
// Return true if the next token matches the specified punctuator.
function match ( value ) {
2018-04-27 21:25:04 +00:00
return lookahead . type === Token . Punctuator && lookahead . value === value ;
2014-07-26 22:44:12 +00:00
}
// Return true if the next token matches the specified keyword
function matchKeyword ( keyword ) {
2018-04-27 21:25:04 +00:00
return lookahead . type === Token . Keyword && lookahead . value === keyword ;
2014-07-26 22:44:12 +00:00
}
// Return true if the next token is an assignment operator
function matchAssign ( ) {
2018-04-27 21:25:04 +00:00
var op ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( lookahead . type !== Token . Punctuator ) {
return false ;
}
op = lookahead . value ;
return (
op === '=' ||
op === '*=' ||
op === '/=' ||
op === '%=' ||
op === '+=' ||
op === '-=' ||
op === '<<=' ||
op === '>>=' ||
op === '>>>=' ||
op === '&=' ||
op === '^=' ||
op === '|='
) ;
2014-07-26 22:44:12 +00:00
}
function consumeSemicolon ( ) {
2018-04-27 21:25:04 +00:00
var line ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
// Catch the very common case first: immediately a semicolon (U+003B).
if ( source . charCodeAt ( index ) === 0x3b || match ( ';' ) ) {
lex ( ) ;
return ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
line = lineNumber ;
skipComment ( ) ;
if ( lineNumber !== line ) {
return ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( lookahead . type !== Token . EOF && ! match ( '}' ) ) {
throwUnexpected ( lookahead ) ;
}
2014-07-26 22:44:12 +00:00
}
// Return true if provided expression is LeftHandSideExpression
function isLeftHandSide ( expr ) {
2018-04-27 21:25:04 +00:00
return (
expr . type === Syntax . Identifier || expr . type === Syntax . MemberExpression
) ;
2014-07-26 22:44:12 +00:00
}
// 11.1.4 Array Initialiser
function parseArrayInitialiser ( ) {
2018-04-27 21:25:04 +00:00
var elements = [ ] ,
startToken ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
startToken = lookahead ;
expect ( '[' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
while ( ! match ( ']' ) ) {
if ( match ( ',' ) ) {
lex ( ) ;
elements . push ( null ) ;
} else {
elements . push ( parseAssignmentExpression ( ) ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( ! match ( ']' ) ) {
expect ( ',' ) ;
}
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
lex ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return delegate . markEnd (
delegate . createArrayExpression ( elements ) ,
startToken
) ;
2014-07-26 22:44:12 +00:00
}
// 11.1.5 Object Initialiser
function parsePropertyFunction ( param , first ) {
2018-04-27 21:25:04 +00:00
var previousStrict , body , startToken ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
previousStrict = strict ;
startToken = lookahead ;
body = parseFunctionSourceElements ( ) ;
if ( first && strict && isRestrictedWord ( param [ 0 ] . name ) ) {
throwErrorTolerant ( first , Messages . StrictParamName ) ;
}
strict = previousStrict ;
return delegate . markEnd (
delegate . createFunctionExpression ( null , param , [ ] , body ) ,
startToken
) ;
2014-07-26 22:44:12 +00:00
}
function parseObjectPropertyKey ( ) {
2018-04-27 21:25:04 +00:00
var token , startToken ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
startToken = lookahead ;
token = lex ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
// Note: This function is called only from parseObjectProperty(), where
// EOF and Punctuator tokens are already filtered out.
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if (
token . type === Token . StringLiteral ||
token . type === Token . NumericLiteral
) {
if ( strict && token . octal ) {
throwErrorTolerant ( token , Messages . StrictOctalLiteral ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
return delegate . markEnd ( delegate . createLiteral ( token ) , startToken ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return delegate . markEnd (
delegate . createIdentifier ( token . value ) ,
startToken
) ;
2014-07-26 22:44:12 +00:00
}
function parseObjectProperty ( ) {
2018-04-27 21:25:04 +00:00
var token , key , id , value , param , startToken ;
token = lookahead ;
startToken = lookahead ;
if ( token . type === Token . Identifier ) {
id = parseObjectPropertyKey ( ) ;
// Property Assignment: Getter and Setter.
if ( token . value === 'get' && ! match ( ':' ) ) {
key = parseObjectPropertyKey ( ) ;
expect ( '(' ) ;
expect ( ')' ) ;
value = parsePropertyFunction ( [ ] ) ;
return delegate . markEnd (
delegate . createProperty ( 'get' , key , value ) ,
startToken
) ;
}
if ( token . value === 'set' && ! match ( ':' ) ) {
key = parseObjectPropertyKey ( ) ;
expect ( '(' ) ;
token = lookahead ;
if ( token . type !== Token . Identifier ) {
expect ( ')' ) ;
throwErrorTolerant ( token , Messages . UnexpectedToken , token . value ) ;
value = parsePropertyFunction ( [ ] ) ;
} else {
param = [ parseVariableIdentifier ( ) ] ;
expect ( ')' ) ;
value = parsePropertyFunction ( param , token ) ;
}
return delegate . markEnd (
delegate . createProperty ( 'set' , key , value ) ,
startToken
) ;
}
expect ( ':' ) ;
value = parseAssignmentExpression ( ) ;
return delegate . markEnd (
delegate . createProperty ( 'init' , id , value ) ,
startToken
) ;
}
if ( token . type === Token . EOF || token . type === Token . Punctuator ) {
throwUnexpected ( token ) ;
} else {
key = parseObjectPropertyKey ( ) ;
expect ( ':' ) ;
value = parseAssignmentExpression ( ) ;
return delegate . markEnd (
delegate . createProperty ( 'init' , key , value ) ,
startToken
) ;
}
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
function parseObjectInitialiser ( ) {
var properties = [ ] ,
property ,
name ,
key ,
kind ,
map = { } ,
toString = String ,
startToken ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
startToken = lookahead ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expect ( '{' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
while ( ! match ( '}' ) ) {
property = parseObjectProperty ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( property . key . type === Syntax . Identifier ) {
name = property . key . name ;
} else {
name = toString ( property . key . value ) ;
}
kind =
property . kind === 'init'
? PropertyKind . Data
: property . kind === 'get'
? PropertyKind . Get
: PropertyKind . Set ;
key = '$' + name ;
if ( Object . prototype . hasOwnProperty . call ( map , key ) ) {
if ( map [ key ] === PropertyKind . Data ) {
if ( strict && kind === PropertyKind . Data ) {
throwErrorTolerant ( { } , Messages . StrictDuplicateProperty ) ;
} else if ( kind !== PropertyKind . Data ) {
throwErrorTolerant ( { } , Messages . AccessorDataProperty ) ;
}
} else {
if ( kind === PropertyKind . Data ) {
throwErrorTolerant ( { } , Messages . AccessorDataProperty ) ;
} else if ( map [ key ] & kind ) {
throwErrorTolerant ( { } , Messages . AccessorGetSet ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
map [ key ] |= kind ;
2014-07-26 22:44:12 +00:00
} else {
2018-04-27 21:25:04 +00:00
map [ key ] = kind ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
properties . push ( property ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( ! match ( '}' ) ) {
expect ( ',' ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expect ( '}' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return delegate . markEnd (
delegate . createObjectExpression ( properties ) ,
startToken
) ;
2014-07-26 22:44:12 +00:00
}
// 11.1.6 The Grouping Operator
function parseGroupExpression ( ) {
2018-04-27 21:25:04 +00:00
var expr ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expect ( '(' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expr = parseExpression ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expect ( ')' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return expr ;
2014-07-26 22:44:12 +00:00
}
// 11.1 Primary Expressions
function parsePrimaryExpression ( ) {
2018-04-27 21:25:04 +00:00
var type , token , expr , startToken ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( match ( '(' ) ) {
return parseGroupExpression ( ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( match ( '[' ) ) {
return parseArrayInitialiser ( ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( match ( '{' ) ) {
return parseObjectInitialiser ( ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
type = lookahead . type ;
startToken = lookahead ;
if ( type === Token . Identifier ) {
expr = delegate . createIdentifier ( lex ( ) . value ) ;
} else if (
type === Token . StringLiteral ||
type === Token . NumericLiteral
) {
if ( strict && lookahead . octal ) {
throwErrorTolerant ( lookahead , Messages . StrictOctalLiteral ) ;
}
expr = delegate . createLiteral ( lex ( ) ) ;
} else if ( type === Token . Keyword ) {
if ( matchKeyword ( 'function' ) ) {
return parseFunctionExpression ( ) ;
}
if ( matchKeyword ( 'this' ) ) {
lex ( ) ;
expr = delegate . createThisExpression ( ) ;
2014-07-26 22:44:12 +00:00
} else {
2018-04-27 21:25:04 +00:00
throwUnexpected ( lex ( ) ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
} else if ( type === Token . BooleanLiteral ) {
token = lex ( ) ;
token . value = token . value === 'true' ;
expr = delegate . createLiteral ( token ) ;
} else if ( type === Token . NullLiteral ) {
token = lex ( ) ;
token . value = null ;
expr = delegate . createLiteral ( token ) ;
} else if ( match ( '/' ) || match ( '/=' ) ) {
if ( typeof extra . tokens !== 'undefined' ) {
expr = delegate . createLiteral ( collectRegex ( ) ) ;
} else {
expr = delegate . createLiteral ( scanRegExp ( ) ) ;
}
peek ( ) ;
} else {
throwUnexpected ( lex ( ) ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return delegate . markEnd ( expr , startToken ) ;
2014-07-26 22:44:12 +00:00
}
// 11.2 Left-Hand-Side Expressions
function parseArguments ( ) {
2018-04-27 21:25:04 +00:00
var args = [ ] ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expect ( '(' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( ! match ( ')' ) ) {
while ( index < length ) {
args . push ( parseAssignmentExpression ( ) ) ;
if ( match ( ')' ) ) {
break ;
}
expect ( ',' ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expect ( ')' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return args ;
2014-07-26 22:44:12 +00:00
}
function parseNonComputedProperty ( ) {
2018-04-27 21:25:04 +00:00
var token , startToken ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
startToken = lookahead ;
token = lex ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( ! isIdentifierName ( token ) ) {
throwUnexpected ( token ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return delegate . markEnd (
delegate . createIdentifier ( token . value ) ,
startToken
) ;
2014-07-26 22:44:12 +00:00
}
function parseNonComputedMember ( ) {
2018-04-27 21:25:04 +00:00
expect ( '.' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return parseNonComputedProperty ( ) ;
2014-07-26 22:44:12 +00:00
}
function parseComputedMember ( ) {
2018-04-27 21:25:04 +00:00
var expr ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expect ( '[' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expr = parseExpression ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expect ( ']' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return expr ;
2014-07-26 22:44:12 +00:00
}
function parseNewExpression ( ) {
2018-04-27 21:25:04 +00:00
var callee , args , startToken ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
startToken = lookahead ;
expectKeyword ( 'new' ) ;
callee = parseLeftHandSideExpression ( ) ;
args = match ( '(' ) ? parseArguments ( ) : [ ] ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return delegate . markEnd (
delegate . createNewExpression ( callee , args ) ,
startToken
) ;
2014-07-26 22:44:12 +00:00
}
function parseLeftHandSideExpressionAllowCall ( ) {
2018-04-27 21:25:04 +00:00
var previousAllowIn , expr , args , property , startToken ;
startToken = lookahead ;
previousAllowIn = state . allowIn ;
state . allowIn = true ;
expr = matchKeyword ( 'new' )
? parseNewExpression ( )
: parsePrimaryExpression ( ) ;
state . allowIn = previousAllowIn ;
for ( ; ; ) {
if ( match ( '.' ) ) {
property = parseNonComputedMember ( ) ;
expr = delegate . createMemberExpression ( '.' , expr , property ) ;
} else if ( match ( '(' ) ) {
args = parseArguments ( ) ;
expr = delegate . createCallExpression ( expr , args ) ;
} else if ( match ( '[' ) ) {
property = parseComputedMember ( ) ;
expr = delegate . createMemberExpression ( '[' , expr , property ) ;
} else {
break ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
delegate . markEnd ( expr , startToken ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return expr ;
2014-07-26 22:44:12 +00:00
}
function parseLeftHandSideExpression ( ) {
2018-04-27 21:25:04 +00:00
var previousAllowIn , expr , property , startToken ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
startToken = lookahead ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
previousAllowIn = state . allowIn ;
expr = matchKeyword ( 'new' )
? parseNewExpression ( )
: parsePrimaryExpression ( ) ;
state . allowIn = previousAllowIn ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
while ( match ( '.' ) || match ( '[' ) ) {
if ( match ( '[' ) ) {
property = parseComputedMember ( ) ;
expr = delegate . createMemberExpression ( '[' , expr , property ) ;
} else {
property = parseNonComputedMember ( ) ;
expr = delegate . createMemberExpression ( '.' , expr , property ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
delegate . markEnd ( expr , startToken ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return expr ;
2014-07-26 22:44:12 +00:00
}
// 11.3 Postfix Expressions
function parsePostfixExpression ( ) {
2018-04-27 21:25:04 +00:00
var expr ,
token ,
startToken = lookahead ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expr = parseLeftHandSideExpressionAllowCall ( ) ;
if ( lookahead . type === Token . Punctuator ) {
if ( ( match ( '++' ) || match ( '--' ) ) && ! peekLineTerminator ( ) ) {
// 11.3.1, 11.3.2
if (
strict &&
expr . type === Syntax . Identifier &&
isRestrictedWord ( expr . name )
) {
throwErrorTolerant ( { } , Messages . StrictLHSPostfix ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( ! isLeftHandSide ( expr ) ) {
throwErrorTolerant ( { } , Messages . InvalidLHSInAssignment ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
token = lex ( ) ;
expr = delegate . markEnd (
delegate . createPostfixExpression ( token . value , expr ) ,
startToken
) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return expr ;
2014-07-26 22:44:12 +00:00
}
// 11.4 Unary Operators
function parseUnaryExpression ( ) {
2018-04-27 21:25:04 +00:00
var token , expr , startToken ;
if (
lookahead . type !== Token . Punctuator &&
lookahead . type !== Token . Keyword
) {
expr = parsePostfixExpression ( ) ;
} else if ( match ( '++' ) || match ( '--' ) ) {
startToken = lookahead ;
token = lex ( ) ;
expr = parseUnaryExpression ( ) ;
// 11.4.4, 11.4.5
if (
strict &&
expr . type === Syntax . Identifier &&
isRestrictedWord ( expr . name )
) {
throwErrorTolerant ( { } , Messages . StrictLHSPrefix ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( ! isLeftHandSide ( expr ) ) {
throwErrorTolerant ( { } , Messages . InvalidLHSInAssignment ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
expr = delegate . createUnaryExpression ( token . value , expr ) ;
expr = delegate . markEnd ( expr , startToken ) ;
} else if ( match ( '+' ) || match ( '-' ) || match ( '~' ) || match ( '!' ) ) {
startToken = lookahead ;
token = lex ( ) ;
expr = parseUnaryExpression ( ) ;
expr = delegate . createUnaryExpression ( token . value , expr ) ;
expr = delegate . markEnd ( expr , startToken ) ;
} else if (
matchKeyword ( 'delete' ) ||
matchKeyword ( 'void' ) ||
matchKeyword ( 'typeof' )
) {
startToken = lookahead ;
token = lex ( ) ;
expr = parseUnaryExpression ( ) ;
expr = delegate . createUnaryExpression ( token . value , expr ) ;
expr = delegate . markEnd ( expr , startToken ) ;
if (
strict &&
expr . operator === 'delete' &&
expr . argument . type === Syntax . Identifier
) {
throwErrorTolerant ( { } , Messages . StrictDelete ) ;
}
} else {
expr = parsePostfixExpression ( ) ;
}
return expr ;
2014-07-26 22:44:12 +00:00
}
function binaryPrecedence ( token , allowIn ) {
2018-04-27 21:25:04 +00:00
var prec = 0 ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( token . type !== Token . Punctuator && token . type !== Token . Keyword ) {
return 0 ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
switch ( token . value ) {
2014-07-26 22:44:12 +00:00
case '||' :
2018-04-27 21:25:04 +00:00
prec = 1 ;
break ;
2014-07-26 22:44:12 +00:00
case '&&' :
2018-04-27 21:25:04 +00:00
prec = 2 ;
break ;
2014-07-26 22:44:12 +00:00
case '|' :
2018-04-27 21:25:04 +00:00
prec = 3 ;
break ;
2014-07-26 22:44:12 +00:00
case '^' :
2018-04-27 21:25:04 +00:00
prec = 4 ;
break ;
2014-07-26 22:44:12 +00:00
case '&' :
2018-04-27 21:25:04 +00:00
prec = 5 ;
break ;
2014-07-26 22:44:12 +00:00
case '==' :
case '!=' :
case '===' :
case '!==' :
2018-04-27 21:25:04 +00:00
prec = 6 ;
break ;
2014-07-26 22:44:12 +00:00
case '<' :
case '>' :
case '<=' :
case '>=' :
case 'instanceof' :
2018-04-27 21:25:04 +00:00
prec = 7 ;
break ;
2014-07-26 22:44:12 +00:00
case 'in' :
2018-04-27 21:25:04 +00:00
prec = allowIn ? 7 : 0 ;
break ;
2014-07-26 22:44:12 +00:00
case '<<' :
case '>>' :
case '>>>' :
2018-04-27 21:25:04 +00:00
prec = 8 ;
break ;
2014-07-26 22:44:12 +00:00
case '+' :
case '-' :
2018-04-27 21:25:04 +00:00
prec = 9 ;
break ;
2014-07-26 22:44:12 +00:00
case '*' :
case '/' :
case '%' :
2018-04-27 21:25:04 +00:00
prec = 11 ;
break ;
2014-07-26 22:44:12 +00:00
default :
2018-04-27 21:25:04 +00:00
break ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return prec ;
2014-07-26 22:44:12 +00:00
}
// 11.5 Multiplicative Operators
// 11.6 Additive Operators
// 11.7 Bitwise Shift Operators
// 11.8 Relational Operators
// 11.9 Equality Operators
// 11.10 Binary Bitwise Operators
// 11.11 Binary Logical Operators
function parseBinaryExpression ( ) {
2018-04-27 21:25:04 +00:00
var marker , markers , expr , token , prec , stack , right , operator , left , i ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
marker = lookahead ;
left = parseUnaryExpression ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
token = lookahead ;
prec = binaryPrecedence ( token , state . allowIn ) ;
if ( prec === 0 ) {
return left ;
}
token . prec = prec ;
lex ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
markers = [ marker , lookahead ] ;
right = parseUnaryExpression ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
stack = [ left , token , right ] ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
while ( ( prec = binaryPrecedence ( lookahead , state . allowIn ) ) > 0 ) {
// Reduce: make a binary expression from the three topmost entries.
while ( stack . length > 2 && prec <= stack [ stack . length - 2 ] . prec ) {
right = stack . pop ( ) ;
operator = stack . pop ( ) . value ;
left = stack . pop ( ) ;
expr = delegate . createBinaryExpression ( operator , left , right ) ;
markers . pop ( ) ;
marker = markers [ markers . length - 1 ] ;
delegate . markEnd ( expr , marker ) ;
stack . push ( expr ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
// Shift.
token = lex ( ) ;
token . prec = prec ;
stack . push ( token ) ;
markers . push ( lookahead ) ;
expr = parseUnaryExpression ( ) ;
stack . push ( expr ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
// Final reduce to clean-up the stack.
i = stack . length - 1 ;
expr = stack [ i ] ;
markers . pop ( ) ;
while ( i > 1 ) {
expr = delegate . createBinaryExpression (
stack [ i - 1 ] . value ,
stack [ i - 2 ] ,
expr
) ;
i -= 2 ;
marker = markers . pop ( ) ;
delegate . markEnd ( expr , marker ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return expr ;
}
2014-07-26 22:44:12 +00:00
// 11.12 Conditional Operator
function parseConditionalExpression ( ) {
2018-04-27 21:25:04 +00:00
var expr , previousAllowIn , consequent , alternate , startToken ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
startToken = lookahead ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expr = parseBinaryExpression ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( match ( '?' ) ) {
lex ( ) ;
previousAllowIn = state . allowIn ;
state . allowIn = true ;
consequent = parseAssignmentExpression ( ) ;
state . allowIn = previousAllowIn ;
expect ( ':' ) ;
alternate = parseAssignmentExpression ( ) ;
expr = delegate . createConditionalExpression (
expr ,
consequent ,
alternate
) ;
delegate . markEnd ( expr , startToken ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return expr ;
2014-07-26 22:44:12 +00:00
}
// 11.13 Assignment Operators
function parseAssignmentExpression ( ) {
2018-04-27 21:25:04 +00:00
var token , left , right , node , startToken ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
token = lookahead ;
startToken = lookahead ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
node = left = parseConditionalExpression ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( matchAssign ( ) ) {
// LeftHandSideExpression
if ( ! isLeftHandSide ( left ) ) {
throwErrorTolerant ( { } , Messages . InvalidLHSInAssignment ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
// 11.13.1
if (
strict &&
left . type === Syntax . Identifier &&
isRestrictedWord ( left . name )
) {
throwErrorTolerant ( token , Messages . StrictLHSAssignment ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
token = lex ( ) ;
right = parseAssignmentExpression ( ) ;
node = delegate . markEnd (
delegate . createAssignmentExpression ( token . value , left , right ) ,
startToken
) ;
}
return node ;
2014-07-26 22:44:12 +00:00
}
// 11.14 Comma Operator
function parseExpression ( ) {
2018-04-27 21:25:04 +00:00
var expr ,
startToken = lookahead ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expr = parseAssignmentExpression ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( match ( ',' ) ) {
expr = delegate . createSequenceExpression ( [ expr ] ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
while ( index < length ) {
if ( ! match ( ',' ) ) {
break ;
}
lex ( ) ;
expr . expressions . push ( parseAssignmentExpression ( ) ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
delegate . markEnd ( expr , startToken ) ;
}
return expr ;
2014-07-26 22:44:12 +00:00
}
// 12.1 Block
function parseStatementList ( ) {
2018-04-27 21:25:04 +00:00
var list = [ ] ,
statement ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
while ( index < length ) {
if ( match ( '}' ) ) {
break ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
statement = parseSourceElement ( ) ;
if ( typeof statement === 'undefined' ) {
break ;
}
list . push ( statement ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return list ;
2014-07-26 22:44:12 +00:00
}
function parseBlock ( ) {
2018-04-27 21:25:04 +00:00
var block , startToken ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
startToken = lookahead ;
expect ( '{' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
block = parseStatementList ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expect ( '}' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return delegate . markEnd ( delegate . createBlockStatement ( block ) , startToken ) ;
2014-07-26 22:44:12 +00:00
}
// 12.2 Variable Statement
function parseVariableIdentifier ( ) {
2018-04-27 21:25:04 +00:00
var token , startToken ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
startToken = lookahead ;
token = lex ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( token . type !== Token . Identifier ) {
throwUnexpected ( token ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return delegate . markEnd (
delegate . createIdentifier ( token . value ) ,
startToken
) ;
2014-07-26 22:44:12 +00:00
}
function parseVariableDeclaration ( kind ) {
2018-04-27 21:25:04 +00:00
var init = null ,
id ,
startToken ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
startToken = lookahead ;
id = parseVariableIdentifier ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
// 12.2.1
if ( strict && isRestrictedWord ( id . name ) ) {
throwErrorTolerant ( { } , Messages . StrictVarName ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( kind === 'const' ) {
expect ( '=' ) ;
init = parseAssignmentExpression ( ) ;
} else if ( match ( '=' ) ) {
lex ( ) ;
init = parseAssignmentExpression ( ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return delegate . markEnd (
delegate . createVariableDeclarator ( id , init ) ,
startToken
) ;
2014-07-26 22:44:12 +00:00
}
function parseVariableDeclarationList ( kind ) {
2018-04-27 21:25:04 +00:00
var list = [ ] ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
do {
list . push ( parseVariableDeclaration ( kind ) ) ;
if ( ! match ( ',' ) ) {
break ;
}
lex ( ) ;
} while ( index < length ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return list ;
2014-07-26 22:44:12 +00:00
}
function parseVariableStatement ( ) {
2018-04-27 21:25:04 +00:00
var declarations ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expectKeyword ( 'var' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
declarations = parseVariableDeclarationList ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
consumeSemicolon ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return delegate . createVariableDeclaration ( declarations , 'var' ) ;
2014-07-26 22:44:12 +00:00
}
// kind may be `const` or `let`
// Both are experimental and not in the specification yet.
// see http://wiki.ecmascript.org/doku.php?id=harmony:const
// and http://wiki.ecmascript.org/doku.php?id=harmony:let
function parseConstLetDeclaration ( kind ) {
2018-04-27 21:25:04 +00:00
var declarations , startToken ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
startToken = lookahead ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expectKeyword ( kind ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
declarations = parseVariableDeclarationList ( kind ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
consumeSemicolon ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return delegate . markEnd (
delegate . createVariableDeclaration ( declarations , kind ) ,
startToken
) ;
2014-07-26 22:44:12 +00:00
}
// 12.3 Empty Statement
function parseEmptyStatement ( ) {
2018-04-27 21:25:04 +00:00
expect ( ';' ) ;
return delegate . createEmptyStatement ( ) ;
2014-07-26 22:44:12 +00:00
}
// 12.4 Expression Statement
function parseExpressionStatement ( ) {
2018-04-27 21:25:04 +00:00
var expr = parseExpression ( ) ;
consumeSemicolon ( ) ;
return delegate . createExpressionStatement ( expr ) ;
2014-07-26 22:44:12 +00:00
}
// 12.5 If statement
function parseIfStatement ( ) {
2018-04-27 21:25:04 +00:00
var test , consequent , alternate ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expectKeyword ( 'if' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expect ( '(' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
test = parseExpression ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expect ( ')' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
consequent = parseStatement ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( matchKeyword ( 'else' ) ) {
lex ( ) ;
alternate = parseStatement ( ) ;
} else {
alternate = null ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return delegate . createIfStatement ( test , consequent , alternate ) ;
2014-07-26 22:44:12 +00:00
}
// 12.6 Iteration Statements
function parseDoWhileStatement ( ) {
2018-04-27 21:25:04 +00:00
var body , test , oldInIteration ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expectKeyword ( 'do' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
oldInIteration = state . inIteration ;
state . inIteration = true ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
body = parseStatement ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
state . inIteration = oldInIteration ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expectKeyword ( 'while' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expect ( '(' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
test = parseExpression ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expect ( ')' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( match ( ';' ) ) {
lex ( ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return delegate . createDoWhileStatement ( body , test ) ;
2014-07-26 22:44:12 +00:00
}
function parseWhileStatement ( ) {
2018-04-27 21:25:04 +00:00
var test , body , oldInIteration ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expectKeyword ( 'while' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expect ( '(' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
test = parseExpression ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expect ( ')' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
oldInIteration = state . inIteration ;
state . inIteration = true ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
body = parseStatement ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
state . inIteration = oldInIteration ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return delegate . createWhileStatement ( test , body ) ;
2014-07-26 22:44:12 +00:00
}
function parseForVariableDeclaration ( ) {
2018-04-27 21:25:04 +00:00
var token , declarations , startToken ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
startToken = lookahead ;
token = lex ( ) ;
declarations = parseVariableDeclarationList ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return delegate . markEnd (
delegate . createVariableDeclaration ( declarations , token . value ) ,
startToken
) ;
2014-07-26 22:44:12 +00:00
}
function parseForStatement ( ) {
2018-04-27 21:25:04 +00:00
var init , test , update , left , right , body , oldInIteration ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
init = test = update = null ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expectKeyword ( 'for' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expect ( '(' ) ;
if ( match ( ';' ) ) {
lex ( ) ;
} else {
if ( matchKeyword ( 'var' ) || matchKeyword ( 'let' ) ) {
state . allowIn = false ;
init = parseForVariableDeclaration ( ) ;
state . allowIn = true ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( init . declarations . length === 1 && matchKeyword ( 'in' ) ) {
2014-07-26 22:44:12 +00:00
lex ( ) ;
2018-04-27 21:25:04 +00:00
left = init ;
right = parseExpression ( ) ;
init = null ;
}
2014-07-26 22:44:12 +00:00
} else {
2018-04-27 21:25:04 +00:00
state . allowIn = false ;
init = parseExpression ( ) ;
state . allowIn = true ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( matchKeyword ( 'in' ) ) {
// LeftHandSideExpression
if ( ! isLeftHandSide ( init ) ) {
throwErrorTolerant ( { } , Messages . InvalidLHSInForIn ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
lex ( ) ;
left = init ;
right = parseExpression ( ) ;
init = null ;
}
2014-07-26 22:44:12 +00:00
}
if ( typeof left === 'undefined' ) {
2018-04-27 21:25:04 +00:00
expect ( ';' ) ;
}
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( typeof left === 'undefined' ) {
if ( ! match ( ';' ) ) {
test = parseExpression ( ) ;
}
expect ( ';' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( ! match ( ')' ) ) {
update = parseExpression ( ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expect ( ')' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
oldInIteration = state . inIteration ;
state . inIteration = true ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
body = parseStatement ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
state . inIteration = oldInIteration ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return typeof left === 'undefined'
? delegate . createForStatement ( init , test , update , body )
: delegate . createForInStatement ( left , right , body ) ;
2014-07-26 22:44:12 +00:00
}
// 12.7 The continue statement
function parseContinueStatement ( ) {
2018-04-27 21:25:04 +00:00
var label = null ,
key ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expectKeyword ( 'continue' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
// Optimize the most common form: 'continue;'.
if ( source . charCodeAt ( index ) === 0x3b ) {
lex ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( ! state . inIteration ) {
throwError ( { } , Messages . IllegalContinue ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
return delegate . createContinueStatement ( null ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( peekLineTerminator ( ) ) {
if ( ! state . inIteration ) {
throwError ( { } , Messages . IllegalContinue ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
return delegate . createContinueStatement ( null ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( lookahead . type === Token . Identifier ) {
label = parseVariableIdentifier ( ) ;
key = '$' + label . name ;
if ( ! Object . prototype . hasOwnProperty . call ( state . labelSet , key ) ) {
throwError ( { } , Messages . UnknownLabel , label . name ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
consumeSemicolon ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( label === null && ! state . inIteration ) {
throwError ( { } , Messages . IllegalContinue ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return delegate . createContinueStatement ( label ) ;
2014-07-26 22:44:12 +00:00
}
// 12.8 The break statement
function parseBreakStatement ( ) {
2018-04-27 21:25:04 +00:00
var label = null ,
key ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expectKeyword ( 'break' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
// Catch the very common case first: immediately a semicolon (U+003B).
if ( source . charCodeAt ( index ) === 0x3b ) {
lex ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( ! ( state . inIteration || state . inSwitch ) ) {
throwError ( { } , Messages . IllegalBreak ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
return delegate . createBreakStatement ( null ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( peekLineTerminator ( ) ) {
if ( ! ( state . inIteration || state . inSwitch ) ) {
throwError ( { } , Messages . IllegalBreak ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
return delegate . createBreakStatement ( null ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( lookahead . type === Token . Identifier ) {
label = parseVariableIdentifier ( ) ;
key = '$' + label . name ;
if ( ! Object . prototype . hasOwnProperty . call ( state . labelSet , key ) ) {
throwError ( { } , Messages . UnknownLabel , label . name ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
consumeSemicolon ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( label === null && ! ( state . inIteration || state . inSwitch ) ) {
throwError ( { } , Messages . IllegalBreak ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return delegate . createBreakStatement ( label ) ;
2014-07-26 22:44:12 +00:00
}
// 12.9 The return statement
function parseReturnStatement ( ) {
2018-04-27 21:25:04 +00:00
var argument = null ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expectKeyword ( 'return' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( ! state . inFunctionBody ) {
throwErrorTolerant ( { } , Messages . IllegalReturn ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
// 'return' followed by a space and an identifier is very common.
if ( source . charCodeAt ( index ) === 0x20 ) {
if ( isIdentifierStart ( source . charCodeAt ( index + 1 ) ) ) {
argument = parseExpression ( ) ;
consumeSemicolon ( ) ;
return delegate . createReturnStatement ( argument ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( peekLineTerminator ( ) ) {
return delegate . createReturnStatement ( null ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( ! match ( ';' ) ) {
if ( ! match ( '}' ) && lookahead . type !== Token . EOF ) {
argument = parseExpression ( ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
consumeSemicolon ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return delegate . createReturnStatement ( argument ) ;
2014-07-26 22:44:12 +00:00
}
// 12.10 The with statement
function parseWithStatement ( ) {
2018-04-27 21:25:04 +00:00
var object , body ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( strict ) {
// TODO(ikarienator): Should we update the test cases instead?
skipComment ( ) ;
throwErrorTolerant ( { } , Messages . StrictModeWith ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expectKeyword ( 'with' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expect ( '(' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
object = parseExpression ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expect ( ')' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
body = parseStatement ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return delegate . createWithStatement ( object , body ) ;
2014-07-26 22:44:12 +00:00
}
// 12.10 The swith statement
function parseSwitchCase ( ) {
2018-04-27 21:25:04 +00:00
var test ,
consequent = [ ] ,
statement ,
startToken ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
startToken = lookahead ;
if ( matchKeyword ( 'default' ) ) {
lex ( ) ;
test = null ;
} else {
expectKeyword ( 'case' ) ;
test = parseExpression ( ) ;
}
expect ( ':' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
while ( index < length ) {
if ( match ( '}' ) || matchKeyword ( 'default' ) || matchKeyword ( 'case' ) ) {
break ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
statement = parseStatement ( ) ;
consequent . push ( statement ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return delegate . markEnd (
delegate . createSwitchCase ( test , consequent ) ,
startToken
) ;
2014-07-26 22:44:12 +00:00
}
function parseSwitchStatement ( ) {
2018-04-27 21:25:04 +00:00
var discriminant , cases , clause , oldInSwitch , defaultFound ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expectKeyword ( 'switch' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expect ( '(' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
discriminant = parseExpression ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expect ( ')' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expect ( '{' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
cases = [ ] ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( match ( '}' ) ) {
lex ( ) ;
return delegate . createSwitchStatement ( discriminant , cases ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
oldInSwitch = state . inSwitch ;
state . inSwitch = true ;
defaultFound = false ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
while ( index < length ) {
if ( match ( '}' ) ) {
break ;
}
clause = parseSwitchCase ( ) ;
if ( clause . test === null ) {
if ( defaultFound ) {
throwError ( { } , Messages . MultipleDefaultsInSwitch ) ;
}
defaultFound = true ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
cases . push ( clause ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
state . inSwitch = oldInSwitch ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expect ( '}' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return delegate . createSwitchStatement ( discriminant , cases ) ;
2014-07-26 22:44:12 +00:00
}
// 12.13 The throw statement
function parseThrowStatement ( ) {
2018-04-27 21:25:04 +00:00
var argument ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expectKeyword ( 'throw' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( peekLineTerminator ( ) ) {
throwError ( { } , Messages . NewlineAfterThrow ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
argument = parseExpression ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
consumeSemicolon ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return delegate . createThrowStatement ( argument ) ;
2014-07-26 22:44:12 +00:00
}
// 12.14 The try statement
function parseCatchClause ( ) {
2018-04-27 21:25:04 +00:00
var param , body , startToken ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
startToken = lookahead ;
expectKeyword ( 'catch' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expect ( '(' ) ;
if ( match ( ')' ) ) {
throwUnexpected ( lookahead ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
param = parseVariableIdentifier ( ) ;
// 12.14.1
if ( strict && isRestrictedWord ( param . name ) ) {
throwErrorTolerant ( { } , Messages . StrictCatchVariable ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expect ( ')' ) ;
body = parseBlock ( ) ;
return delegate . markEnd (
delegate . createCatchClause ( param , body ) ,
startToken
) ;
2014-07-26 22:44:12 +00:00
}
function parseTryStatement ( ) {
2018-04-27 21:25:04 +00:00
var block ,
handlers = [ ] ,
finalizer = null ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expectKeyword ( 'try' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
block = parseBlock ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( matchKeyword ( 'catch' ) ) {
handlers . push ( parseCatchClause ( ) ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( matchKeyword ( 'finally' ) ) {
lex ( ) ;
finalizer = parseBlock ( ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( handlers . length === 0 && ! finalizer ) {
throwError ( { } , Messages . NoCatchOrFinally ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return delegate . createTryStatement ( block , [ ] , handlers , finalizer ) ;
2014-07-26 22:44:12 +00:00
}
// 12.15 The debugger statement
function parseDebuggerStatement ( ) {
2018-04-27 21:25:04 +00:00
expectKeyword ( 'debugger' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
consumeSemicolon ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return delegate . createDebuggerStatement ( ) ;
2014-07-26 22:44:12 +00:00
}
// 12 Statements
function parseStatement ( ) {
2018-04-27 21:25:04 +00:00
var type = lookahead . type ,
expr ,
labeledBody ,
key ,
startToken ;
if ( type === Token . EOF ) {
throwUnexpected ( lookahead ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( type === Token . Punctuator && lookahead . value === '{' ) {
return parseBlock ( ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
startToken = lookahead ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( type === Token . Punctuator ) {
switch ( lookahead . value ) {
case ';' :
return delegate . markEnd ( parseEmptyStatement ( ) , startToken ) ;
case '(' :
return delegate . markEnd ( parseExpressionStatement ( ) , startToken ) ;
default :
break ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( type === Token . Keyword ) {
switch ( lookahead . value ) {
case 'break' :
return delegate . markEnd ( parseBreakStatement ( ) , startToken ) ;
case 'continue' :
return delegate . markEnd ( parseContinueStatement ( ) , startToken ) ;
case 'debugger' :
return delegate . markEnd ( parseDebuggerStatement ( ) , startToken ) ;
case 'do' :
return delegate . markEnd ( parseDoWhileStatement ( ) , startToken ) ;
case 'for' :
return delegate . markEnd ( parseForStatement ( ) , startToken ) ;
case 'function' :
return delegate . markEnd ( parseFunctionDeclaration ( ) , startToken ) ;
case 'if' :
return delegate . markEnd ( parseIfStatement ( ) , startToken ) ;
case 'return' :
return delegate . markEnd ( parseReturnStatement ( ) , startToken ) ;
case 'switch' :
return delegate . markEnd ( parseSwitchStatement ( ) , startToken ) ;
case 'throw' :
return delegate . markEnd ( parseThrowStatement ( ) , startToken ) ;
case 'try' :
return delegate . markEnd ( parseTryStatement ( ) , startToken ) ;
case 'var' :
return delegate . markEnd ( parseVariableStatement ( ) , startToken ) ;
case 'while' :
return delegate . markEnd ( parseWhileStatement ( ) , startToken ) ;
case 'with' :
return delegate . markEnd ( parseWithStatement ( ) , startToken ) ;
default :
break ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expr = parseExpression ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
// 12.12 Labelled Statements
if ( expr . type === Syntax . Identifier && match ( ':' ) ) {
lex ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
key = '$' + expr . name ;
if ( Object . prototype . hasOwnProperty . call ( state . labelSet , key ) ) {
throwError ( { } , Messages . Redeclaration , 'Label' , expr . name ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
state . labelSet [ key ] = true ;
labeledBody = parseStatement ( ) ;
delete state . labelSet [ key ] ;
return delegate . markEnd (
delegate . createLabeledStatement ( expr , labeledBody ) ,
startToken
) ;
}
consumeSemicolon ( ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return delegate . markEnd (
delegate . createExpressionStatement ( expr ) ,
startToken
) ;
2014-07-26 22:44:12 +00:00
}
// 13 Function Definition
function parseFunctionSourceElements ( ) {
2018-04-27 21:25:04 +00:00
var sourceElement ,
sourceElements = [ ] ,
token ,
directive ,
firstRestricted ,
oldLabelSet ,
oldInIteration ,
oldInSwitch ,
oldInFunctionBody ,
startToken ;
startToken = lookahead ;
expect ( '{' ) ;
while ( index < length ) {
if ( lookahead . type !== Token . StringLiteral ) {
break ;
}
token = lookahead ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
sourceElement = parseSourceElement ( ) ;
sourceElements . push ( sourceElement ) ;
if ( sourceElement . expression . type !== Syntax . Literal ) {
// this is not directive
break ;
}
directive = source . slice ( token . start + 1 , token . end - 1 ) ;
if ( directive === 'use strict' ) {
strict = true ;
if ( firstRestricted ) {
throwErrorTolerant ( firstRestricted , Messages . StrictOctalLiteral ) ;
}
} else {
if ( ! firstRestricted && token . octal ) {
firstRestricted = token ;
}
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
oldLabelSet = state . labelSet ;
oldInIteration = state . inIteration ;
oldInSwitch = state . inSwitch ;
oldInFunctionBody = state . inFunctionBody ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
state . labelSet = { } ;
state . inIteration = false ;
state . inSwitch = false ;
state . inFunctionBody = true ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
while ( index < length ) {
if ( match ( '}' ) ) {
break ;
}
sourceElement = parseSourceElement ( ) ;
if ( typeof sourceElement === 'undefined' ) {
break ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
sourceElements . push ( sourceElement ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expect ( '}' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
state . labelSet = oldLabelSet ;
state . inIteration = oldInIteration ;
state . inSwitch = oldInSwitch ;
state . inFunctionBody = oldInFunctionBody ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return delegate . markEnd (
delegate . createBlockStatement ( sourceElements ) ,
startToken
) ;
2014-07-26 22:44:12 +00:00
}
function parseParams ( firstRestricted ) {
2018-04-27 21:25:04 +00:00
var param ,
params = [ ] ,
token ,
stricted ,
paramSet ,
key ,
message ;
expect ( '(' ) ;
if ( ! match ( ')' ) ) {
paramSet = { } ;
while ( index < length ) {
token = lookahead ;
param = parseVariableIdentifier ( ) ;
key = '$' + token . value ;
if ( strict ) {
if ( isRestrictedWord ( token . value ) ) {
stricted = token ;
message = Messages . StrictParamName ;
}
if ( Object . prototype . hasOwnProperty . call ( paramSet , key ) ) {
stricted = token ;
message = Messages . StrictParamDupe ;
}
} else if ( ! firstRestricted ) {
if ( isRestrictedWord ( token . value ) ) {
firstRestricted = token ;
message = Messages . StrictParamName ;
} else if ( isStrictModeReservedWord ( token . value ) ) {
firstRestricted = token ;
message = Messages . StrictReservedWord ;
} else if ( Object . prototype . hasOwnProperty . call ( paramSet , key ) ) {
firstRestricted = token ;
message = Messages . StrictParamDupe ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
params . push ( param ) ;
paramSet [ key ] = true ;
if ( match ( ')' ) ) {
break ;
}
expect ( ',' ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
expect ( ')' ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return {
params : params ,
stricted : stricted ,
firstRestricted : firstRestricted ,
message : message ,
} ;
2014-07-26 22:44:12 +00:00
}
function parseFunctionDeclaration ( ) {
2018-04-27 21:25:04 +00:00
var id ,
params = [ ] ,
body ,
token ,
stricted ,
tmp ,
firstRestricted ,
message ,
previousStrict ,
startToken ;
startToken = lookahead ;
expectKeyword ( 'function' ) ;
token = lookahead ;
id = parseVariableIdentifier ( ) ;
if ( strict ) {
if ( isRestrictedWord ( token . value ) ) {
throwErrorTolerant ( token , Messages . StrictFunctionName ) ;
}
} else {
if ( isRestrictedWord ( token . value ) ) {
firstRestricted = token ;
message = Messages . StrictFunctionName ;
} else if ( isStrictModeReservedWord ( token . value ) ) {
firstRestricted = token ;
message = Messages . StrictReservedWord ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
tmp = parseParams ( firstRestricted ) ;
params = tmp . params ;
stricted = tmp . stricted ;
firstRestricted = tmp . firstRestricted ;
if ( tmp . message ) {
message = tmp . message ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
previousStrict = strict ;
body = parseFunctionSourceElements ( ) ;
if ( strict && firstRestricted ) {
throwError ( firstRestricted , message ) ;
}
if ( strict && stricted ) {
throwErrorTolerant ( stricted , message ) ;
}
strict = previousStrict ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return delegate . markEnd (
delegate . createFunctionDeclaration ( id , params , [ ] , body ) ,
startToken
) ;
2014-07-26 22:44:12 +00:00
}
function parseFunctionExpression ( ) {
2018-04-27 21:25:04 +00:00
var token ,
id = null ,
stricted ,
firstRestricted ,
message ,
tmp ,
params = [ ] ,
body ,
previousStrict ,
startToken ;
startToken = lookahead ;
expectKeyword ( 'function' ) ;
if ( ! match ( '(' ) ) {
token = lookahead ;
id = parseVariableIdentifier ( ) ;
if ( strict ) {
if ( isRestrictedWord ( token . value ) ) {
throwErrorTolerant ( token , Messages . StrictFunctionName ) ;
}
} else {
if ( isRestrictedWord ( token . value ) ) {
firstRestricted = token ;
message = Messages . StrictFunctionName ;
} else if ( isStrictModeReservedWord ( token . value ) ) {
firstRestricted = token ;
message = Messages . StrictReservedWord ;
}
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
tmp = parseParams ( firstRestricted ) ;
params = tmp . params ;
stricted = tmp . stricted ;
firstRestricted = tmp . firstRestricted ;
if ( tmp . message ) {
message = tmp . message ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
previousStrict = strict ;
body = parseFunctionSourceElements ( ) ;
if ( strict && firstRestricted ) {
throwError ( firstRestricted , message ) ;
}
if ( strict && stricted ) {
throwErrorTolerant ( stricted , message ) ;
}
strict = previousStrict ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return delegate . markEnd (
delegate . createFunctionExpression ( id , params , [ ] , body ) ,
startToken
) ;
2014-07-26 22:44:12 +00:00
}
// 14 Program
function parseSourceElement ( ) {
2018-04-27 21:25:04 +00:00
if ( lookahead . type === Token . Keyword ) {
switch ( lookahead . value ) {
case 'const' :
case 'let' :
return parseConstLetDeclaration ( lookahead . value ) ;
case 'function' :
return parseFunctionDeclaration ( ) ;
default :
2014-07-26 22:44:12 +00:00
return parseStatement ( ) ;
}
2018-04-27 21:25:04 +00:00
}
if ( lookahead . type !== Token . EOF ) {
return parseStatement ( ) ;
}
2014-07-26 22:44:12 +00:00
}
function parseSourceElements ( ) {
2018-04-27 21:25:04 +00:00
var sourceElement ,
sourceElements = [ ] ,
token ,
directive ,
firstRestricted ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
while ( index < length ) {
token = lookahead ;
if ( token . type !== Token . StringLiteral ) {
break ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
sourceElement = parseSourceElement ( ) ;
sourceElements . push ( sourceElement ) ;
if ( sourceElement . expression . type !== Syntax . Literal ) {
// this is not directive
break ;
}
directive = source . slice ( token . start + 1 , token . end - 1 ) ;
if ( directive === 'use strict' ) {
strict = true ;
if ( firstRestricted ) {
throwErrorTolerant ( firstRestricted , Messages . StrictOctalLiteral ) ;
}
} else {
if ( ! firstRestricted && token . octal ) {
firstRestricted = token ;
}
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
while ( index < length ) {
sourceElement = parseSourceElement ( ) ;
/* istanbul ignore if */
if ( typeof sourceElement === 'undefined' ) {
break ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
sourceElements . push ( sourceElement ) ;
}
return sourceElements ;
2014-07-26 22:44:12 +00:00
}
function parseProgram ( ) {
2018-04-27 21:25:04 +00:00
var body , startToken ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
skipComment ( ) ;
peek ( ) ;
startToken = lookahead ;
strict = false ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
body = parseSourceElements ( ) ;
return delegate . markEnd ( delegate . createProgram ( body ) , startToken ) ;
2014-07-26 22:44:12 +00:00
}
function filterTokenLocation ( ) {
2018-04-27 21:25:04 +00:00
var i ,
entry ,
token ,
tokens = [ ] ;
for ( i = 0 ; i < extra . tokens . length ; ++ i ) {
entry = extra . tokens [ i ] ;
token = {
type : entry . type ,
value : entry . value ,
} ;
if ( extra . range ) {
token . range = entry . range ;
}
if ( extra . loc ) {
token . loc = entry . loc ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
tokens . push ( token ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
extra . tokens = tokens ;
2014-07-26 22:44:12 +00:00
}
function tokenize ( code , options ) {
2018-04-27 21:25:04 +00:00
var toString , token , tokens ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
toString = String ;
if ( typeof code !== 'string' && ! ( code instanceof String ) ) {
code = toString ( code ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
delegate = SyntaxTreeDelegate ;
source = code ;
index = 0 ;
lineNumber = source . length > 0 ? 1 : 0 ;
lineStart = 0 ;
length = source . length ;
lookahead = null ;
state = {
allowIn : true ,
labelSet : { } ,
inFunctionBody : false ,
inIteration : false ,
inSwitch : false ,
lastCommentStart : - 1 ,
} ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
extra = { } ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
// Options matching.
options = options || { } ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
// Of course we collect tokens here.
options . tokens = true ;
extra . tokens = [ ] ;
extra . tokenize = true ;
// The following two fields are necessary to compute the Regex tokens.
extra . openParenToken = - 1 ;
extra . openCurlyToken = - 1 ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
extra . range = typeof options . range === 'boolean' && options . range ;
extra . loc = typeof options . loc === 'boolean' && options . loc ;
if ( typeof options . comment === 'boolean' && options . comment ) {
extra . comments = [ ] ;
}
if ( typeof options . tolerant === 'boolean' && options . tolerant ) {
extra . errors = [ ] ;
}
try {
peek ( ) ;
if ( lookahead . type === Token . EOF ) {
return extra . tokens ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
token = lex ( ) ;
while ( lookahead . type !== Token . EOF ) {
try {
2014-07-26 22:44:12 +00:00
token = lex ( ) ;
2018-04-27 21:25:04 +00:00
} catch ( lexError ) {
token = lookahead ;
if ( extra . errors ) {
extra . errors . push ( lexError ) ;
// We have to break on the first error
// to avoid infinite loops.
break ;
} else {
throw lexError ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
filterTokenLocation ( ) ;
tokens = extra . tokens ;
if ( typeof extra . comments !== 'undefined' ) {
tokens . comments = extra . comments ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
if ( typeof extra . errors !== 'undefined' ) {
tokens . errors = extra . errors ;
}
} catch ( e ) {
throw e ;
} finally {
extra = { } ;
}
return tokens ;
2014-07-26 22:44:12 +00:00
}
function parse ( code , options ) {
2018-04-27 21:25:04 +00:00
var program , toString ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
toString = String ;
if ( typeof code !== 'string' && ! ( code instanceof String ) ) {
code = toString ( code ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
delegate = SyntaxTreeDelegate ;
source = code ;
index = 0 ;
lineNumber = source . length > 0 ? 1 : 0 ;
lineStart = 0 ;
length = source . length ;
lookahead = null ;
state = {
allowIn : true ,
labelSet : { } ,
inFunctionBody : false ,
inIteration : false ,
inSwitch : false ,
lastCommentStart : - 1 ,
} ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
extra = { } ;
if ( typeof options !== 'undefined' ) {
extra . range = typeof options . range === 'boolean' && options . range ;
extra . loc = typeof options . loc === 'boolean' && options . loc ;
extra . attachComment =
typeof options . attachComment === 'boolean' && options . attachComment ;
if (
extra . loc &&
options . source !== null &&
options . source !== undefined
) {
extra . source = toString ( options . source ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
if ( typeof options . tokens === 'boolean' && options . tokens ) {
extra . tokens = [ ] ;
}
if ( typeof options . comment === 'boolean' && options . comment ) {
extra . comments = [ ] ;
}
if ( typeof options . tolerant === 'boolean' && options . tolerant ) {
extra . errors = [ ] ;
}
if ( extra . attachComment ) {
extra . range = true ;
extra . comments = [ ] ;
extra . bottomRightStack = [ ] ;
extra . trailingComments = [ ] ;
extra . leadingComments = [ ] ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
try {
program = parseProgram ( ) ;
if ( typeof extra . comments !== 'undefined' ) {
program . comments = extra . comments ;
}
if ( typeof extra . tokens !== 'undefined' ) {
filterTokenLocation ( ) ;
program . tokens = extra . tokens ;
}
if ( typeof extra . errors !== 'undefined' ) {
program . errors = extra . errors ;
}
} catch ( e ) {
throw e ;
} finally {
extra = { } ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return program ;
2014-07-26 22:44:12 +00:00
}
// Sync with *.json manifests.
exports . version = '1.2.2' ;
exports . tokenize = tokenize ;
exports . parse = parse ;
// Deep copy.
2018-04-27 21:25:04 +00:00
/* istanbul ignore next */
exports . Syntax = ( function ( ) {
var name ,
types = { } ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( typeof Object . create === 'function' ) {
types = Object . create ( null ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
for ( name in Syntax ) {
if ( Syntax . hasOwnProperty ( name ) ) {
types [ name ] = Syntax [ name ] ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( typeof Object . freeze === 'function' ) {
Object . freeze ( types ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return types ;
} ) ( ) ;
} ) ;
/* vim: set sw=4 ts=4 et tw=80 : */
2014-07-26 22:44:12 +00:00
} ) ( null ) ;
/ * !
* falafel ( c ) James Halliday / MIT License
* https : //github.com/substack/node-falafel
* /
2018-04-27 21:25:04 +00:00
( function ( require , module ) {
var parse = require ( 'esprima' ) . parse ;
var objectKeys =
Object . keys ||
function ( obj ) {
var keys = [ ] ;
for ( var key in obj ) keys . push ( key ) ;
return keys ;
} ;
var forEach = function ( xs , fn ) {
2014-07-26 22:44:12 +00:00
if ( xs . forEach ) return xs . forEach ( fn ) ;
for ( var i = 0 ; i < xs . length ; i ++ ) {
2018-04-27 21:25:04 +00:00
fn . call ( xs , xs [ i ] , i , xs ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
} ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
var isArray =
Array . isArray ||
function ( xs ) {
return Object . prototype . toString . call ( xs ) === '[object Array]' ;
} ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
module . exports = function ( src , opts , fn ) {
2014-07-26 22:44:12 +00:00
if ( typeof opts === 'function' ) {
2018-04-27 21:25:04 +00:00
fn = opts ;
opts = { } ;
2014-07-26 22:44:12 +00:00
}
if ( typeof src === 'object' ) {
2018-04-27 21:25:04 +00:00
opts = src ;
src = opts . source ;
delete opts . source ;
2014-07-26 22:44:12 +00:00
}
src = src === undefined ? opts . source : src ;
opts . range = true ;
if ( typeof src !== 'string' ) src = String ( src ) ;
2018-04-27 21:25:04 +00:00
2014-07-26 22:44:12 +00:00
var ast = parse ( src , opts ) ;
2018-04-27 21:25:04 +00:00
2014-07-26 22:44:12 +00:00
var result = {
2018-04-27 21:25:04 +00:00
chunks : src . split ( '' ) ,
toString : function ( ) {
return result . chunks . join ( '' ) ;
} ,
inspect : function ( ) {
return result . toString ( ) ;
} ,
2014-07-26 22:44:12 +00:00
} ;
var index = 0 ;
2018-04-27 21:25:04 +00:00
( function walk ( node , parent ) {
insertHelpers ( node , parent , result . chunks ) ;
forEach ( objectKeys ( node ) , function ( key ) {
if ( key === 'parent' ) return ;
var child = node [ key ] ;
if ( isArray ( child ) ) {
forEach ( child , function ( c ) {
if ( c && typeof c . type === 'string' ) {
walk ( c , node ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
} ) ;
} else if ( child && typeof child . type === 'string' ) {
insertHelpers ( child , node , result . chunks ) ;
walk ( child , node ) ;
}
} ) ;
fn ( node ) ;
2014-07-26 22:44:12 +00:00
} ) ( ast , undefined ) ;
2018-04-27 21:25:04 +00:00
2014-07-26 22:44:12 +00:00
return result ;
2018-04-27 21:25:04 +00:00
} ;
function insertHelpers ( node , parent , chunks ) {
2014-07-26 22:44:12 +00:00
if ( ! node . range ) return ;
2018-04-27 21:25:04 +00:00
2014-07-26 22:44:12 +00:00
node . parent = parent ;
2018-04-27 21:25:04 +00:00
node . source = function ( ) {
return chunks . slice ( node . range [ 0 ] , node . range [ 1 ] ) . join ( '' ) ;
2014-07-26 22:44:12 +00:00
} ;
2018-04-27 21:25:04 +00:00
2014-07-26 22:44:12 +00:00
if ( node . update && typeof node . update === 'object' ) {
2018-04-27 21:25:04 +00:00
var prev = node . update ;
forEach ( objectKeys ( prev ) , function ( key ) {
update [ key ] = prev [ key ] ;
} ) ;
node . update = update ;
} else {
node . update = update ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
function update ( s ) {
chunks [ node . range [ 0 ] ] = s ;
for ( var i = node . range [ 0 ] + 1 ; i < node . range [ 1 ] ; i ++ ) {
chunks [ i ] = '' ;
}
}
}
window . falafel = module . exports ;
} ) (
function ( ) {
return { parse : esprima . parse } ;
} ,
{ exports : { } }
) ;
2014-07-26 22:44:12 +00:00
var inBrowser = typeof window !== 'undefined' && this === window ;
2018-04-27 21:25:04 +00:00
var parseAndModify = inBrowser ? window . falafel : require ( 'falafel' ) ;
( inBrowser ? window : exports ) . blanket = ( function ( ) {
var linesToAddTracking = [
'ExpressionStatement' ,
'BreakStatement' ,
'ContinueStatement' ,
'VariableDeclaration' ,
'ReturnStatement' ,
'ThrowStatement' ,
'TryStatement' ,
'FunctionDeclaration' ,
'IfStatement' ,
'WhileStatement' ,
'DoWhileStatement' ,
'ForStatement' ,
'ForInStatement' ,
'SwitchStatement' ,
'WithStatement' ,
2014-07-26 22:44:12 +00:00
] ,
linesToAddBrackets = [
2018-04-27 21:25:04 +00:00
'IfStatement' ,
'WhileStatement' ,
'DoWhileStatement' ,
'ForStatement' ,
'ForInStatement' ,
'WithStatement' ,
2014-07-26 22:44:12 +00:00
] ,
_ _blanket ,
2018-04-27 21:25:04 +00:00
copynumber = Math . floor ( Math . random ( ) * 1000 ) ,
coverageInfo = { } ,
options = {
reporter : null ,
adapter : null ,
filter : null ,
customVariable : null ,
loader : null ,
ignoreScriptError : false ,
existingRequireJS : false ,
autoStart : false ,
timeout : 180 ,
ignoreCors : false ,
branchTracking : false ,
sourceURL : false ,
debug : false ,
engineOnly : false ,
testReadyCallback : null ,
commonJS : false ,
instrumentCache : false ,
modulePattern : null ,
2014-07-26 22:44:12 +00:00
} ;
2018-04-27 21:25:04 +00:00
if ( inBrowser && typeof window . blanket !== 'undefined' ) {
_ _blanket = window . blanket . noConflict ( ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
_blanket = {
noConflict : function ( ) {
if ( _ _blanket ) {
return _ _blanket ;
}
return _blanket ;
} ,
_getCopyNumber : function ( ) {
//internal method
//for differentiating between instances
return copynumber ;
} ,
extend : function ( obj ) {
//borrowed from underscore
_blanket . _extend ( _blanket , obj ) ;
} ,
_extend : function ( dest , source ) {
if ( source ) {
for ( var prop in source ) {
if (
dest [ prop ] instanceof Object &&
typeof dest [ prop ] !== 'function'
) {
_blanket . _extend ( dest [ prop ] , source [ prop ] ) ;
} else {
dest [ prop ] = source [ prop ] ;
}
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
} ,
getCovVar : function ( ) {
var opt = _blanket . options ( 'customVariable' ) ;
if ( opt ) {
if ( _blanket . options ( 'debug' ) ) {
console . log ( 'BLANKET-Using custom tracking variable:' , opt ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
return inBrowser ? 'window.' + opt : opt ;
}
return inBrowser ? 'window._$blanket' : '_$jscoverage' ;
} ,
options : function ( key , value ) {
if ( typeof key !== 'string' ) {
_blanket . _extend ( options , key ) ;
} else if ( typeof value === 'undefined' ) {
return options [ key ] ;
} else {
options [ key ] = value ;
}
} ,
instrument : function ( config , next ) {
//check instrumented hash table,
//return instrumented code if available.
var inFile = config . inputFile ,
inFileName = config . inputFileName ;
//check instrument cache
if (
_blanket . options ( 'instrumentCache' ) &&
sessionStorage &&
sessionStorage . getItem ( 'blanket_instrument_store-' + inFileName )
) {
if ( _blanket . options ( 'debug' ) ) {
console . log (
'BLANKET-Reading instrumentation from cache: ' ,
inFileName
) ;
}
next ( sessionStorage . getItem ( 'blanket_instrument_store-' + inFileName ) ) ;
} else {
var sourceArray = _blanket . _prepareSource ( inFile ) ;
_blanket . _trackingArraySetup = [ ] ;
//remove shebang
inFile = inFile . replace ( /^\#\!.*/ , '' ) ;
var instrumented = parseAndModify (
inFile ,
{ loc : true , comment : true } ,
_blanket . _addTracking ( inFileName )
) ;
instrumented =
_blanket . _trackingSetup ( inFileName , sourceArray ) + instrumented ;
if ( _blanket . options ( 'sourceURL' ) ) {
instrumented +=
'\n//@ sourceURL=' + inFileName . replace ( 'http://' , '' ) ;
}
if ( _blanket . options ( 'debug' ) ) {
console . log ( 'BLANKET-Instrumented file: ' , inFileName ) ;
}
if ( _blanket . options ( 'instrumentCache' ) && sessionStorage ) {
if ( _blanket . options ( 'debug' ) ) {
console . log (
'BLANKET-Saving instrumentation to cache: ' ,
inFileName
) ;
}
sessionStorage . setItem (
'blanket_instrument_store-' + inFileName ,
instrumented
) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
next ( instrumented ) ;
}
} ,
_trackingArraySetup : [ ] ,
_branchingArraySetup : [ ] ,
_prepareSource : function ( source ) {
return source
. replace ( /\\/g , '\\\\' )
. replace ( /'/g , "\\'" )
. replace ( /(\r\n|\n|\r)/gm , '\n' )
. split ( '\n' ) ;
2014-07-26 22:44:12 +00:00
} ,
2018-04-27 21:25:04 +00:00
_trackingSetup : function ( filename , sourceArray ) {
var branches = _blanket . options ( 'branchTracking' ) ;
var sourceString = sourceArray . join ( "',\n'" ) ;
var intro = '' ;
var covVar = _blanket . getCovVar ( ) ;
intro +=
'if (typeof ' + covVar + " === 'undefined') " + covVar + ' = {};\n' ;
if ( branches ) {
intro += 'var _$branchFcn=function(f,l,c,r){ ' ;
intro += 'if (!!r) { ' ;
intro +=
covVar +
'[f].branchData[l][c][0] = ' +
covVar +
'[f].branchData[l][c][0] || [];' ;
intro += covVar + '[f].branchData[l][c][0].push(r); }' ;
intro += 'else { ' ;
intro +=
covVar +
'[f].branchData[l][c][1] = ' +
covVar +
'[f].branchData[l][c][1] || [];' ;
intro += covVar + '[f].branchData[l][c][1].push(r); }' ;
intro += 'return r;};\n' ;
}
intro +=
'if (typeof ' + covVar + "['" + filename + "'] === 'undefined'){" ;
intro += covVar + "['" + filename + "']=[];\n" ;
if ( branches ) {
intro += covVar + "['" + filename + "'].branchData=[];\n" ;
}
intro +=
covVar + "['" + filename + "'].source=['" + sourceString + "'];\n" ;
//initialize array values
_blanket . _trackingArraySetup
. sort ( function ( a , b ) {
return parseInt ( a , 10 ) > parseInt ( b , 10 ) ;
} )
. forEach ( function ( item ) {
intro += covVar + "['" + filename + "'][" + item + ']=0;\n' ;
} ) ;
if ( branches ) {
_blanket . _branchingArraySetup
. sort ( function ( a , b ) {
return a . line > b . line ;
} )
. sort ( function ( a , b ) {
return a . column > b . column ;
} )
. forEach ( function ( item ) {
if ( item . file === filename ) {
intro +=
'if (typeof ' +
covVar +
"['" +
filename +
"'].branchData[" +
item . line +
"] === 'undefined'){\n" ;
intro +=
covVar +
"['" +
filename +
"'].branchData[" +
item . line +
']=[];\n' ;
intro += '}' ;
intro +=
covVar +
"['" +
filename +
"'].branchData[" +
item . line +
'][' +
item . column +
'] = [];\n' ;
intro +=
covVar +
"['" +
filename +
"'].branchData[" +
item . line +
'][' +
item . column +
'].consequent = ' +
JSON . stringify ( item . consequent ) +
';\n' ;
intro +=
covVar +
"['" +
filename +
"'].branchData[" +
item . line +
'][' +
item . column +
'].alternate = ' +
JSON . stringify ( item . alternate ) +
';\n' ;
}
} ) ;
}
intro += '}' ;
return intro ;
} ,
_blockifyIf : function ( node ) {
if ( linesToAddBrackets . indexOf ( node . type ) > - 1 ) {
var bracketsExistObject = node . consequent || node . body ;
var bracketsExistAlt = node . alternate ;
if ( bracketsExistAlt && bracketsExistAlt . type !== 'BlockStatement' ) {
bracketsExistAlt . update ( '{\n' + bracketsExistAlt . source ( ) + '}\n' ) ;
}
if (
bracketsExistObject &&
bracketsExistObject . type !== 'BlockStatement'
) {
bracketsExistObject . update (
'{\n' + bracketsExistObject . source ( ) + '}\n'
) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
2014-07-26 22:44:12 +00:00
} ,
2018-04-27 21:25:04 +00:00
_trackBranch : function ( node , filename ) {
//recursive on consequent and alternative
var line = node . loc . start . line ;
var col = node . loc . start . column ;
_blanket . _branchingArraySetup . push ( {
line : line ,
column : col ,
file : filename ,
consequent : node . consequent . loc ,
alternate : node . alternate . loc ,
} ) ;
var updated =
'_$branchFcn' +
"('" +
filename +
"'," +
line +
',' +
col +
',' +
node . test . source ( ) +
')?' +
node . consequent . source ( ) +
':' +
node . alternate . source ( ) ;
node . update ( updated ) ;
2014-07-26 22:44:12 +00:00
} ,
2018-04-27 21:25:04 +00:00
_addTracking : function ( filename ) {
//falafel doesn't take a file name
//so we include the filename in a closure
//and return the function to falafel
var covVar = _blanket . getCovVar ( ) ;
return function ( node ) {
_blanket . _blockifyIf ( node ) ;
if (
linesToAddTracking . indexOf ( node . type ) > - 1 &&
node . parent . type !== 'LabeledStatement'
) {
_blanket . _checkDefs ( node , filename ) ;
if (
node . type === 'VariableDeclaration' &&
( node . parent . type === 'ForStatement' ||
node . parent . type === 'ForInStatement' )
) {
2014-07-26 22:44:12 +00:00
return ;
2018-04-27 21:25:04 +00:00
}
if ( node . loc && node . loc . start ) {
node . update (
covVar +
"['" +
filename +
"'][" +
node . loc . start . line +
']++;\n' +
node . source ( )
) ;
_blanket . _trackingArraySetup . push ( node . loc . start . line ) ;
} else {
//I don't think we can handle a node with no location
throw new Error (
'The instrumenter encountered a node with no location: ' +
Object . keys ( node )
) ;
}
} else if (
_blanket . options ( 'branchTracking' ) &&
node . type === 'ConditionalExpression'
) {
_blanket . _trackBranch ( node , filename ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
} ;
2014-07-26 22:44:12 +00:00
} ,
2018-04-27 21:25:04 +00:00
_checkDefs : function ( node , filename ) {
// Make sure developers don't redefine window. if they do, inform them it is wrong.
if ( inBrowser ) {
if ( node . type === 'VariableDeclaration' && node . declarations ) {
node . declarations . forEach ( function ( declaration ) {
if ( declaration . id . name === 'window' ) {
throw new Error (
"Instrumentation error, you cannot redefine the 'window' variable in " +
filename +
':' +
node . loc . start . line
) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
} ) ;
}
if ( node . type === 'FunctionDeclaration' && node . params ) {
node . params . forEach ( function ( param ) {
if ( param . name === 'window' ) {
throw new Error (
"Instrumentation error, you cannot redefine the 'window' variable in " +
filename +
':' +
node . loc . start . line
) ;
}
} ) ;
}
//Make sure developers don't redefine the coverage variable
if (
node . type === 'ExpressionStatement' &&
node . expression &&
node . expression . left &&
node . expression . left . object &&
node . expression . left . property &&
node . expression . left . object . name +
'.' +
node . expression . left . property . name ===
_blanket . getCovVar ( )
) {
throw new Error (
'Instrumentation error, you cannot redefine the coverage variable in ' +
filename +
':' +
node . loc . start . line
) ;
}
} else {
//Make sure developers don't redefine the coverage variable in node
if (
node . type === 'ExpressionStatement' &&
node . expression &&
node . expression . left &&
! node . expression . left . object &&
! node . expression . left . property &&
node . expression . left . name === _blanket . getCovVar ( )
) {
throw new Error (
'Instrumentation error, you cannot redefine the coverage variable in ' +
filename +
':' +
node . loc . start . line
) ;
}
}
} ,
setupCoverage : function ( ) {
coverageInfo . instrumentation = 'blanket' ;
coverageInfo . stats = {
suites : 0 ,
tests : 0 ,
passes : 0 ,
pending : 0 ,
failures : 0 ,
start : new Date ( ) ,
} ;
} ,
_checkIfSetup : function ( ) {
if ( ! coverageInfo . stats ) {
throw new Error ( 'You must call blanket.setupCoverage() first.' ) ;
}
} ,
onTestStart : function ( ) {
if ( _blanket . options ( 'debug' ) ) {
console . log ( 'BLANKET-Test event started' ) ;
}
this . _checkIfSetup ( ) ;
coverageInfo . stats . tests ++ ;
coverageInfo . stats . pending ++ ;
} ,
onTestDone : function ( total , passed ) {
this . _checkIfSetup ( ) ;
if ( passed === total ) {
coverageInfo . stats . passes ++ ;
} else {
coverageInfo . stats . failures ++ ;
}
coverageInfo . stats . pending -- ;
} ,
onModuleStart : function ( ) {
this . _checkIfSetup ( ) ;
coverageInfo . stats . suites ++ ;
} ,
onTestsDone : function ( ) {
if ( _blanket . options ( 'debug' ) ) {
console . log ( 'BLANKET-Test event done' ) ;
}
this . _checkIfSetup ( ) ;
coverageInfo . stats . end = new Date ( ) ;
if ( inBrowser ) {
this . report ( coverageInfo ) ;
} else {
if ( ! _blanket . options ( 'branchTracking' ) ) {
delete ( inBrowser ? window : global ) [ _blanket . getCovVar ( ) ] . branchFcn ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
this . options ( 'reporter' ) . call ( this , coverageInfo ) ;
}
} ,
} ;
return _blanket ;
} ) ( ) ;
( function ( _blanket ) {
var oldOptions = _blanket . options ;
_blanket . extend ( {
outstandingRequireFiles : [ ] ,
options : function ( key , value ) {
var newVal = { } ;
if ( typeof key !== 'string' ) {
//key is key/value map
oldOptions ( key ) ;
newVal = key ;
} else if ( typeof value === 'undefined' ) {
//accessor
return oldOptions ( key ) ;
} else {
//setter
oldOptions ( key , value ) ;
newVal [ key ] = value ;
}
if ( newVal . adapter ) {
_blanket . _loadFile ( newVal . adapter ) ;
}
if ( newVal . loader ) {
_blanket . _loadFile ( newVal . loader ) ;
}
} ,
requiringFile : function ( filename , done ) {
if ( typeof filename === 'undefined' ) {
_blanket . outstandingRequireFiles = [ ] ;
} else if ( typeof done === 'undefined' ) {
_blanket . outstandingRequireFiles . push ( filename ) ;
} else {
_blanket . outstandingRequireFiles . splice (
_blanket . outstandingRequireFiles . indexOf ( filename ) ,
1
) ;
}
} ,
requireFilesLoaded : function ( ) {
return _blanket . outstandingRequireFiles . length === 0 ;
} ,
showManualLoader : function ( ) {
if ( document . getElementById ( 'blanketLoaderDialog' ) ) {
return ;
}
//copied from http://blog.avtex.com/2012/01/26/cross-browser-css-only-modal-box/
var loader = "<div class='blanketDialogOverlay'>" ;
loader += ' </div>' ;
loader += "<div class='blanketDialogVerticalOffset'>" ;
loader += "<div class='blanketDialogBox'>" ;
loader +=
'<b>Error:</b> Blanket.js encountered a cross origin request error while instrumenting the source files. ' ;
loader +=
'<br><br>This is likely caused by the source files being referenced locally (using the file:// protocol). ' ;
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)." ;
loader += '<br>' ;
if ( typeof FileReader !== 'undefined' ) {
loader +=
'<br>Or, try the experimental loader. When prompted, simply click on the directory containing all the source files you want covered.' ;
loader +=
'<a href=\'javascript:document.getElementById("fileInput").click();\'>Start Loader</a>' ;
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'/>" ;
}
loader +=
"<br><span style='float:right;cursor:pointer;' onclick=document.getElementById('blanketLoaderDialog').style.display='none';>Close</span>" ;
loader += "<div style='clear:both'></div>" ;
loader += '</div></div>' ;
var css = '.blanketDialogWrapper {' ;
css += 'display:block;' ;
css += 'position:fixed;' ;
css += 'z-index:40001; }' ;
css += '.blanketDialogOverlay {' ;
css += 'position:fixed;' ;
css += 'width:100%;' ;
css += 'height:100%;' ;
css += 'background-color:black;' ;
css += 'opacity:.5; ' ;
css +=
"-ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=50)'; " ;
css += 'filter:alpha(opacity=50); ' ;
css += 'z-index:40001; }' ;
css += '.blanketDialogVerticalOffset { ' ;
css += 'position:fixed;' ;
css += 'top:30%;' ;
css += 'width:100%;' ;
css += 'z-index:40002; }' ;
css += '.blanketDialogBox { ' ;
css += 'width:405px; ' ;
css += 'position:relative;' ;
css += 'margin:0 auto;' ;
css += 'background-color:white;' ;
css += 'padding:10px;' ;
css += 'border:1px solid black; }' ;
var dom = document . createElement ( 'style' ) ;
dom . innerHTML = css ;
document . head . appendChild ( dom ) ;
var div = document . createElement ( 'div' ) ;
div . id = 'blanketLoaderDialog' ;
div . className = 'blanketDialogWrapper' ;
div . innerHTML = loader ;
document . body . insertBefore ( div , document . body . firstChild ) ;
2014-07-26 22:44:12 +00:00
} ,
2018-04-27 21:25:04 +00:00
manualFileLoader : function ( files ) {
var toArray = Array . prototype . slice ;
files = toArray . call ( files ) . filter ( function ( item ) {
return item . type !== '' ;
} ) ;
var sessionLength = files . length - 1 ;
var sessionIndx = 0 ;
var sessionArray = { } ;
if ( sessionStorage [ 'blanketSessionLoader' ] ) {
sessionArray = JSON . parse ( sessionStorage [ 'blanketSessionLoader' ] ) ;
}
var fileLoader = function ( event ) {
var fileContent = event . currentTarget . result ;
var file = files [ sessionIndx ] ;
var filename =
file . webkitRelativePath && file . webkitRelativePath !== ''
? file . webkitRelativePath
: file . name ;
sessionArray [ filename ] = fileContent ;
sessionIndx ++ ;
if ( sessionIndx === sessionLength ) {
sessionStorage . setItem (
'blanketSessionLoader' ,
JSON . stringify ( sessionArray )
) ;
document . location . reload ( ) ;
} else {
readFile ( files [ sessionIndx ] ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
} ;
function readFile ( file ) {
var reader = new FileReader ( ) ;
reader . onload = fileLoader ;
reader . readAsText ( file ) ;
}
readFile ( files [ sessionIndx ] ) ;
2014-07-26 22:44:12 +00:00
} ,
2018-04-27 21:25:04 +00:00
_loadFile : function ( path ) {
if ( typeof path !== 'undefined' ) {
var request = new XMLHttpRequest ( ) ;
request . open ( 'GET' , path , false ) ;
request . send ( ) ;
_blanket . _addScript ( request . responseText ) ;
}
} ,
_addScript : function ( data ) {
/ * v a r s c r i p t = d o c u m e n t . c r e a t e E l e m e n t ( " s c r i p t " ) ;
2014-07-26 22:44:12 +00:00
script . type = "text/javascript" ;
script . text = data ;
2014-07-26 22:51:06 +00:00
( document . body || document . getElementsByTagName ( 'head' ) [ 0 ] ) . appendChild ( script ) ; * /
2018-04-27 21:25:04 +00:00
( 1 , eval ) ( data ) ;
2014-07-26 22:44:12 +00:00
} ,
2018-04-27 21:25:04 +00:00
hasAdapter : function ( callback ) {
return _blanket . options ( 'adapter' ) !== null ;
2014-07-26 22:44:12 +00:00
} ,
2018-04-27 21:25:04 +00:00
report : function ( coverage _data ) {
if ( ! document . getElementById ( 'blanketLoaderDialog' ) ) {
//all found, clear it
_blanket . blanketSession = null ;
}
coverage _data . files = window . _$blanket ;
var require = blanket . options ( 'commonJS' )
? blanket . _commonjs . require
: window . require ;
// Check if we have any covered files that requires reporting
// otherwise just exit gracefully.
if ( ! coverage _data . files || ! Object . keys ( coverage _data . files ) . length ) {
if ( _blanket . options ( 'debug' ) ) {
console . log ( 'BLANKET-Reporting No files were instrumented.' ) ;
}
return ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( typeof coverage _data . files . branchFcn !== 'undefined' ) {
delete coverage _data . files . branchFcn ;
}
if ( typeof _blanket . options ( 'reporter' ) === 'string' ) {
_blanket . _loadFile ( _blanket . options ( 'reporter' ) ) ;
_blanket . customReporter (
coverage _data ,
_blanket . options ( 'reporter_options' )
) ;
} else if ( typeof _blanket . options ( 'reporter' ) === 'function' ) {
_blanket . options ( 'reporter' ) (
coverage _data ,
_blanket . options ( 'reporter_options' )
) ;
} else if ( typeof _blanket . defaultReporter === 'function' ) {
_blanket . defaultReporter (
coverage _data ,
_blanket . options ( 'reporter_options' )
) ;
} else {
throw new Error ( 'no reporter defined.' ) ;
}
2014-07-26 22:44:12 +00:00
} ,
2018-04-27 21:25:04 +00:00
_bindStartTestRunner : function ( bindEvent , startEvent ) {
if ( bindEvent ) {
bindEvent ( startEvent ) ;
} else {
window . addEventListener ( 'load' , startEvent , false ) ;
}
2014-07-26 22:44:12 +00:00
} ,
2018-04-27 21:25:04 +00:00
_loadSourceFiles : function ( callback ) {
var require = blanket . options ( 'commonJS' )
? blanket . _commonjs . require
: window . require ;
function copy ( o ) {
var _copy = Object . create ( Object . getPrototypeOf ( o ) ) ;
var propNames = Object . getOwnPropertyNames ( o ) ;
propNames . forEach ( function ( name ) {
var desc = Object . getOwnPropertyDescriptor ( o , name ) ;
Object . defineProperty ( _copy , name , desc ) ;
} ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
return _copy ;
}
if ( _blanket . options ( 'debug' ) ) {
console . log ( 'BLANKET-Collecting page scripts' ) ;
}
var scripts = _blanket . utils . collectPageScripts ( ) ;
//_blanket.options("filter",scripts);
if ( scripts . length === 0 ) {
callback ( ) ;
} else {
//check session state
if ( sessionStorage [ 'blanketSessionLoader' ] ) {
_blanket . blanketSession = JSON . parse (
sessionStorage [ 'blanketSessionLoader' ]
) ;
}
scripts . forEach ( function ( file , indx ) {
_blanket . utils . cache [ file ] = {
loaded : false ,
} ;
} ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
var currScript = - 1 ;
_blanket . utils . loadAll ( function ( test ) {
if ( test ) {
return typeof scripts [ currScript + 1 ] !== 'undefined' ;
}
currScript ++ ;
if ( currScript >= scripts . length ) {
return null ;
}
return scripts [ currScript ] ;
} , callback ) ;
}
} ,
beforeStartTestRunner : function ( opts ) {
opts = opts || { } ;
opts . checkRequirejs =
typeof opts . checkRequirejs === 'undefined' ? true : opts . checkRequirejs ;
opts . callback = opts . callback || function ( ) { } ;
opts . coverage =
typeof opts . coverage === 'undefined' ? true : opts . coverage ;
if ( opts . coverage ) {
_blanket . _bindStartTestRunner ( opts . bindEvent , function ( ) {
_blanket . _loadSourceFiles ( function ( ) {
var allLoaded = function ( ) {
return opts . condition
? opts . condition ( )
: _blanket . requireFilesLoaded ( ) ;
} ;
var check = function ( ) {
if ( allLoaded ( ) ) {
if ( _blanket . options ( 'debug' ) ) {
console . log (
'BLANKET-All files loaded, init start test runner callback.'
) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
var cb = _blanket . options ( 'testReadyCallback' ) ;
if ( cb ) {
if ( typeof cb === 'function' ) {
cb ( opts . callback ) ;
} else if ( typeof cb === 'string' ) {
_blanket . _addScript ( cb ) ;
opts . callback ( ) ;
}
} else {
opts . callback ( ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
} else {
setTimeout ( check , 13 ) ;
}
} ;
check ( ) ;
} ) ;
} ) ;
} else {
opts . callback ( ) ;
}
2014-07-26 22:44:12 +00:00
} ,
utils : {
2018-04-27 21:25:04 +00:00
qualifyURL : function ( url ) {
//http://stackoverflow.com/questions/470832/getting-an-absolute-url-from-a-relative-one-ie6-issue
var a = document . createElement ( 'a' ) ;
a . href = url ;
return a . href ;
} ,
} ,
} ) ;
2014-07-26 22:44:12 +00:00
} ) ( blanket ) ;
2018-04-27 21:25:04 +00:00
blanket . defaultReporter = function ( coverage ) {
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;}" ,
successRate = 60 ,
head = document . head ,
fileNumber = 0 ,
body = document . body ,
headerContent ,
hasBranchTracking = Object . keys ( coverage . files ) . some ( function ( elem ) {
return typeof coverage . files [ elem ] . branchData !== 'undefined' ;
} ) ,
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>" ,
fileTemplate =
"<div class='blanket {{statusclass}}'><div class='bl-cl bl-file'><span class='bl-nb'>{{fileNumber}}.</span><a id='blanket-link-file-{{fileNumber}}' href='javascript:void(0)'>{{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>" ;
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>" ;
function blanket _toggleSource ( id ) {
var element = document . getElementById ( id ) ;
if ( element . style . display === 'block' ) {
element . style . display = 'none' ;
} else {
element . style . display = 'block' ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
/ * v a r s c r i p t = d o c u m e n t . c r e a t e E l e m e n t ( " s c r i p t " ) ;
2014-07-26 22:44:12 +00:00
script . type = "text/javascript" ;
script . text = blanket _toggleSource . toString ( ) . replace ( 'function ' + blanket _toggleSource . name , 'function blanket_toggleSource' ) ;
2014-07-26 22:51:06 +00:00
body . appendChild ( script ) ; * /
2018-04-27 21:25:04 +00:00
( 1 , eval ) (
blanket _toggleSource
. toString ( )
. replace (
'function ' + blanket _toggleSource . name ,
'function blanket_toggleSource'
)
) ;
var percentage = function ( number , total ) {
return Math . round ( number / total * 100 * 100 ) / 100 ;
} ;
var appendTag = function ( type , el , str ) {
var dom = document . createElement ( type ) ;
dom . innerHTML = str ;
el . appendChild ( dom ) ;
} ;
function escapeInvalidXmlChars ( str ) {
return str
. replace ( /\&/g , '&' )
. replace ( /</g , '<' )
. replace ( /\>/g , '>' )
. replace ( /\"/g , '"' )
. replace ( /\'/g , ''' ) ;
}
function isBranchFollowed ( data , bool ) {
var mode = bool ? 0 : 1 ;
if (
typeof data === 'undefined' ||
typeof data === null ||
typeof data [ mode ] === 'undefined'
) {
return false ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
return data [ mode ] . length > 0 ;
}
var branchStack = [ ] ;
function branchReport ( colsIndex , src , cols , offset , lineNum ) {
var newsrc = '' ;
var postfix = '' ;
if ( branchStack . length > 0 ) {
newsrc +=
"<span class='" +
( isBranchFollowed (
branchStack [ 0 ] [ 1 ] ,
branchStack [ 0 ] [ 1 ] . consequent === branchStack [ 0 ] [ 0 ]
)
? 'branchOkay'
: 'branchWarning' ) +
"'>" ;
if ( branchStack [ 0 ] [ 0 ] . end . line === lineNum ) {
newsrc +=
escapeInvalidXmlChars ( src . slice ( 0 , branchStack [ 0 ] [ 0 ] . end . column ) ) +
'</span>' ;
src = src . slice ( branchStack [ 0 ] [ 0 ] . end . column ) ;
branchStack . shift ( ) ;
if ( branchStack . length > 0 ) {
newsrc +=
"<span class='" +
( isBranchFollowed ( branchStack [ 0 ] [ 1 ] , false )
? 'branchOkay'
: 'branchWarning' ) +
"'>" ;
if ( branchStack [ 0 ] [ 0 ] . end . line === lineNum ) {
newsrc +=
escapeInvalidXmlChars (
src . slice ( 0 , branchStack [ 0 ] [ 0 ] . end . column )
) + '</span>' ;
src = src . slice ( branchStack [ 0 ] [ 0 ] . end . column ) ;
branchStack . shift ( ) ;
if ( ! cols ) {
return { src : newsrc + escapeInvalidXmlChars ( src ) , cols : cols } ;
}
} else if ( ! cols ) {
return {
src : newsrc + escapeInvalidXmlChars ( src ) + '</span>' ,
cols : cols ,
} ;
} else {
postfix = '</span>' ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
} else if ( ! cols ) {
return { src : newsrc + escapeInvalidXmlChars ( src ) , cols : cols } ;
}
} else if ( ! cols ) {
return {
src : newsrc + escapeInvalidXmlChars ( src ) + '</span>' ,
cols : cols ,
} ;
} else {
postfix = '</span>' ;
}
}
var thisline = cols [ colsIndex ] ;
//consequent
var cons = thisline . consequent ;
if ( cons . start . line > lineNum ) {
branchStack . unshift ( [ thisline . alternate , thisline ] ) ;
branchStack . unshift ( [ cons , thisline ] ) ;
src = escapeInvalidXmlChars ( src ) ;
} else {
var style =
"<span class='" +
( isBranchFollowed ( thisline , true ) ? 'branchOkay' : 'branchWarning' ) +
"'>" ;
newsrc +=
escapeInvalidXmlChars ( src . slice ( 0 , cons . start . column - offset ) ) + style ;
if (
cols . length > colsIndex + 1 &&
cols [ colsIndex + 1 ] . consequent . start . line === lineNum &&
cols [ colsIndex + 1 ] . consequent . start . column - offset <
cols [ colsIndex ] . consequent . end . column - offset
) {
var res = branchReport (
colsIndex + 1 ,
src . slice ( cons . start . column - offset , cons . end . column - offset ) ,
cols ,
cons . start . column - offset ,
lineNum
) ;
newsrc += res . src ;
cols = res . cols ;
cols [ colsIndex + 1 ] = cols [ colsIndex + 2 ] ;
cols . length -- ;
} else {
newsrc += escapeInvalidXmlChars (
src . slice ( cons . start . column - offset , cons . end . column - offset )
) ;
}
newsrc += '</span>' ;
var alt = thisline . alternate ;
if ( alt . start . line > lineNum ) {
newsrc += escapeInvalidXmlChars ( src . slice ( cons . end . column - offset ) ) ;
branchStack . unshift ( [ alt , thisline ] ) ;
} else {
newsrc += escapeInvalidXmlChars (
src . slice ( cons . end . column - offset , alt . start . column - offset )
) ;
style =
"<span class='" +
( isBranchFollowed ( thisline , false ) ? 'branchOkay' : 'branchWarning' ) +
"'>" ;
newsrc += style ;
if (
cols . length > colsIndex + 1 &&
cols [ colsIndex + 1 ] . consequent . start . line === lineNum &&
cols [ colsIndex + 1 ] . consequent . start . column - offset <
cols [ colsIndex ] . alternate . end . column - offset
) {
var res2 = branchReport (
colsIndex + 1 ,
src . slice ( alt . start . column - offset , alt . end . column - offset ) ,
cols ,
alt . start . column - offset ,
lineNum
) ;
newsrc += res2 . src ;
cols = res2 . cols ;
cols [ colsIndex + 1 ] = cols [ colsIndex + 2 ] ;
2014-07-26 22:44:12 +00:00
cols . length -- ;
2018-04-27 21:25:04 +00:00
} else {
newsrc += escapeInvalidXmlChars (
src . slice ( alt . start . column - offset , alt . end . column - offset )
) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
newsrc += '</span>' ;
newsrc += escapeInvalidXmlChars ( src . slice ( alt . end . column - offset ) ) ;
src = newsrc ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
return { src : src + postfix , cols : cols } ;
}
var isUndefined = function ( item ) {
return typeof item !== 'undefined' ;
} ;
var files = coverage . files ;
var totals = {
totalSmts : 0 ,
numberOfFilesCovered : 0 ,
passedBranches : 0 ,
totalBranches : 0 ,
moduleTotalStatements : { } ,
moduleTotalCoveredStatements : { } ,
moduleTotalBranches : { } ,
moduleTotalCoveredBranches : { } ,
} ;
// check if a data-cover-modulepattern was provided for per-module coverage reporting
var modulePattern = _blanket . options ( 'modulePattern' ) ;
var modulePatternRegex = modulePattern ? new RegExp ( modulePattern ) : null ;
for ( var file in files ) {
if ( ! files . hasOwnProperty ( file ) ) {
continue ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
fileNumber ++ ;
var statsForFile = files [ file ] ,
totalSmts = 0 ,
numberOfFilesCovered = 0 ,
code = [ ] ,
i ;
var end = [ ] ;
for ( i = 0 ; i < statsForFile . source . length ; i += 1 ) {
var src = statsForFile . source [ i ] ;
if (
branchStack . length > 0 ||
typeof statsForFile . branchData !== 'undefined'
) {
if ( typeof statsForFile . branchData [ i + 1 ] !== 'undefined' ) {
var cols = statsForFile . branchData [ i + 1 ] . filter ( isUndefined ) ;
var colsIndex = 0 ;
src = branchReport ( colsIndex , src , cols , 0 , i + 1 ) . src ;
} else if ( branchStack . length ) {
src = branchReport ( 0 , src , null , 0 , i + 1 ) . src ;
} else {
src = escapeInvalidXmlChars ( src ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
} else {
src = escapeInvalidXmlChars ( src ) ;
}
var lineClass = '' ;
if ( statsForFile [ i + 1 ] ) {
numberOfFilesCovered += 1 ;
totalSmts += 1 ;
lineClass = 'hit' ;
} else {
if ( statsForFile [ i + 1 ] === 0 ) {
totalSmts ++ ;
lineClass = 'miss' ;
}
}
code [ i + 1 ] =
"<div class='" +
lineClass +
"'><span class=''>" +
( i + 1 ) +
'</span>' +
src +
'</div>' ;
}
totals . totalSmts += totalSmts ;
totals . numberOfFilesCovered += numberOfFilesCovered ;
var totalBranches = 0 ;
var passedBranches = 0 ;
if ( typeof statsForFile . branchData !== 'undefined' ) {
for ( var j = 0 ; j < statsForFile . branchData . length ; j ++ ) {
if ( typeof statsForFile . branchData [ j ] !== 'undefined' ) {
for ( var k = 0 ; k < statsForFile . branchData [ j ] . length ; k ++ ) {
if ( typeof statsForFile . branchData [ j ] [ k ] !== 'undefined' ) {
totalBranches ++ ;
if (
typeof statsForFile . branchData [ j ] [ k ] [ 0 ] !== 'undefined' &&
statsForFile . branchData [ j ] [ k ] [ 0 ] . length > 0 &&
typeof statsForFile . branchData [ j ] [ k ] [ 1 ] !== 'undefined' &&
statsForFile . branchData [ j ] [ k ] [ 1 ] . length > 0
) {
passedBranches ++ ;
2014-07-26 22:44:12 +00:00
}
}
}
}
2018-04-27 21:25:04 +00:00
}
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
totals . passedBranches += passedBranches ;
totals . totalBranches += totalBranches ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
// if "data-cover-modulepattern" was provided,
// track totals per module name as well as globally
if ( modulePatternRegex ) {
var moduleName = file . match ( modulePatternRegex ) [ 1 ] ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( ! totals . moduleTotalStatements . hasOwnProperty ( moduleName ) ) {
totals . moduleTotalStatements [ moduleName ] = 0 ;
totals . moduleTotalCoveredStatements [ moduleName ] = 0 ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
totals . moduleTotalStatements [ moduleName ] += totalSmts ;
totals . moduleTotalCoveredStatements [ moduleName ] += numberOfFilesCovered ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( ! totals . moduleTotalBranches . hasOwnProperty ( moduleName ) ) {
totals . moduleTotalBranches [ moduleName ] = 0 ;
totals . moduleTotalCoveredBranches [ moduleName ] = 0 ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
totals . moduleTotalBranches [ moduleName ] += totalBranches ;
totals . moduleTotalCoveredBranches [ moduleName ] += passedBranches ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
var result = percentage ( numberOfFilesCovered , totalSmts ) ;
var output = fileTemplate
. replace ( '{{file}}' , file )
. replace ( '{{percentage}}' , result )
. replace ( '{{numberCovered}}' , numberOfFilesCovered )
. replace ( /\{\{fileNumber\}\}/g , fileNumber )
. replace ( '{{totalSmts}}' , totalSmts )
. replace ( '{{totalBranches}}' , totalBranches )
. replace ( '{{passedBranches}}' , passedBranches )
. replace ( '{{source}}' , code . join ( ' ' ) ) ;
if ( result < successRate ) {
output = output . replace ( '{{statusclass}}' , 'bl-error' ) ;
} else {
output = output . replace ( '{{statusclass}}' , 'bl-success' ) ;
}
bodyContent += output ;
}
// create temporary function for use by the global totals reporter,
// as well as the per-module totals reporter
var createAggregateTotal = function (
numSt ,
numCov ,
numBranch ,
numCovBr ,
moduleName
) {
var totalPercent = percentage ( numCov , numSt ) ;
var statusClass = totalPercent < successRate ? 'bl-error' : 'bl-success' ;
var rowTitle = moduleName
? 'Total for module: ' + moduleName
: 'Global total' ;
var totalsOutput = grandTotalTemplate
. replace ( '{{rowTitle}}' , rowTitle )
. replace ( '{{percentage}}' , totalPercent )
. replace ( '{{numberCovered}}' , numCov )
. replace ( '{{totalSmts}}' , numSt )
. replace ( '{{passedBranches}}' , numCovBr )
. replace ( '{{totalBranches}}' , numBranch )
. replace ( '{{statusclass}}' , statusClass ) ;
bodyContent += totalsOutput ;
} ;
// if "data-cover-modulepattern" was provided,
// output the per-module totals alongside the global totals
if ( modulePatternRegex ) {
for ( var thisModuleName in totals . moduleTotalStatements ) {
if ( totals . moduleTotalStatements . hasOwnProperty ( thisModuleName ) ) {
var moduleTotalSt = totals . moduleTotalStatements [ thisModuleName ] ;
var moduleTotalCovSt =
totals . moduleTotalCoveredStatements [ thisModuleName ] ;
var moduleTotalBr = totals . moduleTotalBranches [ thisModuleName ] ;
var moduleTotalCovBr =
totals . moduleTotalCoveredBranches [ thisModuleName ] ;
createAggregateTotal (
moduleTotalSt ,
moduleTotalCovSt ,
moduleTotalBr ,
moduleTotalCovBr ,
thisModuleName
) ;
}
}
}
createAggregateTotal (
totals . totalSmts ,
totals . numberOfFilesCovered ,
totals . totalBranches ,
totals . passedBranches ,
null
) ;
bodyContent += '</div>' ; //closing main
appendTag ( 'style' , head , cssSytle ) ;
//appendStyle(body, headerContent);
if ( document . getElementById ( 'blanket-main' ) ) {
document . getElementById ( 'blanket-main' ) . innerHTML = bodyContent . slice (
23 ,
- 6
) ;
} else {
appendTag ( 'div' , body , bodyContent ) ;
}
fileNumber = 0 ;
for ( var file in files ) {
if ( ! files . hasOwnProperty ( file ) ) {
continue ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
fileNumber ++ ;
var _ = ( function ( ) {
var localFN = fileNumber ;
$ ( '#blanket-link-file-' + fileNumber ) . click ( function ( ) {
blanket _toggleSource ( 'file-' + localFN ) ;
} ) ;
} ) ( ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
//appendHtml(body, '</div>');
} ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
( function ( ) {
var newOptions = { } ;
//http://stackoverflow.com/a/2954896
var toArray = Array . prototype . slice ;
var scripts = toArray . call ( document . scripts ) ;
toArray . call ( scripts [ scripts . length - 1 ] . attributes ) . forEach ( function ( es ) {
if ( es . nodeName === 'data-cover-only' ) {
newOptions . filter = es . nodeValue ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
if ( es . nodeName === 'data-cover-never' ) {
newOptions . antifilter = es . nodeValue ;
}
if ( es . nodeName === 'data-cover-reporter' ) {
newOptions . reporter = es . nodeValue ;
}
if ( es . nodeName === 'data-cover-adapter' ) {
newOptions . adapter = es . nodeValue ;
}
if ( es . nodeName === 'data-cover-loader' ) {
newOptions . loader = es . nodeValue ;
}
if ( es . nodeName === 'data-cover-timeout' ) {
newOptions . timeout = es . nodeValue ;
}
if ( es . nodeName === 'data-cover-modulepattern' ) {
newOptions . modulePattern = es . nodeValue ;
}
if ( es . nodeName === 'data-cover-reporter-options' ) {
try {
newOptions . reporter _options = JSON . parse ( es . nodeValue ) ;
} catch ( e ) {
if ( blanket . options ( 'debug' ) ) {
throw new Error (
'Invalid reporter options. Must be a valid stringified JSON object.'
) ;
2014-07-26 23:14:14 +00:00
}
2018-04-27 21:25:04 +00:00
}
}
if ( es . nodeName === 'data-cover-testReadyCallback' ) {
newOptions . testReadyCallback = es . nodeValue ;
}
if ( es . nodeName === 'data-cover-customVariable' ) {
newOptions . customVariable = es . nodeValue ;
}
if ( es . nodeName === 'data-cover-flags' ) {
var flags = ' ' + es . nodeValue + ' ' ;
if ( flags . indexOf ( ' ignoreError ' ) > - 1 ) {
newOptions . ignoreScriptError = true ;
}
if ( flags . indexOf ( ' autoStart ' ) > - 1 ) {
newOptions . autoStart = true ;
}
if ( flags . indexOf ( ' ignoreCors ' ) > - 1 ) {
newOptions . ignoreCors = true ;
}
if ( flags . indexOf ( ' branchTracking ' ) > - 1 ) {
newOptions . branchTracking = true ;
}
if ( flags . indexOf ( ' sourceURL ' ) > - 1 ) {
newOptions . sourceURL = true ;
}
if ( flags . indexOf ( ' debug ' ) > - 1 ) {
newOptions . debug = true ;
}
if ( flags . indexOf ( ' engineOnly ' ) > - 1 ) {
newOptions . engineOnly = true ;
}
if ( flags . indexOf ( ' commonJS ' ) > - 1 ) {
newOptions . commonJS = true ;
}
if ( flags . indexOf ( ' instrumentCache ' ) > - 1 ) {
newOptions . instrumentCache = true ;
}
}
} ) ;
blanket . options ( newOptions ) ;
2014-07-26 23:14:14 +00:00
2018-04-27 21:25:04 +00:00
if ( typeof requirejs !== 'undefined' ) {
blanket . options ( 'existingRequireJS' , true ) ;
}
/* setup requirejs loader, if needed */
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
if ( blanket . options ( 'commonJS' ) ) {
blanket . _commonjs = { } ;
}
2014-07-26 22:44:12 +00:00
} ) ( ) ;
2018-04-27 21:25:04 +00:00
( function ( _blanket ) {
_blanket . extend ( {
2014-07-26 22:44:12 +00:00
utils : {
2018-04-27 21:25:04 +00:00
normalizeBackslashes : function ( str ) {
return str . replace ( /\\/g , '/' ) ;
} ,
matchPatternAttribute : function ( filename , pattern ) {
if ( typeof pattern === 'string' ) {
if ( pattern . indexOf ( '[' ) === 0 ) {
//treat as array
var pattenArr = pattern . slice ( 1 , pattern . length - 1 ) . split ( ',' ) ;
return pattenArr . some ( function ( elem ) {
return _blanket . utils . matchPatternAttribute (
filename ,
_blanket . utils . normalizeBackslashes ( elem . slice ( 1 , - 1 ) )
) ;
//return filename.indexOf(_blanket.utils.normalizeBackslashes(elem.slice(1,-1))) > -1;
} ) ;
} else if ( pattern . indexOf ( '//' ) === 0 ) {
var ex = pattern . slice ( 2 , pattern . lastIndexOf ( '/' ) ) ;
var mods = pattern . slice ( pattern . lastIndexOf ( '/' ) + 1 ) ;
var regex = new RegExp ( ex , mods ) ;
return regex . test ( filename ) ;
} else if ( pattern . indexOf ( '#' ) === 0 ) {
return window [ pattern . slice ( 1 ) ] . call ( window , filename ) ;
} else {
return (
filename . indexOf ( _blanket . utils . normalizeBackslashes ( pattern ) ) >
- 1
) ;
}
} else if ( pattern instanceof Array ) {
return pattern . some ( function ( elem ) {
return _blanket . utils . matchPatternAttribute ( filename , elem ) ;
} ) ;
} else if ( pattern instanceof RegExp ) {
return pattern . test ( filename ) ;
} else if ( typeof pattern === 'function' ) {
return pattern . call ( window , filename ) ;
}
} ,
blanketEval : function ( data ) {
_blanket . _addScript ( data ) ;
} ,
collectPageScripts : function ( ) {
var toArray = Array . prototype . slice ;
var scripts = toArray . call ( document . scripts ) ;
var selectedScripts = [ ] ,
scriptNames = [ ] ;
var filter = _blanket . options ( 'filter' ) ;
if ( filter != null ) {
//global filter in place, data-cover-only
var antimatch = _blanket . options ( 'antifilter' ) ;
selectedScripts = toArray . call ( document . scripts ) . filter ( function ( s ) {
return (
toArray . call ( s . attributes ) . filter ( function ( sn ) {
return (
sn . nodeName === 'src' &&
_blanket . utils . matchPatternAttribute ( sn . nodeValue , filter ) &&
( typeof antimatch === 'undefined' ||
! _blanket . utils . matchPatternAttribute (
sn . nodeValue ,
antimatch
) )
2014-07-26 22:44:12 +00:00
) ;
2018-04-27 21:25:04 +00:00
} ) . length === 1
2014-07-26 22:44:12 +00:00
) ;
2018-04-27 21:25:04 +00:00
} ) ;
} else {
selectedScripts = toArray . call (
document . querySelectorAll ( 'script[data-cover]' )
) ;
}
scriptNames = selectedScripts . map ( function ( s ) {
return _blanket . utils . qualifyURL (
toArray . call ( s . attributes ) . filter ( function ( sn ) {
return sn . nodeName === 'src' ;
} ) [ 0 ] . nodeValue
) ;
} ) ;
if ( ! filter ) {
_blanket . options ( 'filter' , "['" + scriptNames . join ( "','" ) + "']" ) ;
}
return scriptNames ;
} ,
loadAll : function ( nextScript , cb , preprocessor ) {
/ * *
* load dependencies
* @ param { nextScript } factory for priority level
* @ param { cb } the done callback
* /
var currScript = nextScript ( ) ;
var isLoaded = _blanket . utils . scriptIsLoaded (
currScript ,
_blanket . utils . ifOrdered ,
nextScript ,
cb
) ;
if (
! (
_blanket . utils . cache [ currScript ] &&
_blanket . utils . cache [ currScript ] . loaded
)
) {
var attach = function ( ) {
if ( _blanket . options ( 'debug' ) ) {
console . log (
'BLANKET-Mark script:' +
currScript +
', as loaded and move to next script.'
) ;
}
isLoaded ( ) ;
} ;
var whenDone = function ( result ) {
if ( _blanket . options ( 'debug' ) ) {
console . log ( 'BLANKET-File loading finished' ) ;
}
if ( typeof result !== 'undefined' ) {
if ( _blanket . options ( 'debug' ) ) {
console . log ( 'BLANKET-Add file to DOM.' ) ;
}
_blanket . _addScript ( result ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
attach ( ) ;
} ;
_blanket . utils . attachScript (
{
url : currScript ,
} ,
function ( content ) {
_blanket . utils . processFile (
content ,
currScript ,
whenDone ,
whenDone
) ;
}
) ;
} else {
isLoaded ( ) ;
}
} ,
attachScript : function ( options , cb ) {
var timeout = _blanket . options ( 'timeout' ) || 3000 ;
setTimeout ( function ( ) {
if ( ! _blanket . utils . cache [ options . url ] . loaded ) {
throw new Error ( 'error loading source script' ) ;
}
} , timeout ) ;
_blanket . utils . getFile ( options . url , cb , function ( ) {
throw new Error ( 'error loading source script' ) ;
} ) ;
} ,
ifOrdered : function ( nextScript , cb ) {
/ * *
* ordered loading callback
* @ param { nextScript } factory for priority level
* @ param { cb } the done callback
* /
var currScript = nextScript ( true ) ;
if ( currScript ) {
_blanket . utils . loadAll ( nextScript , cb ) ;
} else {
cb ( new Error ( 'Error in loading chain.' ) ) ;
}
} ,
scriptIsLoaded : function ( url , orderedCb , nextScript , cb ) {
/ * *
* returns a callback that checks a loading list to see if a script is loaded .
* @ param { orderedCb } callback if ordered loading is being done
* @ param { nextScript } factory for next priority level
* @ param { cb } the done callback
* /
if ( _blanket . options ( 'debug' ) ) {
console . log ( 'BLANKET-Returning function' ) ;
}
return function ( ) {
if ( _blanket . options ( 'debug' ) ) {
console . log ( 'BLANKET-Marking file as loaded: ' + url ) ;
}
_blanket . utils . cache [ url ] . loaded = true ;
if ( _blanket . utils . allLoaded ( ) ) {
if ( _blanket . options ( 'debug' ) ) {
console . log ( 'BLANKET-All files loaded' ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
cb ( ) ;
} else if ( orderedCb ) {
//if it's ordered we need to
//traverse down to the next
//priority level
if ( _blanket . options ( 'debug' ) ) {
console . log ( 'BLANKET-Load next file.' ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
orderedCb ( nextScript , cb ) ;
}
} ;
} ,
cache : { } ,
allLoaded : function ( ) {
/ * *
* check if depdencies are loaded in cache
* /
var cached = Object . keys ( _blanket . utils . cache ) ;
for ( var i = 0 ; i < cached . length ; i ++ ) {
if ( ! _blanket . utils . cache [ cached [ i ] ] . loaded ) {
return false ;
}
}
return true ;
} ,
processFile : function ( content , url , cb , oldCb ) {
var match = _blanket . options ( 'filter' ) ;
//we check the never matches first
var antimatch = _blanket . options ( 'antifilter' ) ;
if (
typeof antimatch !== 'undefined' &&
_blanket . utils . matchPatternAttribute ( url , antimatch )
) {
oldCb ( content ) ;
if ( _blanket . options ( 'debug' ) ) {
console . log ( 'BLANKET-File will never be instrumented:' + url ) ;
}
_blanket . requiringFile ( url , true ) ;
} else if ( _blanket . utils . matchPatternAttribute ( url , match ) ) {
if ( _blanket . options ( 'debug' ) ) {
console . log ( 'BLANKET-Attempting instrument of:' + url ) ;
}
_blanket . instrument (
{
inputFile : content ,
inputFileName : url ,
} ,
function ( instrumented ) {
try {
if ( _blanket . options ( 'debug' ) ) {
console . log (
2020-11-03 00:46:49 +00:00
'BLANKET-instrument of:' + url + ' was successful.'
2018-04-27 21:25:04 +00:00
) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
_blanket . utils . blanketEval ( instrumented ) ;
cb ( ) ;
_blanket . requiringFile ( url , true ) ;
} catch ( err ) {
if ( _blanket . options ( 'ignoreScriptError' ) ) {
//we can continue like normal if
//we're ignoring script errors,
//but otherwise we don't want
//to completeLoad or the error might be
//missed.
if ( _blanket . options ( 'debug' ) ) {
console . log (
'BLANKET-There was an error loading the file:' + url
) ;
}
cb ( content ) ;
_blanket . requiringFile ( url , true ) ;
} else {
throw new Error ( 'Error parsing instrumented code: ' + err ) ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
) ;
} else {
if ( _blanket . options ( 'debug' ) ) {
console . log (
'BLANKET-Loading (without instrumenting) the file:' + url
) ;
}
oldCb ( content ) ;
_blanket . requiringFile ( url , true ) ;
}
} ,
cacheXhrConstructor : function ( ) {
var Constructor , createXhr , i , progId ;
if ( typeof XMLHttpRequest !== 'undefined' ) {
Constructor = XMLHttpRequest ;
this . createXhr = function ( ) {
return new Constructor ( ) ;
} ;
} else if ( typeof ActiveXObject !== 'undefined' ) {
Constructor = ActiveXObject ;
for ( i = 0 ; i < 3 ; i += 1 ) {
progId = progIds [ i ] ;
try {
new ActiveXObject ( progId ) ;
break ;
} catch ( e ) { }
}
this . createXhr = function ( ) {
return new Constructor ( progId ) ;
} ;
}
} ,
craeteXhr : function ( ) {
throw new Error (
'cacheXhrConstructor is supposed to overwrite this function.'
) ;
} ,
getFile : function ( url , callback , errback , onXhr ) {
var foundInSession = false ;
if ( _blanket . blanketSession ) {
var files = Object . keys ( _blanket . blanketSession ) ;
for ( var i = 0 ; i < files . length ; i ++ ) {
var key = files [ i ] ;
if ( url . indexOf ( key ) > - 1 ) {
callback ( _blanket . blanketSession [ key ] ) ;
foundInSession = true ;
return ;
}
}
}
if ( ! foundInSession ) {
var xhr = _blanket . utils . createXhr ( ) ;
xhr . open ( 'GET' , url , true ) ;
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
//Allow overrides specified in config
if ( onXhr ) {
onXhr ( xhr , url ) ;
}
2014-07-26 22:44:12 +00:00
2018-04-27 21:25:04 +00:00
xhr . onreadystatechange = function ( evt ) {
var status , err ;
//Do not explicitly handle errors, those should be
//visible via console output in the browser.
if ( xhr . readyState === 4 ) {
status = xhr . status ;
if (
status > 399 &&
status <
600 / * ||
2014-07-26 22:44:12 +00:00
( status === 0 &&
navigator . userAgent . toLowerCase ( ) . indexOf ( 'firefox' ) > - 1 )
2018-04-27 21:25:04 +00:00
* /
) {
//An http 4xx or 5xx error. Signal an error.
err = new Error ( url + ' HTTP status: ' + status ) ;
err . xhr = xhr ;
errback ( err ) ;
} else {
callback ( xhr . responseText ) ;
}
}
} ;
try {
xhr . send ( null ) ;
} catch ( e ) {
if (
e . code &&
( e . code === 101 || e . code === 1012 ) &&
_blanket . options ( 'ignoreCors' ) === false
) {
//running locally and getting error from browser
_blanket . showManualLoader ( ) ;
} else {
throw e ;
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
}
2014-07-26 22:44:12 +00:00
}
2018-04-27 21:25:04 +00:00
} ,
} ,
} ) ;
( function ( ) {
var require = blanket . options ( 'commonJS' )
? blanket . _commonjs . require
: window . require ;
var requirejs = blanket . options ( 'commonJS' )
? blanket . _commonjs . requirejs
: window . requirejs ;
if (
! _blanket . options ( 'engineOnly' ) &&
_blanket . options ( 'existingRequireJS' )
) {
_blanket . utils . oldloader = requirejs . load ;
requirejs . load = function ( context , moduleName , url ) {
_blanket . requiringFile ( url ) ;
_blanket . utils . getFile (
url ,
function ( content ) {
_blanket . utils . processFile (
content ,
url ,
function newLoader ( ) {
context . completeLoad ( moduleName ) ;
} ,
function oldLoader ( ) {
_blanket . utils . oldloader ( context , moduleName , url ) ;
}
) ;
} ,
function ( err ) {
_blanket . requiringFile ( ) ;
throw err ;
}
) ;
} ;
2014-07-26 22:44:12 +00:00
}
// Save the XHR constructor, just in case frameworks like Sinon would sandbox it.
_blanket . utils . cacheXhrConstructor ( ) ;
2018-04-27 21:25:04 +00:00
} ) ( ) ;
2014-07-26 22:44:12 +00:00
} ) ( blanket ) ;
2018-04-27 21:25:04 +00:00
( function ( ) {
if ( ! mocha ) {
throw new Exception ( 'mocha library does not exist in global namespace!' ) ;
}
/ *
2014-07-26 22:44:12 +00:00
* Mocha Events :
*
* - ` start ` execution started
* - ` end ` execution complete
* - ` suite ` ( suite ) test suite execution started
* - ` suite end ` ( suite ) all tests ( and sub - suites ) have finished
* - ` test ` ( test ) test execution started
* - ` test end ` ( test ) test completed
* - ` hook ` ( hook ) hook execution started
* - ` hook end ` ( hook ) hook complete
* - ` pass ` ( test ) test passed
* - ` fail ` ( test , err ) test failed
*
2018-04-27 21:25:04 +00:00
* /
var OriginalReporter = mocha . _reporter ;
var BlanketReporter = function ( runner ) {
runner . on ( 'start' , function ( ) {
blanket . setupCoverage ( ) ;
} ) ;
runner . on ( 'end' , function ( ) {
blanket . onTestsDone ( ) ;
} ) ;
runner . on ( 'suite' , function ( ) {
blanket . onModuleStart ( ) ;
} ) ;
runner . on ( 'test' , function ( ) {
blanket . onTestStart ( ) ;
} ) ;
runner . on ( 'test end' , function ( test ) {
blanket . onTestDone ( test . parent . tests . length , test . state === 'passed' ) ;
} ) ;
// NOTE: this is an instance of BlanketReporter
new OriginalReporter ( runner ) ;
} ;
BlanketReporter . prototype = OriginalReporter . prototype ;
mocha . reporter ( BlanketReporter ) ;
var oldRun = mocha . run ,
oldCallback = null ;
mocha . run = function ( finishCallback ) {
oldCallback = finishCallback ;
console . log ( 'waiting for blanket...' ) ;
} ;
blanket . beforeStartTestRunner ( {
callback : function ( ) {
if ( ! blanket . options ( 'existingRequireJS' ) ) {
oldRun ( oldCallback ) ;
}
mocha . run = oldRun ;
} ,
} ) ;
} ) ( ) ;