1 /*global core*/ 2 // jslint.js 3 // 2014-07-08 4 5 // Copyright (c) 2002 Douglas Crockford (www.JSLint.com) 6 7 // Permission is hereby granted, free of charge, to any person obtaining a copy 8 // of this software and associated documentation files (the "Software"), to deal 9 // in the Software without restriction, including without limitation the rights 10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 // copies of the Software, and to permit persons to whom the Software is 12 // furnished to do so, subject to the following conditions: 13 14 // The above copyright notice and this permission notice shall be included in 15 // all copies or substantial portions of the Software. 16 17 // The Software shall be used for Good, not Evil. 18 19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 // SOFTWARE. 26 27 // WARNING: JSLint will hurt your feelings. 28 29 // JSLINT is a global function. It takes two parameters. 30 31 // var myResult = JSLINT(source, option); 32 33 // The first parameter is either a string or an array of strings. If it is a 34 // string, it will be split on '\n' or '\r'. If it is an array of strings, it 35 // is assumed that each string represents one line. The source can be a 36 // JavaScript text or a JSON text. 37 38 // The second parameter is an optional object of options that control the 39 // operation of JSLINT. Most of the options are booleans: They are all 40 // optional and have a default value of false. One of the options, predef, 41 // can be an array of names, which will be used to declare global variables, 42 // or an object whose keys are used as global names, with a boolean value 43 // that determines if they are assignable. 44 45 // If it checks out, JSLINT returns true. Otherwise, it returns false. 46 47 // If false, you can inspect JSLINT.errors to find out the problems. 48 // JSLINT.errors is an array of objects containing these properties: 49 50 // { 51 // line : The line (relative to 0) at which the lint was found 52 // character : The character (relative to 0) at which the lint was found 53 // reason : The problem 54 // evidence : The text line in which the problem occurred 55 // raw : The raw message before the details were inserted 56 // a : The first detail 57 // b : The second detail 58 // c : The third detail 59 // d : The fourth detail 60 // } 61 62 // If a stopping error was found, a null will be the last element of the 63 // JSLINT.errors array. A stopping error means that JSLint was not confident 64 // enough to continue. It does not necessarily mean that the error was 65 // especially heinous. 66 67 // You can request a data structure that contains JSLint's results. 68 69 // var myData = JSLINT.data(); 70 71 // It returns a structure with this form: 72 73 // { 74 // errors: [ 75 // { 76 // line: NUMBER, 77 // character: NUMBER, 78 // reason: STRING, 79 // evidence: STRING 80 // } 81 // ], 82 // functions: [ 83 // { 84 // name: STRING, 85 // line: NUMBER, 86 // level: NUMBER, 87 // parameter: [ 88 // STRING 89 // ], 90 // var: [ 91 // STRING 92 // ], 93 // exception: [ 94 // STRING 95 // ], 96 // closure: [ 97 // STRING 98 // ], 99 // outer: [ 100 // STRING 101 // ], 102 // global: [ 103 // STRING 104 // ], 105 // label: [ 106 // STRING 107 // ] 108 // } 109 // ], 110 // global: [ 111 // STRING 112 // ], 113 // member: { 114 // STRING: NUMBER 115 // }, 116 // json: BOOLEAN 117 // } 118 119 // You can request a Function Report, which shows all of the functions 120 // and the parameters and vars that they use. This can be used to find 121 // implied global variables and other problems. The report is in HTML and 122 // can be inserted into an HTML <body>. It should be given the result of the 123 // JSLINT.data function. 124 125 // var myReport = JSLINT.report(data); 126 127 // You can request an HTML error report. 128 129 // var myErrorReport = JSLINT.error_report(data); 130 131 // You can obtain an object containing all of the properties found in the 132 // file. JSLINT.property contains an object containing a key for each 133 // property used in the program, the value being the number of times that 134 // property name was used in the file. 135 136 // You can request a properties report, which produces a list of the program's 137 // properties in the form of a /*properties*/ declaration. 138 139 // var myPropertyReport = JSLINT.properties_report(JSLINT.property); 140 141 // You can obtain the parse tree that JSLint constructed while parsing. The 142 // latest tree is kept in JSLINT.tree. A nice stringification can be produced 143 // with 144 145 // JSON.stringify(JSLINT.tree, [ 146 // 'string', 'arity', 'name', 'first', 147 // 'second', 'third', 'block', 'else' 148 // ], 4)); 149 150 // You can request a context coloring table. It contains information that can be 151 // applied to the file that was analyzed. Context coloring colors functions 152 // based on their nesting level, and variables on the color of the functions 153 // in which they are defined. 154 155 // var myColorization = JSLINT.color(data); 156 157 // It returns an array containing objects of this form: 158 159 // { 160 // from: COLUMN, 161 // thru: COLUMN, 162 // line: ROW, 163 // level: 0 or higher 164 // } 165 166 // JSLint provides three inline directives. They look like slashstar comments, 167 // and allow for setting options, declaring global variables, and establishing a 168 // set of allowed property names. 169 170 // These directives respect function scope. 171 172 // The jslint directive is a special comment that can set one or more options. 173 // For example: 174 175 /*jslint 176 evil: true, nomen: true, regexp: true, todo: true 177 */ 178 179 // The current option set is 180 181 // ass true, if assignment expressions should be allowed 182 // bitwise true, if bitwise operators should be allowed 183 // browser true, if the standard browser globals should be predefined 184 // closure true, if Google Closure idioms should be tolerated 185 // continue true, if the continuation statement should be tolerated 186 // debug true, if debugger statements should be allowed 187 // defined true, if already defined variables are allowed 188 // devel true, if logging should be allowed (console, alert, etc.) 189 // emptyblock true, if empty blocks should be allowed 190 // eqeq true, if == should be allowed 191 // evil true, if eval should be allowed 192 // forin true, if for in statements need not filter 193 // indent the indentation factor 194 // maxerr the maximum number of errors to allow 195 // maxlen the maximum length of a source line 196 // newcap true, if constructor names capitalization is ignored 197 // node true, if Node.js globals should be predefined 198 // nomen true, if names may have dangling _ 199 // passfail true, if the scan should stop on first error 200 // plusplus true, if increment/decrement should be allowed 201 // properties true, if all property names must be declared with /*properties*/ 202 // regexp true, if the . should be allowed in regexp literals 203 // rhino true, if the Rhino environment globals should be predefined 204 // unparam true, if unused parameters should be tolerated 205 // sloppy true, if the 'use strict'; pragma is optional 206 // stupid true, if really stupid practices are tolerated 207 // sub true, if all forms of subscript notation are tolerated 208 // todo true, if TODO comments are tolerated 209 // unvar true, if unused variables should be tolerated 210 // vars true, if multiple var statements per function should be allowed 211 // white true, if sloppy whitespace is tolerated 212 213 // The properties directive declares an exclusive list of property names. 214 // Any properties named in the program that are not in the list will 215 // produce a warning. 216 217 // For example: 218 219 /*properties 220 defined, emptyblock, unvar, JSLINT, JSLint, core, 221 '\b', '\t', '\n', '\f', '\r', '!', '!=', '!==', '"', '%', '\'', '(begin)', 222 '(error)', '*', '+', '-', '/', '<', '<=', '==', '===', '>', '>=', '\\', a, 223 a_label, a_scope, already_defined, and, apply, arguments, arity, ass, 224 assign, assignment_expression, assignment_function_expression, at, avoid_a, 225 b, bad_assignment, bad_constructor, bad_in_a, bad_invocation, bad_new, 226 bad_number, bad_operand, bad_wrap, bitwise, block, break, breakage, browser, 227 c, call, charAt, charCodeAt, character, closure, code, color, combine_var, 228 comments, conditional_assignment, confusing_a, confusing_regexp, 229 constructor_name_a, continue, control_a, couch, create, d, dangling_a, data, 230 dead, debug, deleted, devel, disrupt, duplicate_a, edge, edition, elif, 231 else, empty_block, empty_case, empty_class, entityify, eqeq, error_report, 232 errors, evidence, evil, exception, exec, expected_a_at_b_c, expected_a_b, 233 expected_a_b_from_c_d, expected_id_a, expected_identifier_a, 234 expected_identifier_a_reserved, expected_number_a, expected_operator_a, 235 expected_positive_a, expected_small_a, expected_space_a_b, 236 expected_string_a, f, first, flag, floor, forEach, for_if, forin, from, 237 fromCharCode, fud, function, function_block, function_eval, function_loop, 238 function_statement, function_strict, functions, global, hasOwnProperty, id, 239 identifier, identifier_function, immed, implied_evil, indent, indexOf, 240 infix_in, init, insecure_a, isAlpha, isArray, isDigit, isNaN, join, jslint, 241 json, keys, kind, label, labeled, lbp, leading_decimal_a, led, left, length, 242 level, line, loopage, master, match, maxerr, maxlen, message, missing_a, 243 missing_a_after_b, missing_property, missing_space_a_b, missing_use_strict, 244 mode, move_invocation, move_var, n, name, name_function, nested_comment, 245 newcap, node, nomen, not, not_a_constructor, not_a_defined, not_a_function, 246 not_a_label, not_a_scope, not_greater, nud, number, octal_a, open, outer, 247 parameter, parameter_a_get_b, parameter_arguments_a, parameter_set_a, 248 params, paren, passfail, plusplus, pop, postscript, predef, properties, 249 properties_report, property, prototype, push, quote, r, radix, raw, 250 read_only, reason, redefinition_a_b, regexp, relation, replace, report, 251 reserved, reserved_a, rhino, right, scanned_a_b, scope, search, second, 252 shift, slash_equal, slice, sloppy, sort, split, statement, statement_block, 253 stop, stopping, strange_loop, strict, string, stupid, sub, subscript, 254 substr, supplant, sync_a, t, tag_a_in_b, test, third, thru, toString, todo, 255 todo_comment, token, tokens, too_long, too_many, trailing_decimal_a, tree, 256 unclosed, unclosed_comment, unclosed_regexp, unescaped_a, unexpected_a, 257 unexpected_char_a, unexpected_comment, unexpected_label_a, 258 unexpected_property_a, unexpected_space_a_b, unexpected_typeof_a, 259 uninitialized_a, unnecessary_else, unnecessary_initialize, unnecessary_use, 260 unparam, unreachable_a_b, unsafe, unused_a, url, use_array, use_braces, 261 use_nested_if, use_object, use_or, use_param, use_spaces, used, 262 used_before_a, var, var_a_not, var_loop, vars, varstatement, warn, warning, 263 was, weird_assignment, weird_condition, weird_new, weird_program, 264 weird_relation, weird_ternary, white, wrap, wrap_immediate, wrap_regexp, 265 write_is_wrong, writeable 266 */ 267 268 // The global directive is used to declare global variables that can 269 // be accessed by the program. If a declaration is true, then the variable 270 // is writeable. Otherwise, it is read-only. 271 272 // We build the application inside a function so that we produce only a single 273 // global variable. That function will be invoked immediately, and its return 274 // value is the JSLINT function itself. That function is also an object that 275 // can contain data and other functions. 276 277 var JSLINT = (function () { 278 'use strict'; 279 280 function array_to_object(array, value) { 281 282 // Make an object from an array of keys and a common value. 283 284 var i, length = array.length, object = Object.create(null); 285 for (i = 0; i < length; i += 1) { 286 object[array[i]] = value; 287 } 288 return object; 289 } 290 291 292 var allowed_option = { 293 ass : true, 294 bitwise : true, 295 browser : true, 296 closure : true, 297 continue : true, 298 couch : true, 299 debug : true, 300 defined : true, 301 devel : true, 302 emptyblock: true, 303 eqeq : true, 304 evil : true, 305 forin : true, 306 indent : 10, 307 maxerr : 1000, 308 maxlen : 256, 309 newcap : true, 310 node : true, 311 nomen : true, 312 passfail : true, 313 plusplus : true, 314 properties: true, 315 regexp : true, 316 rhino : true, 317 unparam : true, 318 sloppy : true, 319 stupid : true, 320 sub : true, 321 todo : true, 322 unvar : true, 323 vars : true, 324 white : true 325 }, 326 anonname, // The guessed name for anonymous functions. 327 328 // These are operators that should not be used with the ! operator. 329 330 bang = { 331 '<' : true, 332 '<=' : true, 333 '==' : true, 334 '===': true, 335 '!==': true, 336 '!=' : true, 337 '>' : true, 338 '>=' : true, 339 '+' : true, 340 '-' : true, 341 '*' : true, 342 '/' : true, 343 '%' : true 344 }, 345 begin, // The root token 346 block_var, // vars defined in the current block 347 348 // browser contains a set of global names that are commonly provided by a 349 // web browser environment. 350 351 browser = array_to_object([ 352 'clearInterval', 'clearTimeout', 'document', 'event', 'FormData', 353 'frames', 'history', 'Image', 'localStorage', 'location', 'name', 354 'navigator', 'Option', 'parent', 'screen', 'sessionStorage', 355 'setInterval', 'setTimeout', 'Storage', 'window', 'XMLHttpRequest' 356 ], false), 357 358 // bundle contains the text messages. 359 360 bundle = { 361 a_label: "'{a}' is a statement label.", 362 a_scope: "'{a}' used out of scope.", 363 already_defined: "'{a}' is already defined.", 364 and: "The '&&' subexpression should be wrapped in parens.", 365 assignment_expression: "Unexpected assignment expression.", 366 assignment_function_expression: "Expected an assignment or " + 367 "function call and instead saw an expression.", 368 avoid_a: "Avoid '{a}'.", 369 bad_assignment: "Bad assignment.", 370 bad_constructor: "Bad constructor.", 371 bad_in_a: "Bad for in variable '{a}'.", 372 bad_invocation: "Bad invocation.", 373 bad_new: "Do not use 'new' for side effects.", 374 bad_number: "Bad number '{a}'.", 375 bad_operand: "Bad operand.", 376 bad_wrap: "Do not wrap function literals in parens unless they " + 377 "are to be immediately invoked.", 378 combine_var: "Combine this with the previous 'var' statement.", 379 conditional_assignment: "Expected a conditional expression and " + 380 "instead saw an assignment.", 381 confusing_a: "Confusing use of '{a}'.", 382 confusing_regexp: "Confusing regular expression.", 383 constructor_name_a: "A constructor name '{a}' should start with " + 384 "an uppercase letter.", 385 control_a: "Unexpected control character '{a}'.", 386 dangling_a: "Unexpected dangling '_' in '{a}'.", 387 deleted: "Only properties should be deleted.", 388 duplicate_a: "Duplicate '{a}'.", 389 empty_block: "Empty block.", 390 empty_case: "Empty case.", 391 empty_class: "Empty class.", 392 evil: "eval is evil.", 393 expected_a_b: "Expected '{a}' and instead saw '{b}'.", 394 expected_a_b_from_c_d: "Expected '{a}' to match '{b}' from line " + 395 "{c} and instead saw '{d}'.", 396 expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.", 397 expected_id_a: "Expected an id, and instead saw #{a}.", 398 expected_identifier_a: "Expected an identifier and instead saw '{a}'.", 399 expected_identifier_a_reserved: "Expected an identifier and " + 400 "instead saw '{a}' (a reserved word).", 401 expected_number_a: "Expected a number and instead saw '{a}'.", 402 expected_operator_a: "Expected an operator and instead saw '{a}'.", 403 expected_positive_a: "Expected a positive number and instead saw '{a}'", 404 expected_small_a: "Expected a small positive integer and instead saw '{a}'", 405 expected_space_a_b: "Expected exactly one space between '{a}' and '{b}'.", 406 expected_string_a: "Expected a string and instead saw '{a}'.", 407 for_if: "The body of a for in should be wrapped in an if " + 408 "statement to filter unwanted properties from the prototype.", 409 function_block: "Function statements should not be placed in blocks." + 410 "Use a function expression or move the statement to the top of " + 411 "the outer function.", 412 function_eval: "The Function constructor is eval.", 413 function_loop: "Don't make functions within a loop.", 414 function_statement: "Function statements are not invocable. " + 415 "Wrap the whole function invocation in parens.", 416 function_strict: "Use the function form of 'use strict'.", 417 identifier_function: "Expected an identifier in an assignment " + 418 "and instead saw a function invocation.", 419 implied_evil: "Implied eval is evil. Pass a function instead of a string.", 420 infix_in: "Unexpected 'in'. Compare with undefined, or use the " + 421 "hasOwnProperty method instead.", 422 insecure_a: "Insecure '{a}'.", 423 isNaN: "Use the isNaN function to compare with NaN.", 424 leading_decimal_a: "A leading decimal point can be confused with a dot: '.{a}'.", 425 missing_a: "Missing '{a}'.", 426 missing_a_after_b: "Missing '{a}' after '{b}'.", 427 missing_property: "Missing property name.", 428 missing_space_a_b: "Missing space between '{a}' and '{b}'.", 429 missing_use_strict: "Missing 'use strict' statement.", 430 move_invocation: "Move the invocation into the parens that " + 431 "contain the function.", 432 move_var: "Move 'var' declarations to the top of the function.", 433 name_function: "Missing name in function statement.", 434 nested_comment: "Nested comment.", 435 not: "Nested not.", 436 not_a_constructor: "Do not use {a} as a constructor.", 437 not_a_defined: "'{a}' has not been fully defined yet.", 438 not_a_function: "'{a}' is not a function.", 439 not_a_label: "'{a}' is not a label.", 440 not_a_scope: "'{a}' is out of scope.", 441 not_greater: "'{a}' should not be greater than '{b}'.", 442 octal_a: "Don't use octal: '{a}'. Use '\\u....' instead.", 443 parameter_arguments_a: "Do not mutate parameter '{a}' when using 'arguments'.", 444 parameter_a_get_b: "Unexpected parameter '{a}' in get {b} function.", 445 parameter_set_a: "Expected parameter (value) in set {a} function.", 446 radix: "Missing radix parameter.", 447 read_only: "Read only.", 448 redefinition_a_b: "Redefinition of '{a}' from line {b}.", 449 reserved_a: "Reserved name '{a}'.", 450 scanned_a_b: "{a} ({b}% scanned).", 451 slash_equal: "A regular expression literal can be confused with '/='.", 452 statement_block: "Expected to see a statement and instead saw a block.", 453 stopping: "Stopping.", 454 strange_loop: "Strange loop.", 455 strict: "Strict violation.", 456 subscript: "['{a}'] is better written in dot notation.", 457 sync_a: "Unexpected sync method: '{a}'.", 458 tag_a_in_b: "A '<{a}>' must be within '<{b}>'.", 459 todo_comment: "Unexpected TODO comment.", 460 too_long: "Line too long.", 461 too_many: "Too many errors.", 462 trailing_decimal_a: "A trailing decimal point can be confused " + 463 "with a dot: '.{a}'.", 464 unclosed: "Unclosed string.", 465 unclosed_comment: "Unclosed comment.", 466 unclosed_regexp: "Unclosed regular expression.", 467 unescaped_a: "Unescaped '{a}'.", 468 unexpected_a: "Unexpected '{a}'.", 469 unexpected_char_a: "Unexpected character '{a}'.", 470 unexpected_comment: "Unexpected comment.", 471 unexpected_label_a: "Unexpected label '{a}'.", 472 unexpected_property_a: "Unexpected /*property*/ '{a}'.", 473 unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.", 474 unexpected_typeof_a: "Unexpected 'typeof'. " + 475 "Use '===' to compare directly with {a}.", 476 uninitialized_a: "Uninitialized '{a}'.", 477 unnecessary_else: "Unnecessary 'else' after disruption.", 478 unnecessary_initialize: "It is not necessary to initialize '{a}' " + 479 "to 'undefined'.", 480 unnecessary_use: "Unnecessary 'use strict'.", 481 unreachable_a_b: "Unreachable '{a}' after '{b}'.", 482 unsafe: "Unsafe character.", 483 unused_a: "Unused '{a}'.", 484 url: "JavaScript URL.", 485 use_array: "Use the array literal notation [].", 486 use_braces: "Spaces are hard to count. Use {{a}}.", 487 use_nested_if: "Expected 'else { if' and instead saw 'else if'.", 488 use_object: "Use the object literal notation {} or Object.create(null).", 489 use_or: "Use the || operator.", 490 use_param: "Use a named parameter.", 491 use_spaces: "Use spaces, not tabs.", 492 used_before_a: "'{a}' was used before it was defined.", 493 var_a_not: "Variable {a} was not declared correctly.", 494 var_loop: "Don't declare variables in a loop.", 495 weird_assignment: "Weird assignment.", 496 weird_condition: "Weird condition.", 497 weird_new: "Weird construction. Delete 'new'.", 498 weird_program: "Weird program.", 499 weird_relation: "Weird relation.", 500 weird_ternary: "Weird ternary.", 501 wrap_immediate: "Wrap an immediate function invocation in " + 502 "parentheses to assist the reader in understanding that the " + 503 "expression is the result of a function, and not the " + 504 "function itself.", 505 wrap_regexp: "Wrap the /regexp/ literal in parens to " + 506 "disambiguate the slash operator.", 507 write_is_wrong: "document.write can be a form of eval." 508 }, 509 closure = array_to_object([ 510 'goog' 511 ], false), 512 comments, 513 comments_off, 514 couch = array_to_object([ 515 'emit', 'getRow', 'isArray', 'log', 'provides', 'registerType', 516 'require', 'send', 'start', 'sum', 'toJSON' 517 ], false), 518 519 descapes = { 520 'b': '\b', 521 't': '\t', 522 'n': '\n', 523 'f': '\f', 524 'r': '\r', 525 '"': '"', 526 '/': '/', 527 '\\': '\\', 528 '!': '!' 529 }, 530 531 devel = array_to_object([ 532 'alert', 'confirm', 'console', 'Debug', 'opera', 'prompt', 'WSH' 533 ], false), 534 directive, 535 escapes = { 536 '\b': '\\b', 537 '\t': '\\t', 538 '\n': '\\n', 539 '\f': '\\f', 540 '\r': '\\r', 541 '\'': '\\\'', 542 '"' : '\\"', 543 '/' : '\\/', 544 '\\': '\\\\' 545 }, 546 547 funct, // The current function 548 549 functions, // All of the functions 550 global_funct, // The global body 551 global_scope, // The global scope 552 in_block, // Where function statements are not allowed 553 indent, 554 itself, // JSLINT itself 555 json_mode, 556 lex, // the tokenizer 557 lines, 558 lookahead, 559 node = array_to_object([ 560 'Buffer', 'clearImmediate', 'clearInterval', 'clearTimeout', 561 'console', 'exports', 'global', 'module', 'process', 562 'require', 'setImmediate', 'setInterval', 'setTimeout', 563 '__dirname', '__filename' 564 ], false), 565 node_js, 566 numbery = array_to_object(['indexOf', 'lastIndexOf', 'search'], true), 567 next_token, 568 option, 569 predefined, // Global variables defined by option 570 prereg, 571 prev_token, 572 property, 573 protosymbol, 574 regexp_flag = array_to_object(['g', 'i', 'm'], true), 575 return_this = function return_this() { 576 return this; 577 }, 578 rhino = array_to_object([ 579 'defineClass', 'deserialize', 'gc', 'help', 'load', 'loadClass', 580 'print', 'quit', 'readFile', 'readUrl', 'runCommand', 'seal', 581 'serialize', 'spawn', 'sync', 'toint32', 'version' 582 ], false), 583 584 scope, // An object containing an object for each variable in scope 585 semicolon_coda = array_to_object([';', '"', '\'', ')'], true), 586 587 // standard contains the global names that are provided by the 588 // ECMAScript standard. 589 590 standard = array_to_object([ 591 'Array', 'Boolean', 'Date', 'decodeURI', 'decodeURIComponent', 592 'encodeURI', 'encodeURIComponent', 'Error', 'eval', 'EvalError', 593 'Function', 'isFinite', 'isNaN', 'JSON', 'Map', 'Math', 'Number', 594 'Object', 'parseInt', 'parseFloat', 'Promise', 'Proxy', 595 'RangeError', 'ReferenceError', 'Reflect', 'RegExp', 'Set', 596 'String', 'Symbol', 'SyntaxError', 'System', 'TypeError', 597 'URIError', 'WeakMap', 'WeakSet' 598 ], false), 599 600 strict_mode, 601 syntax = Object.create(null), 602 token, 603 tokens, 604 var_mode, 605 warnings, 606 607 // Regular expressions. Some of these are stupidly long. 608 609 // carriage return, carriage return linefeed, or linefeed 610 crlfx = /\r\n?|\n/, 611 // unsafe characters that are silently deleted by one or more browsers 612 cx = /[\u0000-\u0008\u000a-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/, 613 // identifier 614 ix = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/, 615 // javascript url 616 jx = /^(?:javascript|jscript|ecmascript|vbscript)\s*:/i, 617 // star slash 618 lx = /\*\/|\/\*/, 619 // characters in strings that need escapement 620 nx = /[\u0000-\u001f'\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 621 // sync 622 syx = /Sync$/, 623 // comment todo 624 tox = /^\W*to\s*do(?:\W|$)/i, 625 // token 626 tx = /^\s*([(){}\[\]\?.,:;'"~#@`]|={1,3}|\/(\*(jslint|properties|property|members?|globals?)?|=|\/)?|\*[\/=]?|\+(?:=|\++)?|-(?:=|-+)?|[\^%]=?|&[&=]?|\|[|=]?|>{1,3}=?|<(?:[\/=!]|\!(\[|--)?|<=?)?|\!(\!|==?)?|[a-zA-Z_$][a-zA-Z0-9_$]*|[0-9]+(?:[xX][0-9a-fA-F]+|\.[0-9]*)?(?:[eE][+\-]?[0-9]+)?)/; 627 628 629 if (typeof String.prototype.entityify !== 'function') { 630 String.prototype.entityify = function () { 631 return this 632 .replace(/&/g, '&') 633 .replace(/</g, '<') 634 .replace(/>/g, '>'); 635 }; 636 } 637 638 if (typeof String.prototype.isAlpha !== 'function') { 639 String.prototype.isAlpha = function () { 640 return (this >= 'a' && this <= 'z\uffff') || 641 (this >= 'A' && this <= 'Z\uffff'); 642 }; 643 } 644 645 if (typeof String.prototype.isDigit !== 'function') { 646 String.prototype.isDigit = function () { 647 return (this >= '0' && this <= '9'); 648 }; 649 } 650 651 if (typeof String.prototype.supplant !== 'function') { 652 String.prototype.supplant = function (o) { 653 return this.replace(/\{([^{}]*)\}/g, function (a, b) { 654 var replacement = o[b]; 655 return typeof replacement === 'string' || 656 typeof replacement === 'number' ? replacement : a; 657 }); 658 }; 659 } 660 661 662 function sanitize(a) { 663 664 // Escapify a troublesome character. 665 666 return escapes[a] || 667 '\\u' + ('0000' + a.charCodeAt().toString(16)).slice(-4); 668 } 669 670 671 function add_to_predefined(group) { 672 Object.keys(group).forEach(function (name) { 673 predefined[name] = group[name]; 674 }); 675 } 676 677 678 function assume() { 679 if (option.browser) { 680 add_to_predefined(browser); 681 option.browser = false; 682 } 683 if (option.closure) { 684 add_to_predefined(closure); 685 } 686 if (option.couch) { 687 add_to_predefined(couch); 688 option.couch = false; 689 } 690 if (option.devel) { 691 add_to_predefined(devel); 692 option.devel = false; 693 } 694 if (option.node) { 695 add_to_predefined(node); 696 option.node = false; 697 node_js = true; 698 } 699 if (option.rhino) { 700 add_to_predefined(rhino); 701 option.rhino = false; 702 } 703 } 704 705 706 // Produce an error warning. 707 708 function artifact(tok) { 709 if (!tok) { 710 tok = next_token; 711 } 712 return tok.id === '(number)' ? tok.number : tok.string; 713 } 714 715 function quit(message, line, character) { 716 throw { 717 name: 'JSLintError', 718 line: line, 719 character: character, 720 message: bundle.scanned_a_b.supplant({ 721 a: bundle[message] || message, 722 b: Math.floor((line / lines.length) * 100) 723 }) 724 }; 725 } 726 727 function warn(code, line, character, a, b, c, d) { 728 var warning = { // ~~ 729 id: '(error)', 730 raw: bundle[code] || code, 731 code: code, 732 evidence: lines[line - 1] || '', 733 line: line, 734 character: character, 735 a: a || artifact(this), 736 b: b, 737 c: c, 738 d: d 739 }; 740 warning.reason = warning.raw.supplant(warning); 741 itself.errors.push(warning); 742 if (option.passfail) { 743 quit('stopping', line, character); 744 } 745 warnings += 1; 746 if (warnings >= option.maxerr) { 747 quit('too_many', line, character); 748 } 749 return warning; 750 } 751 752 function stop(code, line, character, a, b, c, d) { 753 var warning = warn(code, line, character, a, b, c, d); 754 quit('stopping', warning.line, warning.character); 755 } 756 757 function expected_at(at) { 758 if (!option.white && next_token.from !== at) { 759 next_token.warn('expected_a_at_b_c', '', at, next_token.from); 760 } 761 } 762 763 // lexical analysis and token construction 764 765 lex = (function lex() { 766 var character, c, from, length, line, pos, source_row; 767 768 // Private lex methods 769 770 function next_line() { 771 var at; 772 character = 1; 773 source_row = lines[line]; 774 line += 1; 775 if (source_row === undefined) { 776 return false; 777 } 778 at = source_row.search(/\t/); 779 if (at >= 0) { 780 if (option.white) { 781 source_row = source_row.replace(/\t/g, ' '); 782 } else { 783 warn('use_spaces', line, at + 1); 784 } 785 } 786 at = source_row.search(cx); 787 if (at >= 0) { 788 warn('unsafe', line, at); 789 } 790 if (option.maxlen && option.maxlen < source_row.length) { 791 warn('too_long', line, source_row.length); 792 } 793 return true; 794 } 795 796 // Produce a token object. The token inherits from a syntax symbol. 797 798 function it(type, value) { 799 var id, the_token; 800 if (type === '(string)') { 801 if (jx.test(value)) { 802 warn('url', line, from); 803 } 804 } 805 the_token = Object.create(syntax[( 806 type === '(punctuator)' || (type === '(identifier)' && 807 Object.prototype.hasOwnProperty.call(syntax, value)) 808 ? value 809 : type 810 )] || syntax['(error)']); 811 if (type === '(identifier)') { 812 the_token.identifier = true; 813 if (value === '__iterator__' || value === '__proto__') { 814 stop('reserved_a', line, from, value); 815 } else if (!option.nomen && 816 (value.charAt(0) === '_' || 817 value.charAt(value.length - 1) === '_')) { 818 warn('dangling_a', line, from, value); 819 } 820 } 821 if (type === '(number)') { 822 the_token.number = +value; 823 } else if (value !== undefined) { 824 the_token.string = String(value); 825 } 826 the_token.line = line; 827 the_token.from = from; 828 the_token.thru = character; 829 if (comments.length) { 830 the_token.comments = comments; 831 comments = []; 832 } 833 id = the_token.id; 834 prereg = id && ( 835 ('(,=:[!&|?{};~+-*%^<>'.indexOf(id.charAt(id.length - 1)) >= 0) || 836 id === 'return' || id === 'case' 837 ); 838 return the_token; 839 } 840 841 function match(x) { 842 var exec = x.exec(source_row), first; 843 if (exec) { 844 length = exec[0].length; 845 first = exec[1]; 846 c = first.charAt(0); 847 source_row = source_row.slice(length); 848 from = character + length - first.length; 849 character += length; 850 return first; 851 } 852 for (;;) { 853 if (!source_row) { 854 if (!option.white) { 855 warn('unexpected_char_a', line, character - 1, '(space)'); 856 } 857 return; 858 } 859 c = source_row.charAt(0); 860 if (c !== ' ') { 861 break; 862 } 863 source_row = source_row.slice(1); 864 character += 1; 865 } 866 stop('unexpected_char_a', line, character, c); 867 868 } 869 870 function string(x) { 871 var ch, at = 0, r = '', result; 872 873 function hex(n) { 874 var i = parseInt(source_row.substr(at + 1, n), 16); 875 at += n; 876 if (i >= 32 && i <= 126 && 877 i !== 34 && i !== 92 && i !== 39) { 878 warn('unexpected_a', line, character, '\\'); 879 } 880 character += n; 881 ch = String.fromCharCode(i); 882 } 883 884 if (json_mode && x !== '"') { 885 warn('expected_a_b', line, character, '"', x); 886 } 887 888 for (;;) { 889 while (at >= source_row.length) { 890 at = 0; 891 if (!next_line()) { 892 stop('unclosed', line - 1, from); 893 } 894 } 895 ch = source_row.charAt(at); 896 if (ch === x) { 897 character += 1; 898 source_row = source_row.slice(at + 1); 899 result = it('(string)', r); 900 result.quote = x; 901 return result; 902 } 903 if (ch < ' ') { 904 if (ch === '\n' || ch === '\r') { 905 break; 906 } 907 warn('control_a', line, character + at, 908 source_row.slice(0, at)); 909 } else if (ch === '\\') { 910 at += 1; 911 character += 1; 912 ch = source_row.charAt(at); 913 switch (ch) { 914 case '': 915 warn('unexpected_a', line, character, '\\'); 916 next_line(); 917 at = -1; 918 break; 919 case '\'': 920 if (json_mode) { 921 warn('unexpected_a', line, character, '\\\''); 922 } 923 break; 924 case 'u': 925 hex(4); 926 break; 927 case 'v': 928 if (json_mode) { 929 warn('unexpected_a', line, character, '\\v'); 930 } 931 ch = '\v'; 932 break; 933 case 'x': 934 if (json_mode) { 935 warn('unexpected_a', line, character, '\\x'); 936 } 937 hex(2); 938 break; 939 default: 940 if (typeof descapes[ch] !== 'string') { 941 warn(ch >= '0' && ch <= '7' ? 'octal_a' : 'unexpected_a', 942 line, character, '\\' + ch); 943 } else { 944 ch = descapes[ch]; 945 } 946 } 947 } 948 r += ch; 949 character += 1; 950 at += 1; 951 } 952 } 953 954 function number(snippet) { 955 var digit; 956 if (source_row.charAt(0).isAlpha()) { 957 warn('expected_space_a_b', 958 line, character, c, source_row.charAt(0)); 959 } 960 if (c === '0') { 961 digit = snippet.charAt(1); 962 if (digit.isDigit()) { 963 if (token.id !== '.') { 964 warn('unexpected_a', line, character, snippet); 965 } 966 } else if (json_mode && (digit === 'x' || digit === 'X')) { 967 warn('unexpected_a', line, character, '0x'); 968 } 969 } 970 if (snippet.slice(snippet.length - 1) === '.') { 971 warn('trailing_decimal_a', line, character, snippet); 972 } 973 digit = +snippet; 974 if (!isFinite(digit)) { 975 warn('bad_number', line, character, snippet); 976 } 977 snippet = digit; 978 return it('(number)', snippet); 979 } 980 981 function comment(snippet, type) { 982 if (comments_off) { 983 warn('unexpected_comment', line, character); 984 } else if (!option.todo && tox.test(snippet)) { 985 warn('todo_comment', line, character); 986 } 987 comments.push({ 988 id: type, 989 from: from, 990 thru: character, 991 line: line, 992 string: snippet 993 }); 994 } 995 996 function regexp() { 997 var at = 0, 998 b, 999 bit, 1000 depth = 0, 1001 flag = '', 1002 high, 1003 letter, 1004 low, 1005 potential, 1006 quote, 1007 result; 1008 for (;;) { 1009 b = true; 1010 c = source_row.charAt(at); 1011 at += 1; 1012 switch (c) { 1013 case '': 1014 stop('unclosed_regexp', line, from); 1015 return; 1016 case '/': 1017 if (depth > 0) { 1018 warn('unescaped_a', line, from + at, '/'); 1019 } 1020 c = source_row.slice(0, at - 1); 1021 potential = Object.create(regexp_flag); 1022 for (;;) { 1023 letter = source_row.charAt(at); 1024 if (potential[letter] !== true) { 1025 break; 1026 } 1027 potential[letter] = false; 1028 at += 1; 1029 flag += letter; 1030 } 1031 if (source_row.charAt(at).isAlpha()) { 1032 stop('unexpected_a', line, from, source_row.charAt(at)); 1033 } 1034 character += at; 1035 source_row = source_row.slice(at); 1036 quote = source_row.charAt(0); 1037 if (quote === '/' || quote === '*') { 1038 stop('confusing_regexp', line, from); 1039 } 1040 result = it('(regexp)', c); 1041 result.flag = flag; 1042 return result; 1043 case '\\': 1044 c = source_row.charAt(at); 1045 if (c < ' ') { 1046 warn('control_a', line, from + at, String(c)); 1047 } else if (c === '<') { 1048 warn('unexpected_a', line, from + at, '\\'); 1049 } 1050 at += 1; 1051 break; 1052 case '(': 1053 depth += 1; 1054 b = false; 1055 if (source_row.charAt(at) === '?') { 1056 at += 1; 1057 switch (source_row.charAt(at)) { 1058 case ':': 1059 case '=': 1060 case '!': 1061 at += 1; 1062 break; 1063 default: 1064 warn('expected_a_b', line, from + at, 1065 ':', source_row.charAt(at)); 1066 } 1067 } 1068 break; 1069 case '|': 1070 b = false; 1071 break; 1072 case ')': 1073 if (depth === 0) { 1074 warn('unescaped_a', line, from + at, ')'); 1075 } else { 1076 depth -= 1; 1077 } 1078 break; 1079 case ' ': 1080 pos = 1; 1081 while (source_row.charAt(at) === ' ') { 1082 at += 1; 1083 pos += 1; 1084 } 1085 if (pos > 1) { 1086 warn('use_braces', line, from + at, pos); 1087 } 1088 break; 1089 case '[': 1090 c = source_row.charAt(at); 1091 if (c === '^') { 1092 at += 1; 1093 if (!option.regexp) { 1094 warn('insecure_a', line, from + at, c); 1095 } else if (source_row.charAt(at) === ']') { 1096 stop('unescaped_a', line, from + at, '^'); 1097 } 1098 } 1099 bit = false; 1100 if (c === ']') { 1101 warn('empty_class', line, from + at - 1); 1102 bit = true; 1103 } 1104 klass: do { 1105 c = source_row.charAt(at); 1106 at += 1; 1107 switch (c) { 1108 case '[': 1109 case '^': 1110 warn('unescaped_a', line, from + at, c); 1111 bit = true; 1112 break; 1113 case '-': 1114 if (bit) { 1115 bit = false; 1116 } else { 1117 warn('unescaped_a', line, from + at, '-'); 1118 bit = true; 1119 } 1120 break; 1121 case ']': 1122 if (!bit) { 1123 warn('unescaped_a', line, from + at - 1, '-'); 1124 } 1125 break klass; 1126 case '\\': 1127 c = source_row.charAt(at); 1128 if (c < ' ') { 1129 warn('control_a', line, from + at, String(c)); 1130 } else if (c === '<') { 1131 warn('unexpected_a', line, from + at, '\\'); 1132 } 1133 at += 1; 1134 bit = true; 1135 break; 1136 case '/': 1137 warn('unescaped_a', line, from + at - 1, '/'); 1138 bit = true; 1139 break; 1140 default: 1141 bit = true; 1142 } 1143 } while (c); 1144 break; 1145 case '.': 1146 if (!option.regexp) { 1147 warn('insecure_a', line, from + at, c); 1148 } 1149 break; 1150 case ']': 1151 case '?': 1152 case '{': 1153 case '}': 1154 case '+': 1155 case '*': 1156 warn('unescaped_a', line, from + at, c); 1157 break; 1158 } 1159 if (b) { 1160 switch (source_row.charAt(at)) { 1161 case '?': 1162 case '+': 1163 case '*': 1164 at += 1; 1165 if (source_row.charAt(at) === '?') { 1166 at += 1; 1167 } 1168 break; 1169 case '{': 1170 at += 1; 1171 c = source_row.charAt(at); 1172 if (c < '0' || c > '9') { 1173 warn('expected_number_a', line, 1174 from + at, c); 1175 } 1176 at += 1; 1177 low = +c; 1178 for (;;) { 1179 c = source_row.charAt(at); 1180 if (c < '0' || c > '9') { 1181 break; 1182 } 1183 at += 1; 1184 low = +c + (low * 10); 1185 } 1186 high = low; 1187 if (c === ',') { 1188 at += 1; 1189 high = Infinity; 1190 c = source_row.charAt(at); 1191 if (c >= '0' && c <= '9') { 1192 at += 1; 1193 high = +c; 1194 for (;;) { 1195 c = source_row.charAt(at); 1196 if (c < '0' || c > '9') { 1197 break; 1198 } 1199 at += 1; 1200 high = +c + (high * 10); 1201 } 1202 } 1203 } 1204 if (source_row.charAt(at) !== '}') { 1205 warn('expected_a_b', line, from + at, 1206 '}', c); 1207 } else { 1208 at += 1; 1209 } 1210 if (source_row.charAt(at) === '?') { 1211 at += 1; 1212 } 1213 if (low > high) { 1214 warn('not_greater', line, from + at, 1215 low, high); 1216 } 1217 break; 1218 } 1219 } 1220 } 1221 c = source_row.slice(0, at - 1); 1222 character += at; 1223 source_row = source_row.slice(at); 1224 return it('(regexp)', c); 1225 } 1226 1227 // Public lex methods 1228 1229 return { 1230 init: function (source) { 1231 if (typeof source === 'string') { 1232 lines = source.split(crlfx); 1233 } else { 1234 lines = source; 1235 } 1236 line = 0; 1237 next_line(); 1238 from = 1; 1239 }, 1240 1241 // token -- this is called by advance to get the next token. 1242 1243 token: function () { 1244 var first, i, snippet; 1245 1246 for (;;) { 1247 while (!source_row) { 1248 if (!next_line()) { 1249 return it('(end)'); 1250 } 1251 } 1252 snippet = match(tx); 1253 if (snippet) { 1254 1255 // identifier 1256 1257 first = snippet.charAt(0); 1258 if (first.isAlpha() || first === '_' || first === '$') { 1259 return it('(identifier)', snippet); 1260 } 1261 1262 // number 1263 1264 if (first.isDigit()) { 1265 return number(snippet); 1266 } 1267 switch (snippet) { 1268 1269 // string 1270 1271 case '"': 1272 case "'": 1273 return string(snippet); 1274 1275 // // comment 1276 1277 case '//': 1278 comment(source_row, '//'); 1279 source_row = ''; 1280 break; 1281 1282 // /* comment 1283 1284 case '/*': 1285 for (;;) { 1286 i = source_row.search(lx); 1287 if (i >= 0) { 1288 break; 1289 } 1290 character = source_row.length; 1291 comment(source_row); 1292 from = 0; 1293 if (!next_line()) { 1294 stop('unclosed_comment', line, character); 1295 } 1296 } 1297 comment(source_row.slice(0, i), '/*'); 1298 character += i + 2; 1299 if (source_row.charAt(i) === '/') { 1300 stop('nested_comment', line, character); 1301 } 1302 source_row = source_row.slice(i + 2); 1303 break; 1304 1305 case '': 1306 break; 1307 // / 1308 case '/': 1309 if (token.id === '/=') { 1310 stop('slash_equal', line, from); 1311 } 1312 return prereg 1313 ? regexp() 1314 : it('(punctuator)', snippet); 1315 1316 // punctuator 1317 default: 1318 return it('(punctuator)', snippet); 1319 } 1320 } 1321 } 1322 } 1323 }; 1324 }()); 1325 1326 function define(kind, token) { 1327 1328 // Define a name. 1329 1330 var name = token.string, 1331 master = scope[name]; // The current definition of the name 1332 1333 // vars are created with a deadzone, so that the expression that initializes 1334 // the var cannot access the var. Functions are not writeable. 1335 1336 token.dead = false; 1337 token.init = false; 1338 token.kind = kind; 1339 token.master = master; 1340 token.used = 0; 1341 token.writeable = true; 1342 1343 // Global variables are a little weird. They can be defined multiple times. 1344 // Some predefined global vars are (or should) not be writeable. 1345 1346 if (kind === 'var' && funct === global_funct) { 1347 if (!master) { 1348 if (predefined[name] === false) { 1349 token.writeable = false; 1350 } 1351 global_scope[name] = token; 1352 } 1353 } else { 1354 1355 // It is an error if the name has already been defined in this scope, except 1356 // when reusing an exception variable name. 1357 1358 if (master && !option.defined) { 1359 if (master.function === funct) { 1360 if (master.kind !== 'exception' || kind !== 'exception' || 1361 !master.dead) { 1362 token.warn('already_defined', name); 1363 } 1364 } else if (master.function !== global_funct) { 1365 if (kind === 'var') { 1366 token.warn('redefinition_a_b', name, master.line); 1367 } 1368 } 1369 } 1370 scope[name] = token; 1371 if (kind === 'var') { 1372 block_var.push(name); 1373 } 1374 } 1375 } 1376 1377 function peek(distance) { 1378 1379 // Peek ahead to a future token. The distance is how far ahead to look. The 1380 // default is the next token. 1381 1382 var found, slot = 0; 1383 1384 distance = distance || 0; 1385 while (slot <= distance) { 1386 found = lookahead[slot]; 1387 if (!found) { 1388 found = lookahead[slot] = lex.token(); 1389 } 1390 slot += 1; 1391 } 1392 return found; 1393 } 1394 1395 1396 function advance(id, match) { 1397 1398 // Produce the next token, also looking for programming errors. 1399 1400 if (indent) { 1401 1402 // If indentation checking was requested, then inspect all of the line breakings. 1403 // The var statement is tricky because the names might be aligned or not. We 1404 // look at the first line break after the var to determine the programmer's 1405 // intention. 1406 1407 if (var_mode && next_token.line !== token.line) { 1408 if ((var_mode !== indent || !next_token.edge) && 1409 next_token.from === indent.at - 1410 (next_token.edge ? option.indent : 0)) { 1411 var dent = indent; 1412 for (;;) { 1413 dent.at -= option.indent; 1414 if (dent === var_mode) { 1415 break; 1416 } 1417 dent = dent.was; 1418 } 1419 dent.open = false; 1420 } 1421 var_mode = null; 1422 } 1423 if (next_token.id === '?' && indent.mode === ':' && 1424 token.line !== next_token.line) { 1425 indent.at -= option.indent; 1426 } 1427 if (indent.open) { 1428 1429 // If the token is an edge. 1430 1431 if (next_token.edge) { 1432 if (next_token.edge === 'label') { 1433 expected_at(1); 1434 } else if (next_token.edge === 'case' || indent.mode === 'statement') { 1435 expected_at(indent.at - option.indent); 1436 } else if (indent.mode !== 'array' || next_token.line !== token.line) { 1437 expected_at(indent.at); 1438 } 1439 1440 // If the token is not an edge, but is the first token on the line. 1441 1442 } else if (next_token.line !== token.line) { 1443 if (next_token.from < indent.at + (indent.mode === 1444 'expression' ? 0 : option.indent)) { 1445 expected_at(indent.at + option.indent); 1446 } 1447 indent.wrap = true; 1448 } 1449 } else if (next_token.line !== token.line) { 1450 if (next_token.edge) { 1451 expected_at(indent.at); 1452 } else { 1453 indent.wrap = true; 1454 if (indent.mode === 'statement' || indent.mode === 'var') { 1455 expected_at(indent.at + option.indent); 1456 } else if (next_token.from < indent.at + (indent.mode === 1457 'expression' ? 0 : option.indent)) { 1458 expected_at(indent.at + option.indent); 1459 } 1460 } 1461 } 1462 } 1463 1464 switch (token.id) { 1465 case '(number)': 1466 if (next_token.id === '.') { 1467 next_token.warn('trailing_decimal_a'); 1468 } 1469 break; 1470 case '-': 1471 if (next_token.id === '-' || next_token.id === '--') { 1472 next_token.warn('confusing_a'); 1473 } 1474 break; 1475 case '+': 1476 if (next_token.id === '+' || next_token.id === '++') { 1477 next_token.warn('confusing_a'); 1478 } 1479 break; 1480 } 1481 if (token.id === '(string)' || token.identifier) { 1482 anonname = token.string; 1483 } 1484 1485 if (id && next_token.id !== id) { 1486 if (match) { 1487 next_token.warn('expected_a_b_from_c_d', id, 1488 match.id, match.line, artifact()); 1489 } else if (!next_token.identifier || next_token.string !== id) { 1490 next_token.warn('expected_a_b', id, artifact()); 1491 } 1492 } 1493 prev_token = token; 1494 token = next_token; 1495 next_token = lookahead.shift() || lex.token(); 1496 next_token.function = funct; 1497 tokens.push(next_token); 1498 } 1499 1500 1501 function do_globals() { 1502 var name, writeable; 1503 for (;;) { 1504 if (next_token.id !== '(string)' && !next_token.identifier) { 1505 return; 1506 } 1507 name = next_token.string; 1508 advance(); 1509 writeable = false; 1510 if (next_token.id === ':') { 1511 advance(':'); 1512 switch (next_token.id) { 1513 case 'true': 1514 writeable = predefined[name] !== false; 1515 advance('true'); 1516 break; 1517 case 'false': 1518 advance('false'); 1519 break; 1520 default: 1521 next_token.stop('unexpected_a'); 1522 } 1523 } 1524 predefined[name] = writeable; 1525 if (next_token.id !== ',') { 1526 return; 1527 } 1528 advance(','); 1529 } 1530 } 1531 1532 1533 function do_jslint() { 1534 var name, value; 1535 while (next_token.id === '(string)' || next_token.identifier) { 1536 name = next_token.string; 1537 if (!allowed_option[name]) { 1538 next_token.stop('unexpected_a'); 1539 } 1540 advance(); 1541 if (next_token.id !== ':') { 1542 next_token.stop('expected_a_b', ':', artifact()); 1543 } 1544 advance(':'); 1545 if (typeof allowed_option[name] === 'number') { 1546 value = next_token.number; 1547 if (value > allowed_option[name] || value <= 0 || 1548 Math.floor(value) !== value) { 1549 next_token.stop('expected_small_a'); 1550 } 1551 option[name] = value; 1552 } else { 1553 if (next_token.id === 'true') { 1554 option[name] = true; 1555 } else if (next_token.id === 'false') { 1556 option[name] = false; 1557 } else { 1558 next_token.stop('unexpected_a'); 1559 } 1560 } 1561 advance(); 1562 if (next_token.id === ',') { 1563 advance(','); 1564 } 1565 } 1566 assume(); 1567 } 1568 1569 1570 function do_properties() { 1571 var name; 1572 option.properties = true; 1573 for (;;) { 1574 if (next_token.id !== '(string)' && !next_token.identifier) { 1575 return; 1576 } 1577 name = next_token.string; 1578 advance(); 1579 if (next_token.id === ':') { 1580 for (;;) { 1581 advance(); 1582 if (next_token.id !== '(string)' && !next_token.identifier) { 1583 break; 1584 } 1585 } 1586 } 1587 property[name] = 0; 1588 if (next_token.id !== ',') { 1589 return; 1590 } 1591 advance(','); 1592 } 1593 } 1594 1595 1596 directive = function directive() { 1597 var command = this.id, 1598 old_comments_off = comments_off, 1599 old_indent = indent; 1600 comments_off = true; 1601 indent = null; 1602 if (next_token.line === token.line && next_token.from === token.thru) { 1603 next_token.warn('missing_space_a_b', artifact(token), artifact()); 1604 } 1605 if (lookahead.length > 0) { 1606 this.warn('unexpected_a'); 1607 } 1608 switch (command) { 1609 case '/*properties': 1610 case '/*property': 1611 case '/*members': 1612 case '/*member': 1613 do_properties(); 1614 break; 1615 case '/*jslint': 1616 do_jslint(); 1617 break; 1618 case '/*globals': 1619 case '/*global': 1620 do_globals(); 1621 break; 1622 default: 1623 this.stop('unexpected_a'); 1624 } 1625 comments_off = old_comments_off; 1626 advance('*/'); 1627 indent = old_indent; 1628 }; 1629 1630 1631 // Indentation intention 1632 1633 function edge(mode) { 1634 next_token.edge = indent ? indent.open && (mode || 'edge') : ''; 1635 } 1636 1637 1638 function step_in(mode) { 1639 var open; 1640 if (typeof mode === 'number') { 1641 indent = { 1642 at: +mode, 1643 open: true, 1644 was: indent 1645 }; 1646 } else if (!indent) { 1647 indent = { 1648 at: 1, 1649 mode: 'statement', 1650 open: true 1651 }; 1652 } else if (mode === 'statement') { 1653 indent = { 1654 at: indent.at, 1655 open: true, 1656 was: indent 1657 }; 1658 } else { 1659 open = mode === 'var' || next_token.line !== token.line; 1660 indent = { 1661 at: (open || mode === 'control' 1662 ? indent.at + option.indent 1663 : indent.at) + (indent.wrap ? option.indent : 0), 1664 mode: mode, 1665 open: open, 1666 was: indent 1667 }; 1668 if (mode === 'var' && open) { 1669 var_mode = indent; 1670 } 1671 } 1672 } 1673 1674 function step_out(id, symbol) { 1675 if (id) { 1676 if (indent && indent.open) { 1677 indent.at -= option.indent; 1678 edge(); 1679 } 1680 advance(id, symbol); 1681 } 1682 if (indent) { 1683 indent = indent.was; 1684 } 1685 } 1686 1687 // Functions for conformance of whitespace. 1688 1689 function one_space(left, right) { 1690 left = left || token; 1691 right = right || next_token; 1692 if (right.id !== '(end)' && !option.white && 1693 (token.line !== right.line || 1694 token.thru + 1 !== right.from)) { 1695 right.warn('expected_space_a_b', artifact(token), artifact(right)); 1696 } 1697 } 1698 1699 function one_space_only(left, right) { 1700 left = left || token; 1701 right = right || next_token; 1702 if (right.id !== '(end)' && (left.line !== right.line || 1703 (!option.white && left.thru + 1 !== right.from))) { 1704 right.warn('expected_space_a_b', artifact(left), artifact(right)); 1705 } 1706 } 1707 1708 function no_space(left, right) { 1709 left = left || token; 1710 right = right || next_token; 1711 if ((!option.white) && 1712 left.thru !== right.from && left.line === right.line) { 1713 if (!(option.closure && right.comments.length === 1 1714 && right.comments[0].string.substr(0, 7) === '*@type{')) { 1715 right.warn('unexpected_space_a_b', artifact(left), 1716 artifact(right)); 1717 } 1718 } 1719 } 1720 1721 function no_space_only(left, right) { 1722 left = left || token; 1723 right = right || next_token; 1724 if (right.id !== '(end)' && (left.line !== right.line || 1725 (!option.white && left.thru !== right.from))) { 1726 right.warn('unexpected_space_a_b', artifact(left), artifact(right)); 1727 } 1728 } 1729 1730 function spaces(left, right) { 1731 if (!option.white) { 1732 left = left || token; 1733 right = right || next_token; 1734 if (left.thru === right.from && left.line === right.line) { 1735 right.warn('missing_space_a_b', artifact(left), artifact(right)); 1736 } 1737 } 1738 } 1739 1740 function comma() { 1741 if (next_token.id !== ',') { 1742 warn('expected_a_b', token.line, token.thru, ',', artifact()); 1743 } else { 1744 if (!option.white) { 1745 no_space_only(); 1746 } 1747 advance(','); 1748 spaces(); 1749 } 1750 } 1751 1752 1753 function semicolon() { 1754 if (next_token.id !== ';') { 1755 warn('expected_a_b', token.line, token.thru, ';', artifact()); 1756 } else { 1757 if (!option.white) { 1758 no_space_only(); 1759 } 1760 advance(';'); 1761 if (semicolon_coda[next_token.id] !== true) { 1762 spaces(); 1763 } 1764 } 1765 } 1766 1767 function use_strict() { 1768 if (next_token.string === 'use strict') { 1769 if (strict_mode) { 1770 next_token.warn('unnecessary_use'); 1771 } 1772 edge(); 1773 advance(); 1774 semicolon(); 1775 strict_mode = true; 1776 return true; 1777 } 1778 return false; 1779 } 1780 1781 1782 function are_similar(a, b) { 1783 if (a === b) { 1784 return true; 1785 } 1786 if (Array.isArray(a)) { 1787 if (Array.isArray(b) && a.length === b.length) { 1788 var i; 1789 for (i = 0; i < a.length; i += 1) { 1790 if (!are_similar(a[i], b[i])) { 1791 return false; 1792 } 1793 } 1794 return true; 1795 } 1796 return false; 1797 } 1798 if (Array.isArray(b)) { 1799 return false; 1800 } 1801 if (a.id === '(number)' && b.id === '(number)') { 1802 return a.number === b.number; 1803 } 1804 if (a.arity === b.arity && a.string === b.string) { 1805 switch (a.arity) { 1806 case undefined: 1807 return a.string === b.string; 1808 case 'prefix': 1809 case 'suffix': 1810 return a.id === b.id && are_similar(a.first, b.first) && 1811 a.id !== '{' && a.id !== '['; 1812 case 'infix': 1813 return are_similar(a.first, b.first) && 1814 are_similar(a.second, b.second); 1815 case 'ternary': 1816 return are_similar(a.first, b.first) && 1817 are_similar(a.second, b.second) && 1818 are_similar(a.third, b.third); 1819 case 'function': 1820 case 'regexp': 1821 return false; 1822 default: 1823 return true; 1824 } 1825 } 1826 if (a.id === '.' && b.id === '[' && b.arity === 'infix') { 1827 return a.second.string === b.second.string && b.second.id === '(string)'; 1828 } 1829 if (a.id === '[' && a.arity === 'infix' && b.id === '.') { 1830 return a.second.string === b.second.string && a.second.id === '(string)'; 1831 } 1832 return false; 1833 } 1834 1835 1836 // This is the heart of JSLINT, the Pratt parser. In addition to parsing, it 1837 // is looking for ad hoc lint patterns. We add .fud to Pratt's model, which is 1838 // like .nud except that it is only used on the first token of a statement. 1839 // Having .fud makes it much easier to define statement-oriented languages like 1840 // JavaScript. I retained Pratt's nomenclature. 1841 1842 // .nud Null denotation 1843 // .fud First null denotation 1844 // .led Left denotation 1845 // lbp Left binding power 1846 // rbp Right binding power 1847 1848 // They are elements of the parsing method called Top Down Operator Precedence. 1849 1850 function expression(rbp, initial) { 1851 1852 // rbp is the right binding power. 1853 // initial indicates that this is the first expression of a statement. 1854 1855 var left; 1856 if (next_token.id === '(end)') { 1857 token.stop('unexpected_a', next_token.id); 1858 } 1859 advance(); 1860 if (initial) { 1861 anonname = 'anonymous'; 1862 } 1863 if (initial === true && token.fud) { 1864 left = token.fud(); 1865 } else { 1866 if (token.nud) { 1867 left = token.nud(); 1868 } else { 1869 if (next_token.id === '(number)' && token.id === '.') { 1870 token.warn('leading_decimal_a', artifact()); 1871 advance(); 1872 return token; 1873 } 1874 token.stop('expected_identifier_a', artifact(token)); 1875 } 1876 while (rbp < next_token.lbp) { 1877 advance(); 1878 left = token.led(left); 1879 } 1880 } 1881 if (left && left.assign && !initial) { 1882 if (!option.ass) { 1883 left.warn('assignment_expression'); 1884 } 1885 if (left.id !== '=' && left.first.master) { 1886 left.first.master.used = true; 1887 } 1888 } 1889 return left; 1890 } 1891 1892 protosymbol = { 1893 nud: function () { 1894 this.stop('unexpected_a'); 1895 }, 1896 led: function () { 1897 this.stop('expected_operator_a'); 1898 }, 1899 warn: function (code, a, b, c, d) { 1900 if (!this.warning) { 1901 this.warning = warn(code, this.line || 0, this.from || 0, 1902 a || artifact(this), b, c, d); 1903 } 1904 }, 1905 stop: function (code, a, b, c, d) { 1906 this.warning = undefined; 1907 this.warn(code, a, b, c, d); 1908 return quit('stopping', this.line, this.character); 1909 }, 1910 lbp: 0 1911 }; 1912 1913 // Functional constructors for making the symbols that will be inherited by 1914 // tokens. 1915 1916 function symbol(s, bp) { 1917 var x = syntax[s]; 1918 if (!x) { 1919 x = Object.create(protosymbol); 1920 x.id = x.string = s; 1921 x.lbp = bp || 0; 1922 syntax[s] = x; 1923 } 1924 return x; 1925 } 1926 1927 function postscript(x) { 1928 x.postscript = true; 1929 return x; 1930 } 1931 1932 function ultimate(s) { 1933 var x = symbol(s, 0); 1934 x.from = 1; 1935 x.thru = 1; 1936 x.line = 0; 1937 x.edge = 'edge'; 1938 x.string = s; 1939 return postscript(x); 1940 } 1941 1942 function reserve_name(x) { 1943 var c = x.id.charAt(0); 1944 if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { 1945 x.identifier = x.reserved = true; 1946 } 1947 return x; 1948 } 1949 1950 function stmt(s, f) { 1951 var x = symbol(s); 1952 x.fud = f; 1953 return reserve_name(x); 1954 } 1955 1956 function disrupt_stmt(s, f) { 1957 var x = stmt(s, f); 1958 x.disrupt = true; 1959 } 1960 1961 function labeled_stmt(s, f) { 1962 var x = stmt(s, function labeled() { 1963 var the_statement; 1964 if (funct.breakage) { 1965 funct.breakage.push(this); 1966 } else { 1967 funct.breakage = [this]; 1968 } 1969 the_statement = f.apply(this); 1970 if (funct.breakage.length > 1) { 1971 funct.breakage.pop(); 1972 } else { 1973 delete funct.breakage; 1974 } 1975 return the_statement; 1976 }); 1977 x.labeled = true; 1978 } 1979 1980 function prefix(s, f) { 1981 var x = symbol(s, 150); 1982 reserve_name(x); 1983 x.nud = function () { 1984 var that = this; 1985 that.arity = 'prefix'; 1986 if (typeof f === 'function') { 1987 that = f(that); 1988 if (that.arity !== 'prefix') { 1989 return that; 1990 } 1991 } else { 1992 if (s === 'typeof') { 1993 one_space(); 1994 } else { 1995 no_space_only(); 1996 } 1997 that.first = expression(150); 1998 } 1999 switch (that.id) { 2000 case '++': 2001 case '--': 2002 if (!option.plusplus) { 2003 that.warn('unexpected_a'); 2004 } else if ((!that.first.identifier || that.first.reserved) && 2005 that.first.id !== '.' && that.first.id !== '[') { 2006 that.warn('bad_operand'); 2007 } 2008 break; 2009 default: 2010 if (that.first.arity === 'prefix' || 2011 that.first.arity === 'function') { 2012 that.warn('unexpected_a'); 2013 } 2014 } 2015 return that; 2016 }; 2017 return x; 2018 } 2019 2020 2021 function type(s, t, nud) { 2022 var x = symbol(s); 2023 x.arity = t; 2024 if (nud) { 2025 x.nud = nud; 2026 } 2027 return x; 2028 } 2029 2030 2031 function reserve(s, f) { 2032 var x = symbol(s); 2033 x.identifier = x.reserved = true; 2034 if (typeof f === 'function') { 2035 x.nud = f; 2036 } 2037 return x; 2038 } 2039 2040 2041 function constant(name) { 2042 var x = reserve(name); 2043 x.string = name; 2044 x.nud = return_this; 2045 return x; 2046 } 2047 2048 2049 function reservevar(s, v) { 2050 return reserve(s, function () { 2051 if (typeof v === 'function') { 2052 v(this); 2053 } 2054 return this; 2055 }); 2056 } 2057 2058 2059 function infix(s, p, f, w) { 2060 var x = symbol(s, p); 2061 reserve_name(x); 2062 x.led = function (left) { 2063 this.arity = 'infix'; 2064 if (!w) { 2065 spaces(prev_token, token); 2066 spaces(); 2067 } 2068 if (!option.bitwise && this.bitwise) { 2069 this.warn('unexpected_a'); 2070 } 2071 if (typeof f === 'function') { 2072 return f(left, this); 2073 } 2074 this.first = left; 2075 this.second = expression(p); 2076 return this; 2077 }; 2078 return x; 2079 } 2080 2081 function expected_relation(node, message) { 2082 if (node.assign) { 2083 node.warn(message || 'conditional_assignment'); 2084 } 2085 return node; 2086 } 2087 2088 function expected_condition(node, message) { 2089 switch (node.id) { 2090 case '[': 2091 case '-': 2092 if (node.arity !== 'infix') { 2093 node.warn(message || 'weird_condition'); 2094 } 2095 break; 2096 case 'false': 2097 case 'function': 2098 case 'Infinity': 2099 case 'NaN': 2100 case 'null': 2101 case 'true': 2102 case 'undefined': 2103 case 'void': 2104 case '(number)': 2105 case '(regexp)': 2106 case '(string)': 2107 case '{': 2108 case '?': 2109 case '~': 2110 node.warn(message || 'weird_condition'); 2111 break; 2112 case '(': 2113 if (node.first.id === 'new' || 2114 (node.first.string === 'Boolean') || 2115 (node.first.id === '.' && 2116 numbery[node.first.second.string] === true)) { 2117 node.warn(message || 'weird_condition'); 2118 } 2119 break; 2120 } 2121 return node; 2122 } 2123 2124 function check_relation(node) { 2125 switch (node.arity) { 2126 case 'prefix': 2127 switch (node.id) { 2128 case '{': 2129 case '[': 2130 node.warn('unexpected_a'); 2131 break; 2132 case '!': 2133 node.warn('confusing_a'); 2134 break; 2135 } 2136 break; 2137 case 'function': 2138 case 'regexp': 2139 node.warn('unexpected_a'); 2140 break; 2141 default: 2142 if (node.id === 'NaN') { 2143 node.warn('isNaN'); 2144 } else if (node.relation) { 2145 node.warn('weird_relation'); 2146 } 2147 } 2148 return node; 2149 } 2150 2151 2152 function relation(s, eqeq) { 2153 var x = infix(s, 100, function (left, that) { 2154 check_relation(left); 2155 if (eqeq && !option.eqeq) { 2156 that.warn('expected_a_b', eqeq, that.id); 2157 } 2158 var right = expression(100); 2159 if (are_similar(left, right) || 2160 ((left.id === '(string)' || left.id === '(number)') && 2161 (right.id === '(string)' || right.id === '(number)'))) { 2162 that.warn('weird_relation'); 2163 } else if (left.id === 'typeof') { 2164 if (right.id !== '(string)') { 2165 right.warn("expected_string_a", artifact(right)); 2166 } else if (right.string === 'undefined' || 2167 right.string === 'null') { 2168 left.warn("unexpected_typeof_a", right.string); 2169 } 2170 } else if (right.id === 'typeof') { 2171 if (left.id !== '(string)') { 2172 left.warn("expected_string_a", artifact(left)); 2173 } else if (left.string === 'undefined' || 2174 left.string === 'null') { 2175 right.warn("unexpected_typeof_a", left.string); 2176 } 2177 } 2178 that.first = left; 2179 that.second = check_relation(right); 2180 return that; 2181 }); 2182 x.relation = true; 2183 return x; 2184 } 2185 2186 function lvalue(that, s) { 2187 var master; 2188 if (that.identifier) { 2189 master = scope[that.string]; 2190 if (master) { 2191 if (scope[that.string].writeable !== true) { 2192 that.warn('read_only'); 2193 } 2194 master.used -= 1; 2195 if (s === '=') { 2196 master.init = true; 2197 } 2198 } else if (that.reserved) { 2199 that.warn('expected_identifier_a_reserved'); 2200 } 2201 } else if (that.id === '.' || that.id === '[') { 2202 if (!that.first || that.first.string === 'arguments') { 2203 that.warn('bad_assignment'); 2204 } 2205 } else { 2206 that.warn('bad_assignment'); 2207 } 2208 } 2209 2210 2211 function assignop(s, op) { 2212 var x = infix(s, 20, function (left, that) { 2213 var next; 2214 that.first = left; 2215 lvalue(left, s); 2216 that.second = expression(20); 2217 if (that.id === '=' && are_similar(that.first, that.second)) { 2218 that.warn('weird_assignment'); 2219 } 2220 next = that; 2221 while (next_token.id === '=') { 2222 lvalue(next.second, '='); 2223 next_token.first = next.second; 2224 next.second = next_token; 2225 next = next_token; 2226 advance('='); 2227 next.second = expression(20); 2228 } 2229 return that; 2230 }); 2231 x.assign = true; 2232 if (op) { 2233 if (syntax[op].bitwise) { 2234 x.bitwise = true; 2235 } 2236 } 2237 return x; 2238 } 2239 2240 2241 function bitwise(s, p) { 2242 var x = infix(s, p, 'number'); 2243 x.bitwise = true; 2244 return x; 2245 } 2246 2247 2248 function suffix(s) { 2249 var x = symbol(s, 150); 2250 x.led = function (left) { 2251 no_space_only(prev_token, token); 2252 if (!option.plusplus) { 2253 this.warn('unexpected_a'); 2254 } else if ((!left.identifier || left.reserved) && 2255 left.id !== '.' && left.id !== '[') { 2256 this.warn('bad_operand'); 2257 } 2258 this.first = left; 2259 this.arity = 'suffix'; 2260 return this; 2261 }; 2262 return x; 2263 } 2264 2265 2266 function optional_identifier(variable) { 2267 if (next_token.identifier) { 2268 advance(); 2269 if (token.reserved && variable) { 2270 token.warn('expected_identifier_a_reserved'); 2271 } 2272 return token.string; 2273 } 2274 } 2275 2276 2277 function identifier(variable) { 2278 var i = optional_identifier(variable); 2279 if (!i) { 2280 next_token.stop(token.id === 'function' && next_token.id === '(' 2281 ? 'name_function' 2282 : 'expected_identifier_a'); 2283 } 2284 return i; 2285 } 2286 2287 2288 function statement() { 2289 2290 var label, preamble, the_statement; 2291 2292 // We don't like the empty statement. 2293 2294 if (next_token.id === ';') { 2295 next_token.warn('unexpected_a'); 2296 semicolon(); 2297 return; 2298 } 2299 2300 // Is this a labeled statement? 2301 2302 if (next_token.identifier && !next_token.reserved && peek().id === ':') { 2303 edge('label'); 2304 label = next_token; 2305 advance(); 2306 advance(':'); 2307 define('label', label); 2308 if (next_token.labeled !== true || funct === global_funct) { 2309 label.stop('unexpected_label_a'); 2310 } else if (jx.test(label.string + ':')) { 2311 label.warn('url'); 2312 } 2313 next_token.label = label; 2314 label.init = true; 2315 label.statement = next_token; 2316 } 2317 2318 // Parse the statement. 2319 2320 preamble = next_token; 2321 if (token.id !== 'else') { 2322 edge(); 2323 } 2324 step_in('statement'); 2325 the_statement = expression(0, true); 2326 if (the_statement) { 2327 2328 // Look for the final semicolon. 2329 2330 if (the_statement.arity === 'statement') { 2331 if (the_statement.id === 'switch' || 2332 (the_statement.block && the_statement.id !== 'do')) { 2333 spaces(); 2334 } else { 2335 semicolon(); 2336 } 2337 } else { 2338 2339 // If this is an expression statement, determine if it is acceptable. 2340 // We do not like 2341 // new Blah; 2342 // statements. If it is to be used at all, new should only be used to make 2343 // objects, not side effects. The expression statements we do like do 2344 // assignment or invocation or delete. 2345 2346 if (the_statement.id === '(') { 2347 if (the_statement.first.id === 'new') { 2348 next_token.warn('bad_new'); 2349 } 2350 } else if (the_statement.id === '++' || 2351 the_statement.id === '--') { 2352 lvalue(the_statement.first); 2353 } else if (!the_statement.assign && 2354 the_statement.id !== 'delete') { 2355 if (!option.closure || !preamble.comments) { 2356 preamble.warn('assignment_function_expression'); 2357 } 2358 } 2359 semicolon(); 2360 } 2361 } 2362 step_out(); 2363 if (label) { 2364 label.dead = true; 2365 } 2366 return the_statement; 2367 } 2368 2369 2370 function statements() { 2371 var array = [], disruptor, the_statement; 2372 2373 // A disrupt statement may not be followed by any other statement. 2374 // If the last statement is disrupt, then the sequence is disrupt. 2375 2376 while (next_token.postscript !== true) { 2377 if (next_token.id === ';') { 2378 next_token.warn('unexpected_a'); 2379 semicolon(); 2380 } else { 2381 if (next_token.string === 'use strict') { 2382 if ((!node_js) || funct !== global_funct || array.length > 0) { 2383 next_token.warn('function_strict'); 2384 } 2385 use_strict(); 2386 } 2387 if (disruptor) { 2388 next_token.warn('unreachable_a_b', next_token.string, 2389 disruptor.string); 2390 disruptor = null; 2391 } 2392 the_statement = statement(); 2393 if (the_statement) { 2394 array.push(the_statement); 2395 if (the_statement.disrupt) { 2396 disruptor = the_statement; 2397 array.disrupt = true; 2398 } 2399 } 2400 } 2401 } 2402 return array; 2403 } 2404 2405 2406 function block(kind) { 2407 2408 // A block is a sequence of statements wrapped in braces. 2409 2410 var array, 2411 curly = next_token, 2412 old_block_var = block_var, 2413 old_in_block = in_block, 2414 old_strict_mode = strict_mode; 2415 2416 in_block = kind !== 'function' && kind !== 'try' && kind !== 'catch'; 2417 block_var = []; 2418 if (curly.id === '{') { 2419 spaces(); 2420 advance('{'); 2421 step_in(); 2422 if (kind === 'function' && !use_strict() && !old_strict_mode && 2423 !option.sloppy && funct.level === 1) { 2424 next_token.warn('missing_use_strict'); 2425 } 2426 array = statements(); 2427 strict_mode = old_strict_mode; 2428 step_out('}', curly); 2429 } else if (in_block) { 2430 curly.stop('expected_a_b', '{', artifact()); 2431 } else { 2432 curly.warn('expected_a_b', '{', artifact()); 2433 array = [statement()]; 2434 array.disrupt = array[0].disrupt; 2435 } 2436 if (!(option.emptyblock || option.debug) && kind !== 'catch' && array.length === 0) { 2437 curly.warn('empty_block'); 2438 } 2439 block_var.forEach(function (name) { 2440 scope[name].dead = true; 2441 }); 2442 block_var = old_block_var; 2443 in_block = old_in_block; 2444 return array; 2445 } 2446 2447 2448 function tally_property(name) { 2449 if (option.properties && typeof property[name] !== 'number') { 2450 token.warn('unexpected_property_a', name); 2451 } 2452 if (property[name]) { 2453 property[name] += 1; 2454 } else { 2455 property[name] = 1; 2456 } 2457 } 2458 2459 2460 // ECMAScript parser 2461 2462 (function () { 2463 var x = symbol('(identifier)'); 2464 x.nud = function () { 2465 var name = this.string, 2466 master = scope[name], 2467 writeable; 2468 2469 // If the master is not in scope, then we may have an undeclared variable. 2470 // Check the predefined list. If it was predefined, create the global 2471 // variable. 2472 2473 if (!master) { 2474 writeable = predefined[name]; 2475 if (typeof writeable === 'boolean') { 2476 global_scope[name] = master = { 2477 dead: false, 2478 function: global_funct, 2479 kind: 'var', 2480 string: name, 2481 writeable: writeable 2482 }; 2483 2484 // But if the variable is not in scope, and is not predefined, and if we are not 2485 // in the global scope, then we have an undefined variable error. 2486 2487 } else { 2488 token.warn('used_before_a'); 2489 } 2490 } else { 2491 this.master = master; 2492 } 2493 2494 // Annotate uses that cross scope boundaries. 2495 2496 if (master) { 2497 if (master.kind === 'label') { 2498 this.warn('a_label'); 2499 } else { 2500 if (master.dead === true || master.dead === funct) { 2501 this.warn('a_scope'); 2502 } 2503 master.used += 1; 2504 if (master.function !== funct) { 2505 if (master.function === global_funct) { 2506 funct.global.push(name); 2507 } else { 2508 master.function.closure.push(name); 2509 funct.outer.push(name); 2510 } 2511 } 2512 } 2513 } 2514 return this; 2515 }; 2516 x.identifier = true; 2517 }()); 2518 2519 2520 // Build the syntax table by declaring the syntactic elements. 2521 2522 type('(array)', 'array'); 2523 type('(function)', 'function'); 2524 type('(number)', 'number', return_this); 2525 type('(object)', 'object'); 2526 type('(string)', 'string', return_this); 2527 type('(boolean)', 'boolean', return_this); 2528 type('(regexp)', 'regexp', return_this); 2529 2530 ultimate('(begin)'); 2531 ultimate('(end)'); 2532 ultimate('(error)'); 2533 postscript(symbol('}')); 2534 symbol(')'); 2535 symbol(']'); 2536 postscript(symbol('"')); 2537 postscript(symbol('\'')); 2538 symbol(';'); 2539 symbol(':'); 2540 symbol(','); 2541 symbol('#'); 2542 symbol('@'); 2543 symbol('*/'); 2544 postscript(reserve('case')); 2545 reserve('catch'); 2546 postscript(reserve('default')); 2547 reserve('else'); 2548 reserve('finally'); 2549 2550 reservevar('arguments', function (x) { 2551 if (strict_mode && funct === global_funct) { 2552 x.warn('strict'); 2553 } 2554 funct.arguments = true; 2555 }); 2556 reservevar('eval'); 2557 constant('false', 'boolean'); 2558 constant('Infinity', 'number'); 2559 constant('NaN', 'number'); 2560 constant('null', ''); 2561 reservevar('this', function (x) { 2562 if (strict_mode && funct.statement && funct.name.charAt(0) > 'Z') { 2563 x.warn('strict'); 2564 } 2565 }); 2566 constant('true', 'boolean'); 2567 constant('undefined', ''); 2568 2569 infix('?', 30, function (left, that) { 2570 step_in('?'); 2571 that.first = expected_condition(expected_relation(left)); 2572 that.second = expression(0); 2573 spaces(); 2574 step_out(); 2575 var colon = next_token; 2576 advance(':'); 2577 step_in(':'); 2578 spaces(); 2579 that.third = expression(10); 2580 that.arity = 'ternary'; 2581 if (are_similar(that.second, that.third)) { 2582 colon.warn('weird_ternary'); 2583 } else if (are_similar(that.first, that.second)) { 2584 that.warn('use_or'); 2585 } 2586 step_out(); 2587 return that; 2588 }); 2589 2590 infix('||', 40, function (left, that) { 2591 function paren_check(that) { 2592 if (that.id === '&&' && !that.paren) { 2593 that.warn('and'); 2594 } 2595 return that; 2596 } 2597 2598 that.first = paren_check(expected_condition(expected_relation(left))); 2599 that.second = paren_check(expected_relation(expression(40))); 2600 if (are_similar(that.first, that.second)) { 2601 that.warn('weird_condition'); 2602 } 2603 return that; 2604 }); 2605 2606 infix('&&', 50, function (left, that) { 2607 that.first = expected_condition(expected_relation(left)); 2608 that.second = expected_relation(expression(50)); 2609 if (are_similar(that.first, that.second)) { 2610 that.warn('weird_condition'); 2611 } 2612 return that; 2613 }); 2614 2615 prefix('void', function (that) { 2616 that.first = expression(0); 2617 that.warn('expected_a_b', 'undefined', 'void'); 2618 return that; 2619 }); 2620 2621 bitwise('|', 70); 2622 bitwise('^', 80); 2623 bitwise('&', 90); 2624 2625 relation('==', '==='); 2626 relation('==='); 2627 relation('!=', '!=='); 2628 relation('!=='); 2629 relation('<'); 2630 relation('>'); 2631 relation('<='); 2632 relation('>='); 2633 2634 bitwise('<<', 120); 2635 bitwise('>>', 120); 2636 bitwise('>>>', 120); 2637 2638 infix('in', 120, function (left, that) { 2639 that.warn('infix_in'); 2640 that.left = left; 2641 that.right = expression(130); 2642 return that; 2643 }); 2644 infix('instanceof', 120); 2645 infix('+', 130, function (left, that) { 2646 if (left.id === '(number)') { 2647 if (left.number === 0) { 2648 left.warn('unexpected_a', '0'); 2649 } 2650 } else if (left.id === '(string)') { 2651 if (left.string === '') { 2652 left.warn('expected_a_b', 'String', '\'\''); 2653 } 2654 } 2655 var right = expression(130); 2656 if (right.id === '(number)') { 2657 if (right.number === 0) { 2658 right.warn('unexpected_a', '0'); 2659 } 2660 } else if (right.id === '(string)') { 2661 if (right.string === '') { 2662 right.warn('expected_a_b', 'String', '\'\''); 2663 } 2664 } 2665 if (left.id === right.id) { 2666 if (left.id === '(string)' || left.id === '(number)') { 2667 if (left.id === '(string)') { 2668 left.string += right.string; 2669 if (jx.test(left.string)) { 2670 left.warn('url'); 2671 } 2672 } else { 2673 left.number += right.number; 2674 } 2675 left.thru = right.thru; 2676 return left; 2677 } 2678 } 2679 that.first = left; 2680 that.second = right; 2681 return that; 2682 }); 2683 prefix('+'); 2684 prefix('+++', function () { 2685 token.warn('confusing_a'); 2686 this.first = expression(150); 2687 this.arity = 'prefix'; 2688 return this; 2689 }); 2690 infix('+++', 130, function (left) { 2691 token.warn('confusing_a'); 2692 this.first = left; 2693 this.second = expression(130); 2694 return this; 2695 }); 2696 infix('-', 130, function (left, that) { 2697 if ((left.id === '(number)' && left.number === 0) || left.id === '(string)') { 2698 left.warn('unexpected_a'); 2699 } 2700 var right = expression(130); 2701 if ((right.id === '(number)' && right.number === 0) || right.id === '(string)') { 2702 right.warn('unexpected_a'); 2703 } 2704 if (left.id === right.id && left.id === '(number)') { 2705 left.number -= right.number; 2706 left.thru = right.thru; 2707 return left; 2708 } 2709 that.first = left; 2710 that.second = right; 2711 return that; 2712 }); 2713 prefix('-'); 2714 prefix('---', function () { 2715 token.warn('confusing_a'); 2716 this.first = expression(150); 2717 this.arity = 'prefix'; 2718 return this; 2719 }); 2720 infix('---', 130, function (left) { 2721 token.warn('confusing_a'); 2722 this.first = left; 2723 this.second = expression(130); 2724 return this; 2725 }); 2726 infix('*', 140, function (left, that) { 2727 if ((left.id === '(number)' && (left.number === 0 || left.number === 1)) || left.id === '(string)') { 2728 left.warn('unexpected_a'); 2729 } 2730 var right = expression(140); 2731 if ((right.id === '(number)' && (right.number === 0 || right.number === 1)) || right.id === '(string)') { 2732 right.warn('unexpected_a'); 2733 } 2734 if (left.id === right.id && left.id === '(number)') { 2735 left.number *= right.number; 2736 left.thru = right.thru; 2737 return left; 2738 } 2739 that.first = left; 2740 that.second = right; 2741 return that; 2742 }); 2743 infix('/', 140, function (left, that) { 2744 if ((left.id === '(number)' && left.number === 0) || left.id === '(string)') { 2745 left.warn('unexpected_a'); 2746 } 2747 var right = expression(140); 2748 if ((right.id === '(number)' && (right.number === 0 || right.number === 1)) || right.id === '(string)') { 2749 right.warn('unexpected_a'); 2750 } 2751 if (left.id === right.id && left.id === '(number)') { 2752 left.number /= right.number; 2753 left.thru = right.thru; 2754 return left; 2755 } 2756 that.first = left; 2757 that.second = right; 2758 return that; 2759 }); 2760 infix('%', 140, function (left, that) { 2761 if ((left.id === '(number)' && (left.number === 0 || left.number === 1)) || left.id === '(string)') { 2762 left.warn('unexpected_a'); 2763 } 2764 var right = expression(140); 2765 if ((right.id === '(number)' && right.number === 0) || right.id === '(string)') { 2766 right.warn('unexpected_a'); 2767 } 2768 if (left.id === right.id && left.id === '(number)') { 2769 left.number %= right.number; 2770 left.thru = right.thru; 2771 return left; 2772 } 2773 that.first = left; 2774 that.second = right; 2775 return that; 2776 }); 2777 2778 suffix('++'); 2779 prefix('++'); 2780 2781 suffix('--'); 2782 prefix('--'); 2783 prefix('delete', function (that) { 2784 one_space(); 2785 var p = expression(0); 2786 if (!p || (p.id !== '.' && p.id !== '[')) { 2787 next_token.warn('deleted'); 2788 } 2789 that.first = p; 2790 return that; 2791 }); 2792 2793 2794 prefix('~', function (that) { 2795 no_space_only(); 2796 if (!option.bitwise) { 2797 that.warn('unexpected_a'); 2798 } 2799 that.first = expression(150); 2800 return that; 2801 }); 2802 function banger(that) { 2803 no_space_only(); 2804 that.first = expected_condition(expression(150)); 2805 if (bang[that.first.id] === that || that.first.assign) { 2806 that.warn('confusing_a'); 2807 } 2808 return that; 2809 } 2810 prefix('!', banger); 2811 prefix('!!', banger); 2812 prefix('typeof'); 2813 prefix('new', function (that) { 2814 one_space(); 2815 var c = expression(160), n, p, v; 2816 that.first = c; 2817 if (c.id !== 'function') { 2818 if (c.identifier) { 2819 switch (c.string) { 2820 case 'Object': 2821 token.warn('use_object'); 2822 break; 2823 case 'Array': 2824 if (next_token.id === '(') { 2825 p = next_token; 2826 p.first = this; 2827 advance('('); 2828 if (next_token.id !== ')') { 2829 n = expression(0); 2830 p.second = [n]; 2831 if (n.id === '(string)' || next_token.id === ',') { 2832 p.warn('use_array'); 2833 } 2834 while (next_token.id === ',') { 2835 advance(','); 2836 p.second.push(expression(0)); 2837 } 2838 } else { 2839 token.warn('use_array'); 2840 } 2841 advance(')', p); 2842 return p; 2843 } 2844 token.warn('use_array'); 2845 break; 2846 case 'Number': 2847 case 'String': 2848 case 'Boolean': 2849 case 'Math': 2850 case 'JSON': 2851 c.warn('not_a_constructor'); 2852 break; 2853 case 'Function': 2854 if (!option.evil) { 2855 next_token.warn('function_eval'); 2856 } 2857 break; 2858 case 'Date': 2859 case 'RegExp': 2860 case 'this': 2861 break; 2862 default: 2863 if (c.id !== 'function') { 2864 v = c.string.charAt(0); 2865 if (!option.newcap && (v < 'A' || v > 'Z')) { 2866 token.warn('constructor_name_a'); 2867 } 2868 } 2869 } 2870 } else { 2871 if (c.id !== '.' && c.id !== '[' && c.id !== '(') { 2872 token.warn('bad_constructor'); 2873 } 2874 } 2875 } else { 2876 that.warn('weird_new'); 2877 } 2878 if (next_token.id !== '(') { 2879 next_token.warn('missing_a', '()'); 2880 } 2881 return that; 2882 }); 2883 2884 infix('(', 160, function (left, that) { 2885 var e, p; 2886 if (indent && indent.mode === 'expression') { 2887 no_space(prev_token, token); 2888 } else { 2889 no_space_only(prev_token, token); 2890 } 2891 if (!left.immed && left.id === 'function') { 2892 next_token.warn('wrap_immediate'); 2893 } 2894 p = []; 2895 if (left.identifier) { 2896 if (left.string.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)) { 2897 if (left.string !== 'Number' && left.string !== 'String' && 2898 left.string !== 'Boolean' && left.string !== 'Date') { 2899 if (left.string === 'Math') { 2900 left.warn('not_a_function'); 2901 } else if (left.string === 'Object') { 2902 token.warn('use_object'); 2903 } else if (left.string === 'Array' || !option.newcap) { 2904 left.warn('missing_a', 'new'); 2905 } 2906 } 2907 } else if (left.string === 'JSON') { 2908 left.warn('not_a_function'); 2909 } 2910 } else if (left.id === '.') { 2911 if (left.second.string === 'split' && 2912 left.first.id === '(string)') { 2913 left.second.warn('use_array'); 2914 } 2915 } 2916 step_in(); 2917 if (next_token.id !== ')') { 2918 no_space(); 2919 for (;;) { 2920 edge(); 2921 e = expression(10); 2922 if (left.string === 'Boolean' && (e.id === '!' || e.id === '~')) { 2923 e.warn('weird_condition'); 2924 } 2925 p.push(e); 2926 if (next_token.id !== ',') { 2927 break; 2928 } 2929 comma(); 2930 } 2931 } 2932 no_space(); 2933 step_out(')', that); 2934 if (typeof left === 'object') { 2935 if (left.string === 'parseInt' && p.length === 1) { 2936 left.warn('radix'); 2937 } else if (left.string === 'String' && p.length >= 1 && p[0].id === '(string)') { 2938 left.warn('unexpected_a'); 2939 } 2940 if (!option.evil) { 2941 if (left.string === 'eval' || left.string === 'Function' || 2942 left.string === 'execScript') { 2943 left.warn('evil'); 2944 } else if (p[0] && p[0].id === '(string)' && 2945 (left.string === 'setTimeout' || 2946 left.string === 'setInterval')) { 2947 left.warn('implied_evil'); 2948 } 2949 } 2950 if (!left.identifier && left.id !== '.' && left.id !== '[' && 2951 left.id !== '(' && left.id !== '&&' && left.id !== '||' && 2952 left.id !== '?') { 2953 left.warn('bad_invocation'); 2954 } 2955 if (left.id === '.') { 2956 if (p.length > 0 && 2957 left.first && left.first.first && 2958 are_similar(p[0], left.first.first)) { 2959 if (left.second.string === 'call' || 2960 (left.second.string === 'apply' && (p.length === 1 || 2961 (p[1].arity === 'prefix' && p[1].id === '[')))) { 2962 left.second.warn('unexpected_a'); 2963 } 2964 } 2965 if (left.second.string === 'toString') { 2966 if (left.first.id === '(string)' || left.first.id === '(number)') { 2967 left.second.warn('unexpected_a'); 2968 } 2969 } 2970 } 2971 } 2972 that.first = left; 2973 that.second = p; 2974 return that; 2975 }, true); 2976 2977 prefix('(', function (that) { 2978 step_in('expression'); 2979 no_space(); 2980 edge(); 2981 if (next_token.id === 'function') { 2982 next_token.immed = true; 2983 } 2984 var value = expression(0); 2985 value.paren = true; 2986 no_space(); 2987 step_out(')', that); 2988 if (value.id === 'function') { 2989 switch (next_token.id) { 2990 case '(': 2991 next_token.warn('move_invocation'); 2992 break; 2993 case '.': 2994 case '[': 2995 next_token.warn('unexpected_a'); 2996 break; 2997 default: 2998 that.warn('bad_wrap'); 2999 } 3000 } else if (!value.arity) { 3001 if (!option.closure || !that.comments) { 3002 that.warn('unexpected_a'); 3003 } 3004 } 3005 return value; 3006 }); 3007 3008 infix('.', 170, function (left, that) { 3009 no_space(prev_token, token); 3010 no_space(); 3011 var name = identifier(); 3012 if (typeof name === 'string') { 3013 tally_property(name); 3014 } 3015 that.first = left; 3016 that.second = token; 3017 if (left && left.string === 'arguments' && 3018 (name === 'callee' || name === 'caller')) { 3019 left.warn('avoid_a', 'arguments.' + name); 3020 } else if (!option.evil && left && left.string === 'document' && 3021 (name === 'write' || name === 'writeln')) { 3022 left.warn('write_is_wrong'); 3023 } else if (!option.stupid && syx.test(name)) { 3024 token.warn('sync_a'); 3025 } else if (left && left.id === '{') { 3026 that.warn('unexpected_a'); 3027 } 3028 if (!option.evil && (name === 'eval' || name === 'execScript')) { 3029 next_token.warn('evil'); 3030 } 3031 return that; 3032 }, true); 3033 3034 infix('[', 170, function (left, that) { 3035 var e, s; 3036 no_space_only(prev_token, token); 3037 no_space(); 3038 step_in(); 3039 edge(); 3040 e = expression(0); 3041 switch (e.id) { 3042 case '(number)': 3043 if (e.id === '(number)' && left.id === 'arguments') { 3044 left.warn('use_param'); 3045 } 3046 break; 3047 case '(string)': 3048 if (!option.evil && 3049 (e.string === 'eval' || e.string === 'execScript')) { 3050 e.warn('evil'); 3051 } else if (!option.sub && ix.test(e.string)) { 3052 s = syntax[e.string]; 3053 if (!s || !s.reserved) { 3054 e.warn('subscript'); 3055 } 3056 } 3057 tally_property(e.string); 3058 break; 3059 } 3060 if (left && (left.id === '{' || (left.id === '[' && left.arity === 'prefix'))) { 3061 that.warn('unexpected_a'); 3062 } 3063 step_out(']', that); 3064 no_space(prev_token, token); 3065 that.first = left; 3066 that.second = e; 3067 return that; 3068 }, true); 3069 3070 prefix('[', function (that) { 3071 that.first = []; 3072 step_in('array'); 3073 while (next_token.id !== '(end)') { 3074 while (next_token.id === ',') { 3075 next_token.warn('unexpected_a'); 3076 advance(','); 3077 } 3078 if (next_token.id === ']') { 3079 break; 3080 } 3081 indent.wrap = false; 3082 edge(); 3083 that.first.push(expression(10)); 3084 if (next_token.id === ',') { 3085 comma(); 3086 if (next_token.id === ']') { 3087 token.warn('unexpected_a'); 3088 break; 3089 } 3090 } else { 3091 break; 3092 } 3093 } 3094 step_out(']', that); 3095 return that; 3096 }, 170); 3097 3098 3099 function property_name() { 3100 var id = optional_identifier(); 3101 if (!id) { 3102 if (next_token.id === '(string)') { 3103 id = next_token.string; 3104 advance(); 3105 } else if (next_token.id === '(number)') { 3106 id = next_token.number.toString(); 3107 advance(); 3108 } 3109 } 3110 return id; 3111 } 3112 3113 3114 3115 assignop('='); 3116 assignop('+=', '+'); 3117 assignop('-=', '-'); 3118 assignop('*=', '*'); 3119 assignop('/=', '/').nud = function () { 3120 next_token.stop('slash_equal'); 3121 }; 3122 assignop('%=', '%'); 3123 assignop('&=', '&'); 3124 assignop('|=', '|'); 3125 assignop('^=', '^'); 3126 assignop('<<=', '<<'); 3127 assignop('>>=', '>>'); 3128 assignop('>>>=', '>>>'); 3129 3130 function function_parameters() { 3131 var id, parameters = [], paren = next_token; 3132 advance('('); 3133 token.function = funct; 3134 step_in(); 3135 no_space(); 3136 if (next_token.id !== ')') { 3137 for (;;) { 3138 edge(); 3139 id = identifier(); 3140 if (token.reserved) { 3141 token.warn('expected_identifier_a_reserved'); 3142 } 3143 define('parameter', token); 3144 parameters.push(id); 3145 token.init = true; 3146 token.writeable = true; 3147 if (next_token.id !== ',') { 3148 break; 3149 } 3150 comma(); 3151 } 3152 } 3153 no_space(); 3154 step_out(')', paren); 3155 return parameters; 3156 } 3157 3158 function do_function(func, name) { 3159 var old_funct = funct, 3160 old_option = option, 3161 old_scope = scope; 3162 scope = Object.create(old_scope); 3163 funct = { 3164 closure: [], 3165 global: [], 3166 level: old_funct.level + 1, 3167 line: next_token.line, 3168 loopage: 0, 3169 name: name || '\'' + (anonname || '').replace(nx, sanitize) + '\'', 3170 outer: [], 3171 scope: scope 3172 }; 3173 funct.parameter = function_parameters(); 3174 func.function = funct; 3175 option = Object.create(old_option); 3176 functions.push(funct); 3177 if (name) { 3178 func.name = name; 3179 func.string = name; 3180 define('function', func); 3181 func.init = true; 3182 func.used += 1; 3183 } 3184 func.writeable = false; 3185 one_space(); 3186 func.block = block('function'); 3187 Object.keys(scope).forEach(function (name) { 3188 var master = scope[name]; 3189 if (!master.used && master.kind !== 'exception' && 3190 ((master.kind === 'var' && !option.unvar) 3191 || (master.kind === 'parameter' && !option.unparam))) { 3192 master.warn('unused_a'); 3193 } else if (!master.init && (master.kind !== 'var' || !option.unvar)) { 3194 master.warn('uninitialized_a'); 3195 } 3196 }); 3197 funct = old_funct; 3198 option = old_option; 3199 scope = old_scope; 3200 } 3201 3202 prefix('{', function (that) { 3203 var get, i, j, name, set, seen = Object.create(null); 3204 that.first = []; 3205 step_in(); 3206 while (next_token.id !== '}') { 3207 indent.wrap = false; 3208 3209 // JSLint recognizes the ES5 extension for get/set in object literals, 3210 // but requires that they be used in pairs. 3211 3212 edge(); 3213 if (next_token.string === 'get' && peek().id !== ':') { 3214 get = next_token; 3215 advance('get'); 3216 one_space_only(); 3217 name = next_token; 3218 i = property_name(); 3219 if (!i) { 3220 next_token.stop('missing_property'); 3221 } 3222 get.string = ''; 3223 do_function(get); 3224 if (funct.loopage) { 3225 get.warn('function_loop'); 3226 } 3227 if (get.function.parameter.length) { 3228 get.warn('parameter_a_get_b', get.function.parameter[0], i); 3229 } 3230 comma(); 3231 set = next_token; 3232 spaces(); 3233 edge(); 3234 advance('set'); 3235 set.string = ''; 3236 one_space_only(); 3237 j = property_name(); 3238 if (i !== j) { 3239 token.stop('expected_a_b', i, j || next_token.string); 3240 } 3241 do_function(set); 3242 if (set.block.length === 0) { 3243 token.warn('missing_a', 'throw'); 3244 } 3245 if (set.function.parameter.length === 0) { 3246 set.stop('parameter_set_a', 'value'); 3247 } else if (set.function.parameter[0] !== 'value') { 3248 set.stop('expected_a_b', 'value', 3249 set.function.parameter[0]); 3250 } 3251 name.first = [get, set]; 3252 } else { 3253 name = next_token; 3254 i = property_name(); 3255 if (typeof i !== 'string') { 3256 next_token.stop('missing_property'); 3257 } 3258 advance(':'); 3259 spaces(); 3260 name.first = expression(10); 3261 } 3262 that.first.push(name); 3263 if (seen[i] === true) { 3264 next_token.warn('duplicate_a', i); 3265 } 3266 seen[i] = true; 3267 tally_property(i); 3268 if (next_token.id !== ',') { 3269 break; 3270 } 3271 for (;;) { 3272 comma(); 3273 if (next_token.id !== ',') { 3274 break; 3275 } 3276 next_token.warn('unexpected_a'); 3277 } 3278 if (next_token.id === '}') { 3279 token.warn('unexpected_a'); 3280 } 3281 } 3282 step_out('}', that); 3283 return that; 3284 }); 3285 3286 stmt('{', function () { 3287 next_token.warn('statement_block'); 3288 this.arity = 'statement'; 3289 this.block = statements(); 3290 this.disrupt = this.block.disrupt; 3291 advance('}', this); 3292 return this; 3293 }); 3294 3295 stmt('/*global', directive); 3296 stmt('/*globals', directive); 3297 stmt('/*jslint', directive); 3298 stmt('/*member', directive); 3299 stmt('/*members', directive); 3300 stmt('/*property', directive); 3301 stmt('/*properties', directive); 3302 3303 stmt('var', function () { 3304 3305 // JavaScript does not have block scope. It only has function scope. So, 3306 // declaring a variable in a block can have unexpected consequences. 3307 3308 // var.first will contain an array, the array containing name tokens 3309 // and assignment tokens. 3310 3311 var assign, id, name; 3312 3313 if (funct.loopage) { 3314 next_token.warn('var_loop'); 3315 } else if (funct.varstatement && !option.vars) { 3316 next_token.warn('combine_var'); 3317 } 3318 if (funct !== global_funct) { 3319 funct.varstatement = true; 3320 } 3321 this.arity = 'statement'; 3322 this.first = []; 3323 step_in('var'); 3324 for (;;) { 3325 name = next_token; 3326 id = identifier(true); 3327 define('var', name); 3328 name.dead = funct; 3329 if (next_token.id === '=') { 3330 if (funct === global_funct && !name.writeable) { 3331 name.warn('read_only'); 3332 } 3333 assign = next_token; 3334 assign.first = name; 3335 spaces(); 3336 advance('='); 3337 spaces(); 3338 if (next_token.id === 'undefined') { 3339 token.warn('unnecessary_initialize', id); 3340 } 3341 if (peek(0).id === '=' && next_token.identifier) { 3342 next_token.stop('var_a_not'); 3343 } 3344 assign.second = expression(0); 3345 assign.arity = 'infix'; 3346 name.init = true; 3347 this.first.push(assign); 3348 } else { 3349 this.first.push(name); 3350 } 3351 name.dead = false; 3352 name.writeable = true; 3353 if (next_token.id !== ',') { 3354 break; 3355 } 3356 comma(); 3357 indent.wrap = false; 3358 if (var_mode && next_token.line === token.line && 3359 this.first.length === 1) { 3360 var_mode = null; 3361 indent.open = false; 3362 indent.at -= option.indent; 3363 } 3364 spaces(); 3365 edge(); 3366 } 3367 var_mode = null; 3368 step_out(); 3369 return this; 3370 }); 3371 3372 stmt('function', function () { 3373 one_space(); 3374 if (in_block) { 3375 token.warn('function_block'); 3376 } 3377 var name = next_token, 3378 id = identifier(true); 3379 define('var', name); 3380 if (!name.writeable) { 3381 name.warn('read_only'); 3382 } 3383 name.init = true; 3384 name.statement = true; 3385 no_space(); 3386 this.arity = 'statement'; 3387 do_function(this, id); 3388 if (next_token.id === '(' && next_token.line === token.line) { 3389 next_token.stop('function_statement'); 3390 } 3391 return this; 3392 }); 3393 3394 prefix('function', function (that) { 3395 var id = optional_identifier(true), name; 3396 if (id) { 3397 name = token; 3398 no_space(); 3399 } else { 3400 id = ''; 3401 one_space(); 3402 } 3403 do_function(that, id); 3404 if (name) { 3405 name.function = that.function; 3406 } 3407 if (funct.loopage) { 3408 that.warn('function_loop'); 3409 } 3410 switch (next_token.id) { 3411 case ';': 3412 case '(': 3413 case ')': 3414 case ',': 3415 case ']': 3416 case '}': 3417 case ':': 3418 case '(end)': 3419 break; 3420 case '.': 3421 if (peek().string !== 'bind' || peek(1).id !== '(') { 3422 next_token.warn('unexpected_a'); 3423 } 3424 break; 3425 default: 3426 next_token.stop('unexpected_a'); 3427 } 3428 that.arity = 'function'; 3429 return that; 3430 }); 3431 3432 stmt('if', function () { 3433 var paren = next_token; 3434 one_space(); 3435 advance('('); 3436 step_in('control'); 3437 no_space(); 3438 edge(); 3439 this.arity = 'statement'; 3440 this.first = expected_condition(expected_relation(expression(0))); 3441 no_space(); 3442 step_out(')', paren); 3443 one_space(); 3444 this.block = block('if'); 3445 if (next_token.id === 'else') { 3446 if (this.block.disrupt) { 3447 next_token.warn(this.elif ? 'use_nested_if' : 'unnecessary_else'); 3448 } 3449 one_space(); 3450 advance('else'); 3451 one_space(); 3452 if (next_token.id === 'if') { 3453 next_token.elif = true; 3454 this.else = statement(true); 3455 } else { 3456 this.else = block('else'); 3457 } 3458 if (this.else.disrupt && this.block.disrupt) { 3459 this.disrupt = true; 3460 } 3461 } 3462 return this; 3463 }); 3464 3465 stmt('try', function () { 3466 3467 // try.first The catch variable 3468 // try.second The catch clause 3469 // try.third The finally clause 3470 // try.block The try block 3471 3472 var exception_variable, paren; 3473 one_space(); 3474 this.arity = 'statement'; 3475 this.block = block('try'); 3476 if (next_token.id === 'catch') { 3477 one_space(); 3478 advance('catch'); 3479 one_space(); 3480 paren = next_token; 3481 advance('('); 3482 step_in('control'); 3483 no_space(); 3484 edge(); 3485 exception_variable = next_token; 3486 this.first = identifier(); 3487 define('exception', exception_variable); 3488 exception_variable.init = true; 3489 no_space(); 3490 step_out(')', paren); 3491 one_space(); 3492 this.second = block('catch'); 3493 if (this.second.length) { 3494 if (this.first === 'ignore') { 3495 exception_variable.warn('unexpected_a'); 3496 } 3497 } else { 3498 if (this.first !== 'ignore') { 3499 exception_variable.warn('expected_a_b', 'ignore', 3500 exception_variable.string); 3501 } 3502 } 3503 exception_variable.dead = true; 3504 } 3505 if (next_token.id === 'finally') { 3506 one_space(); 3507 advance('finally'); 3508 one_space(); 3509 this.third = block('finally'); 3510 } else if (!this.second) { 3511 next_token.stop('expected_a_b', 'catch', artifact()); 3512 } 3513 return this; 3514 }); 3515 3516 labeled_stmt('while', function () { 3517 one_space(); 3518 var paren = next_token; 3519 funct.loopage += 1; 3520 advance('('); 3521 step_in('control'); 3522 no_space(); 3523 edge(); 3524 this.arity = 'statement'; 3525 this.first = expected_relation(expression(0)); 3526 if (this.first.id !== 'true') { 3527 expected_condition(this.first, 'unexpected_a'); 3528 } 3529 no_space(); 3530 step_out(')', paren); 3531 one_space(); 3532 this.block = block('while'); 3533 if (this.block.disrupt) { 3534 prev_token.warn('strange_loop'); 3535 } 3536 funct.loopage -= 1; 3537 return this; 3538 }); 3539 3540 reserve('with'); 3541 3542 labeled_stmt('switch', function () { 3543 3544 // switch.first the switch expression 3545 // switch.second the array of cases. A case is 'case' or 'default' token: 3546 // case.first the array of case expressions 3547 // case.second the array of statements 3548 // If all of the arrays of statements are disrupt, then the switch is disrupt. 3549 3550 var cases = [], 3551 old_in_block = in_block, 3552 particular, 3553 that = token, 3554 the_case = next_token; 3555 3556 function find_duplicate_case(value) { 3557 if (are_similar(particular, value)) { 3558 value.warn('duplicate_a'); 3559 } 3560 } 3561 3562 one_space(); 3563 advance('('); 3564 no_space(); 3565 step_in(); 3566 this.arity = 'statement'; 3567 this.first = expected_condition(expected_relation(expression(0))); 3568 no_space(); 3569 step_out(')', the_case); 3570 one_space(); 3571 advance('{'); 3572 step_in(); 3573 in_block = true; 3574 this.second = []; 3575 if (that.from !== next_token.from && !option.white) { 3576 next_token.warn('expected_a_at_b_c', next_token.string, that.from, next_token.from); 3577 } 3578 while (next_token.id === 'case') { 3579 the_case = next_token; 3580 the_case.first = []; 3581 the_case.arity = 'case'; 3582 for (;;) { 3583 spaces(); 3584 edge('case'); 3585 advance('case'); 3586 one_space(); 3587 particular = expression(0); 3588 cases.forEach(find_duplicate_case); 3589 cases.push(particular); 3590 the_case.first.push(particular); 3591 if (particular.id === 'NaN') { 3592 particular.warn('unexpected_a'); 3593 } 3594 no_space_only(); 3595 advance(':'); 3596 if (next_token.id !== 'case') { 3597 break; 3598 } 3599 } 3600 spaces(); 3601 the_case.second = statements(); 3602 if (the_case.second && the_case.second.length > 0) { 3603 if (!the_case.second[the_case.second.length - 1].disrupt) { 3604 next_token.warn('missing_a_after_b', 'break', 'case'); 3605 } 3606 } else { 3607 next_token.warn('empty_case'); 3608 } 3609 this.second.push(the_case); 3610 } 3611 if (this.second.length === 0) { 3612 next_token.warn('missing_a', 'case'); 3613 } 3614 if (next_token.id === 'default') { 3615 spaces(); 3616 the_case = next_token; 3617 the_case.arity = 'case'; 3618 edge('case'); 3619 advance('default'); 3620 no_space_only(); 3621 advance(':'); 3622 spaces(); 3623 the_case.second = statements(); 3624 if (the_case.second && the_case.second.length > 0) { 3625 this.disrupt = the_case.second[the_case.second.length - 1].disrupt; 3626 } else { 3627 the_case.warn('empty_case'); 3628 } 3629 this.second.push(the_case); 3630 } 3631 if (this.break) { 3632 this.disrupt = false; 3633 } 3634 spaces(); 3635 step_out('}', this); 3636 in_block = old_in_block; 3637 return this; 3638 }); 3639 3640 stmt('debugger', function () { 3641 if (!option.debug) { 3642 this.warn('unexpected_a'); 3643 } 3644 this.arity = 'statement'; 3645 return this; 3646 }); 3647 3648 labeled_stmt('do', function () { 3649 funct.loopage += 1; 3650 one_space(); 3651 this.arity = 'statement'; 3652 this.block = block('do'); 3653 if (this.block.disrupt) { 3654 prev_token.warn('strange_loop'); 3655 } 3656 one_space(); 3657 advance('while'); 3658 var paren = next_token; 3659 one_space(); 3660 advance('('); 3661 step_in(); 3662 no_space(); 3663 edge(); 3664 this.first = expected_condition(expected_relation(expression(0)), 'unexpected_a'); 3665 no_space(); 3666 step_out(')', paren); 3667 funct.loopage -= 1; 3668 return this; 3669 }); 3670 3671 labeled_stmt('for', function () { 3672 3673 var blok, filter, master, ok = false, paren = next_token, value; 3674 this.arity = 'statement'; 3675 funct.loopage += 1; 3676 advance('('); 3677 if (next_token.id === ';') { 3678 no_space(); 3679 advance(';'); 3680 no_space(); 3681 advance(';'); 3682 no_space(); 3683 advance(')'); 3684 blok = block('for'); 3685 } else { 3686 step_in('control'); 3687 spaces(this, paren); 3688 no_space(); 3689 if (next_token.id === 'var') { 3690 next_token.stop('move_var'); 3691 } 3692 edge(); 3693 if (peek(0).id === 'in') { 3694 this.forin = true; 3695 value = expression(1000); 3696 master = value.master; 3697 if (!master) { 3698 value.stop('bad_in_a'); 3699 } 3700 if (master.kind !== 'var' || master.function !== funct || 3701 !master.writeable || master.dead) { 3702 value.warn('bad_in_a'); 3703 } 3704 master.init = true; 3705 master.used -= 1; 3706 this.first = value; 3707 advance('in'); 3708 this.second = expression(20); 3709 step_out(')', paren); 3710 blok = block('for'); 3711 if (!option.forin) { 3712 if (blok.length === 1 && typeof blok[0] === 'object') { 3713 if (blok[0].id === 'if' && !blok[0].else) { 3714 filter = blok[0].first; 3715 while (filter.id === '&&') { 3716 filter = filter.first; 3717 } 3718 switch (filter.id) { 3719 case '===': 3720 case '!==': 3721 ok = filter.first.id === '[' 3722 ? are_similar(filter.first.first, this.second) && 3723 are_similar(filter.first.second, this.first) 3724 : filter.first.id === 'typeof' && 3725 filter.first.first.id === '[' && 3726 are_similar(filter.first.first.first, this.second) && 3727 are_similar(filter.first.first.second, this.first); 3728 break; 3729 case '(': 3730 ok = filter.first.id === '.' && (( 3731 are_similar(filter.first.first, this.second) && 3732 filter.first.second.string === 'hasOwnProperty' && 3733 are_similar(filter.second[0], this.first) 3734 ) || ( 3735 filter.first.first.id === '.' && 3736 filter.first.first.first.first && 3737 filter.first.first.first.first.string === 'Object' && 3738 filter.first.first.first.id === '.' && 3739 filter.first.first.first.second.string === 'prototype' && 3740 filter.first.first.second.string === 'hasOwnProperty' && 3741 filter.first.second.string === 'call' && 3742 are_similar(filter.second[0], this.second) && 3743 are_similar(filter.second[1], this.first) 3744 )); 3745 break; 3746 } 3747 } else if (blok[0].id === 'switch') { 3748 ok = blok[0].id === 'switch' && 3749 blok[0].first.id === 'typeof' && 3750 blok[0].first.first.id === '[' && 3751 are_similar(blok[0].first.first.first, this.second) && 3752 are_similar(blok[0].first.first.second, this.first); 3753 } 3754 } 3755 if (!ok) { 3756 this.warn('for_if'); 3757 } 3758 } 3759 } else { 3760 edge(); 3761 this.first = []; 3762 for (;;) { 3763 this.first.push(expression(0, 'for')); 3764 if (next_token.id !== ',') { 3765 break; 3766 } 3767 comma(); 3768 } 3769 semicolon(); 3770 edge(); 3771 this.second = expected_relation(expression(0)); 3772 if (this.second.id !== 'true') { 3773 expected_condition(this.second, 'unexpected_a'); 3774 } 3775 semicolon(token); 3776 if (next_token.id === ';') { 3777 next_token.stop('expected_a_b', ')', ';'); 3778 } 3779 this.third = []; 3780 edge(); 3781 for (;;) { 3782 this.third.push(expression(0, 'for')); 3783 if (next_token.id !== ',') { 3784 break; 3785 } 3786 comma(); 3787 } 3788 no_space(); 3789 step_out(')', paren); 3790 one_space(); 3791 blok = block('for'); 3792 } 3793 } 3794 if (blok.disrupt) { 3795 prev_token.warn('strange_loop'); 3796 } 3797 this.block = blok; 3798 funct.loopage -= 1; 3799 return this; 3800 }); 3801 3802 function optional_label(that) { 3803 var label = next_token.string, 3804 master; 3805 that.arity = 'statement'; 3806 if (!funct.breakage || (!option.continue && that.id === 'continue')) { 3807 that.warn('unexpected_a'); 3808 } else if (next_token.identifier && token.line === next_token.line) { 3809 one_space_only(); 3810 master = scope[label]; 3811 if (!master || master.kind !== 'label') { 3812 next_token.warn('not_a_label'); 3813 } else if (master.dead || master.function !== funct) { 3814 next_token.warn('not_a_scope'); 3815 } else { 3816 master.used += 1; 3817 if (that.id === 'break') { 3818 master.statement.break = true; 3819 } 3820 if (funct.breakage[funct.breakage.length - 1] === master.statement) { 3821 next_token.warn('unexpected_a'); 3822 } 3823 } 3824 that.first = next_token; 3825 advance(); 3826 } else { 3827 if (that.id === 'break') { 3828 funct.breakage[funct.breakage.length - 1].break = true; 3829 } 3830 } 3831 return that; 3832 3833 } 3834 3835 disrupt_stmt('break', function () { 3836 return optional_label(this); 3837 }); 3838 3839 disrupt_stmt('continue', function () { 3840 return optional_label(this); 3841 }); 3842 3843 disrupt_stmt('return', function () { 3844 if (funct === global_funct) { 3845 this.warn('unexpected_a'); 3846 } 3847 this.arity = 'statement'; 3848 if (next_token.id !== ';' && next_token.line === token.line) { 3849 if (option.closure) { 3850 spaces(); 3851 } else { 3852 one_space_only(); 3853 } 3854 if (next_token.id === '/' || next_token.id === '(regexp)') { 3855 next_token.warn('wrap_regexp'); 3856 } 3857 this.first = expression(0); 3858 if (this.first.assign) { 3859 this.first.warn('unexpected_a'); 3860 } 3861 } 3862 return this; 3863 }); 3864 3865 disrupt_stmt('throw', function () { 3866 this.arity = 'statement'; 3867 one_space_only(); 3868 this.first = expression(20); 3869 return this; 3870 }); 3871 3872 3873 // Superfluous reserved words 3874 3875 reserve('class'); 3876 reserve('const'); 3877 reserve('enum'); 3878 reserve('export'); 3879 reserve('extends'); 3880 reserve('import'); 3881 reserve('super'); 3882 3883 // Harmony reserved words 3884 3885 reserve('implements'); 3886 reserve('interface'); 3887 reserve('let'); 3888 reserve('package'); 3889 reserve('private'); 3890 reserve('protected'); 3891 reserve('public'); 3892 reserve('static'); 3893 reserve('yield'); 3894 3895 3896 // Parse JSON 3897 3898 function json_value() { 3899 3900 function json_object() { 3901 var brace = next_token, object = Object.create(null); 3902 advance('{'); 3903 if (next_token.id !== '}') { 3904 while (next_token.id !== '(end)') { 3905 while (next_token.id === ',') { 3906 next_token.warn('unexpected_a'); 3907 advance(','); 3908 } 3909 if (next_token.id !== '(string)') { 3910 next_token.warn('expected_string_a'); 3911 } 3912 if (object[next_token.string] === true) { 3913 next_token.warn('duplicate_a'); 3914 } else if (next_token.string === '__proto__') { 3915 next_token.warn('dangling_a'); 3916 } else { 3917 object[next_token.string] = true; 3918 } 3919 advance(); 3920 advance(':'); 3921 json_value(); 3922 if (next_token.id !== ',') { 3923 break; 3924 } 3925 advance(','); 3926 if (next_token.id === '}') { 3927 token.warn('unexpected_a'); 3928 break; 3929 } 3930 } 3931 } 3932 advance('}', brace); 3933 } 3934 3935 function json_array() { 3936 var bracket = next_token; 3937 advance('['); 3938 if (next_token.id !== ']') { 3939 while (next_token.id !== '(end)') { 3940 while (next_token.id === ',') { 3941 next_token.warn('unexpected_a'); 3942 advance(','); 3943 } 3944 json_value(); 3945 if (next_token.id !== ',') { 3946 break; 3947 } 3948 advance(','); 3949 if (next_token.id === ']') { 3950 token.warn('unexpected_a'); 3951 break; 3952 } 3953 } 3954 } 3955 advance(']', bracket); 3956 } 3957 3958 switch (next_token.id) { 3959 case '{': 3960 json_object(); 3961 break; 3962 case '[': 3963 json_array(); 3964 break; 3965 case 'true': 3966 case 'false': 3967 case 'null': 3968 case '(number)': 3969 case '(string)': 3970 advance(); 3971 break; 3972 case '-': 3973 advance('-'); 3974 no_space_only(); 3975 advance('(number)'); 3976 break; 3977 default: 3978 next_token.stop('unexpected_a'); 3979 } 3980 } 3981 3982 3983 // The actual JSLINT function itself. 3984 3985 itself = function JSLint(the_source, the_option) { 3986 3987 var i, predef, tree; 3988 itself.errors = []; 3989 itself.tree = ''; 3990 itself.properties = ''; 3991 begin = prev_token = token = next_token = 3992 Object.create(syntax['(begin)']); 3993 tokens = []; 3994 predefined = Object.create(null); 3995 add_to_predefined(standard); 3996 property = Object.create(null); 3997 if (the_option) { 3998 option = Object.create(the_option); 3999 predef = option.predef; 4000 if (predef) { 4001 if (Array.isArray(predef)) { 4002 for (i = 0; i < predef.length; i += 1) { 4003 predefined[predef[i]] = true; 4004 } 4005 } else if (typeof predef === 'object') { 4006 add_to_predefined(predef); 4007 } 4008 } 4009 } else { 4010 option = Object.create(null); 4011 } 4012 option.indent = +option.indent || 4; 4013 option.maxerr = +option.maxerr || 50; 4014 global_scope = scope = Object.create(null); 4015 global_funct = funct = { 4016 scope: scope, 4017 loopage: 0, 4018 level: 0 4019 }; 4020 functions = [funct]; 4021 block_var = []; 4022 4023 comments = []; 4024 comments_off = false; 4025 in_block = false; 4026 indent = null; 4027 json_mode = false; 4028 lookahead = []; 4029 node_js = false; 4030 prereg = true; 4031 strict_mode = false; 4032 var_mode = null; 4033 warnings = 0; 4034 lex.init(the_source); 4035 4036 assume(); 4037 4038 try { 4039 advance(); 4040 if (next_token.id === '(number)') { 4041 next_token.stop('unexpected_a'); 4042 } else { 4043 switch (next_token.id) { 4044 case '{': 4045 case '[': 4046 comments_off = true; 4047 json_mode = true; 4048 json_value(); 4049 break; 4050 default: 4051 4052 // If the first token is a semicolon, ignore it. This is sometimes used when 4053 // files are intended to be appended to files that may be sloppy. A sloppy 4054 // file may be depending on semicolon insertion on its last line. 4055 4056 step_in(1); 4057 if (next_token.id === ';' && !node_js) { 4058 next_token.edge = true; 4059 advance(';'); 4060 } 4061 tree = statements(); 4062 begin.first = tree; 4063 itself.tree = begin; 4064 if (tree.disrupt) { 4065 prev_token.warn('weird_program'); 4066 } 4067 } 4068 } 4069 indent = null; 4070 advance('(end)'); 4071 itself.property = property; 4072 } catch (e) { 4073 if (e) { // ~~ 4074 itself.errors.push({ 4075 reason : e.message, 4076 line : e.line || next_token.line, 4077 character : e.character || next_token.from 4078 }, null); 4079 } 4080 } 4081 return itself.errors.length === 0; 4082 }; 4083 4084 function unique(array) { 4085 array = array.sort(); 4086 var i, length = 0, previous, value; 4087 for (i = 0; i < array.length; i += 1) { 4088 value = array[i]; 4089 if (value !== previous) { 4090 array[length] = value; 4091 previous = value; 4092 length += 1; 4093 } 4094 } 4095 array.length = length; 4096 return array; 4097 } 4098 4099 // Data summary. 4100 4101 itself.data = function () { 4102 var data = {functions: []}, 4103 function_data, 4104 i, 4105 the_function, 4106 the_scope; 4107 data.errors = itself.errors; 4108 data.json = json_mode; 4109 data.global = unique(Object.keys(global_scope)); 4110 4111 function selects(name) { 4112 var kind = the_scope[name].kind; 4113 switch (kind) { 4114 case 'var': 4115 case 'exception': 4116 case 'label': 4117 function_data[kind].push(name); 4118 break; 4119 } 4120 } 4121 4122 for (i = 1; i < functions.length; i += 1) { 4123 the_function = functions[i]; 4124 function_data = { 4125 name: the_function.name, 4126 line: the_function.line, 4127 level: the_function.level, 4128 parameter: the_function.parameter, 4129 var: [], 4130 exception: [], 4131 closure: unique(the_function.closure), 4132 outer: unique(the_function.outer), 4133 global: unique(the_function.global), 4134 label: [] 4135 }; 4136 the_scope = the_function.scope; 4137 Object.keys(the_scope).forEach(selects); 4138 function_data.var.sort(); 4139 function_data.exception.sort(); 4140 function_data.label.sort(); 4141 data.functions.push(function_data); 4142 } 4143 data.tokens = tokens; 4144 return data; 4145 }; 4146 4147 itself.error_report = function (data) { 4148 var evidence, i, output = [], warning; 4149 if (data.errors.length) { 4150 if (data.json) { 4151 output.push('<cite>JSON: bad.</cite><br>'); 4152 } 4153 for (i = 0; i < data.errors.length; i += 1) { 4154 warning = data.errors[i]; 4155 if (warning) { 4156 evidence = warning.evidence || ''; 4157 output.push('<cite>'); 4158 if (isFinite(warning.line)) { 4159 output.push('<address>line ' + 4160 String(warning.line) + 4161 ' character ' + String(warning.character) + 4162 '</address>'); 4163 } 4164 output.push(warning.reason.entityify() + '</cite>'); 4165 if (evidence) { 4166 output.push('<pre>' + evidence.entityify() + '</pre>'); 4167 } 4168 } 4169 } 4170 } 4171 return output.join(''); 4172 }; 4173 4174 4175 itself.report = function (data) { 4176 var dl, i, j, names, output = [], the_function; 4177 4178 function detail(h, array) { 4179 var comma_needed = false; 4180 if (array.length) { 4181 output.push("<dt>" + h + "</dt><dd>"); 4182 array.forEach(function (item) { 4183 output.push((comma_needed ? ', ' : '') + item); 4184 comma_needed = true; 4185 }); 4186 output.push("</dd>"); 4187 } 4188 } 4189 4190 output.push('<dl class=level0>'); 4191 if (data.global.length) { 4192 detail('global', data.global); 4193 dl = true; 4194 } else if (data.json) { 4195 if (!data.errors.length) { 4196 output.push("<dt>JSON: good.</dt>"); 4197 } 4198 } else { 4199 output.push("<dt><i>No new global variables introduced.</i></dt>"); 4200 } 4201 if (dl) { 4202 output.push("</dl>"); 4203 } else { 4204 output[0] = ''; 4205 } 4206 4207 if (data.functions) { 4208 for (i = 0; i < data.functions.length; i += 1) { 4209 the_function = data.functions[i]; 4210 names = []; 4211 if (the_function.params) { 4212 for (j = 0; j < the_function.params.length; j += 1) { 4213 names[j] = the_function.params[j].string; 4214 } 4215 } 4216 output.push('<dl class=level' + the_function.level + 4217 '><address>line ' + String(the_function.line) + 4218 '</address>' + the_function.name.entityify()); 4219 detail('parameter', the_function.parameter); 4220 detail('variable', the_function.var); 4221 detail('exception', the_function.exception); 4222 detail('closure', the_function.closure); 4223 detail('outer', the_function.outer); 4224 detail('global', the_function.global); 4225 detail('label', the_function.label); 4226 output.push('</dl>'); 4227 } 4228 } 4229 return output.join(''); 4230 }; 4231 4232 itself.properties_report = function (property) { 4233 if (!property) { 4234 return ''; 4235 } 4236 var i, 4237 key, 4238 keys = Object.keys(property).sort(), 4239 mem = ' ', 4240 name, 4241 not_first = false, 4242 output = ['/*properties']; 4243 for (i = 0; i < keys.length; i += 1) { 4244 key = keys[i]; 4245 if (property[key] > 0) { 4246 if (not_first) { 4247 mem += ','; 4248 } 4249 name = ix.test(key) 4250 ? key 4251 : '\'' + key.replace(nx, sanitize) + '\''; 4252 if (mem.length + name.length >= 80) { 4253 output.push(mem); 4254 mem = ' '; 4255 } else { 4256 mem += ' '; 4257 } 4258 mem += name; 4259 not_first = true; 4260 } 4261 } 4262 output.push(mem, '*/\n'); 4263 return output.join('\n'); 4264 }; 4265 4266 itself.color = function (data) { 4267 var from, 4268 i = 1, 4269 level, 4270 line, 4271 result = [], 4272 thru, 4273 data_token = data.tokens[0]; 4274 while (data_token && data_token.id !== '(end)') { 4275 from = data_token.from; 4276 line = data_token.line; 4277 thru = data_token.thru; 4278 level = data_token.function.level; 4279 do { 4280 thru = data_token.thru; 4281 data_token = data.tokens[i]; 4282 i += 1; 4283 } while (data_token && data_token.line === line && 4284 data_token.from - thru < 5 && 4285 level === data_token.function.level); 4286 result.push({ 4287 line: line, 4288 level: level, 4289 from: from, 4290 thru: thru 4291 }); 4292 } 4293 return result; 4294 }; 4295 4296 itself.jslint = itself; 4297 4298 itself.edition = '2014-07-08'; 4299 4300 return itself; 4301 }()); 4302 core.JSLint = function JSLint() { 4303 "use strict"; 4304 this.JSLINT = JSLINT; 4305 }; 4306